/// <reference path="../ScriptReference/jquery-1.7.1-vsdoc.js"/>

if (!Mediasite) {
	var Mediasite = {};
}


// ImageButton
// +===========================================================================
// events: checkchanged
// ============================================================================
Mediasite.ImageButton = function (element, options) {
    var self = this;
    var _element = Mediasite.ImageButton.AdaptButtonElement(element);

    // Define some default settings and let options override them.
    var settings = $.extend({
        stateCalculator: new Mediasite.ImageButton.ButtonStateCalculator(),
        strategy: new Mediasite.ImageButton.SpriteStrategy(),
        normalPosition: _element.getBackgroundPosition(),
        normalImage: _element.getBackgroundImage(),
        normalTitle: _element.getTitle(),
        checkedPosition: _element.getBackgroundPosition(),
        checkedImage: _element.getBackgroundImage(),
        checkedTitle: _element.getTitle(),
        spriteOffset: _element.getSize().width,
        isCheckButton: false,
        isChecked: false
    }, options);

    var getBgImage = _element.getBackgroundImage();
    var _stateCalculator, _strategy, _isTouching, _isCheckButton, _isChecked;
    var _normalPosition, _normalImage, _normalTitle, _checkedPosition, _checkedImage, _checkedTitle, _spriteOffset, _initialCursor;


    function init() {
        _stateCalculator = settings.stateCalculator;
        _strategy = settings.strategy;
        _normalPosition = settings.normalPosition;
        _normalImage = settings.normalImage;
        _normalTitle = settings.normalTitle;
        _checkedPosition = settings.checkedPosition;
        _checkedImage = settings.checkedImage;
        _checkedTitle = settings.checkedTitle;
        _spriteOffset = settings.spriteOffset;
        _isCheckButton = settings.isCheckButton;
        _isChecked = settings.isChecked;
        _initialCursor = _element.getCursor();

        _element.bind({
            'mouseenter.imageButton': self.MouseEnter,
            'mouseleave.imageButton': self.MouseLeave,
            'mousedown.imageButton': self.MouseDown,
            'mouseup.imageButton': self.MouseUp,
            'touchstart.imageButton': self.TouchStart,
            'touchend.imageButton': self.TouchEnd,
            'touchcancel.imageButton': self.TouchCancel,
            'click.imageButton': self.Click
        });

        _stateCalculator.setIsDisabled(_element.getIsDisabled());
        updateState();
    }


    this.getIsCheckButton = function () { return _isCheckButton; };
    this.setIsCheckButton = function (value) { _isCheckButton = value; };

    this.getIsChecked = function () { return _isChecked; };
    this.setIsChecked = function (value) {
        if (value === _isChecked) {
            return;
        }

        _isChecked = value;
        updateState();
        onCheckChanged(_isChecked);
    };

    this.getIsDisabled = function () { return _stateCalculator.getIsDisabled(); };

    this.getNormalPosition = function () { return _normalPosition; };
    this.setNormalPosition = function (value) { _normalPosition = value; };

    this.getNormalImage = function () { return _normalImage; };
    this.setNormalImage = function (value) { _normalImage = value; };

    this.getCheckedPosition = function () { return _checkedPosition; };
    this.setCheckedPosition = function (value) { _checkedPosition = value; };

    this.getCheckedImage = function () { return _checkedImage; };
    this.setCheckedImage = function (value) { _checkedImage = value; };

    this.getSpriteOffset = function () { return _spriteOffset; };
    this.setSpriteOffset = function (value) { _spriteOffset = value; };


    // properties for dependencies
    this.getStateCalculator = function () { return _stateCalculator; };
    this.setStateCalculator = function (value) { _stateCalculator = value; };
    this.getStrategy = function () { return _strategy; };
    this.setStrategy = function (value) { _strategy = value; };


    this.destroy = function () {
        _element.revert();
        _element.unbind('.imageButton');
        _element.setBackground(_normalPosition, _normalImage);
        _element.setCursor(_initialCursor);
        delete _element;
    }

    this.MouseEnter = function () {
        if (_isTouching) {
            return;
        }

        _stateCalculator.setIsHovered(true);
        updateState();
    }

    this.MouseLeave = function () {
        if (_isTouching) {
            return;
        }

        _stateCalculator.setIsPressed(false); // We won't know when the mouse is unpressed.
        _stateCalculator.setIsHovered(false);
        updateState();
    }

    this.MouseDown = function () {
        if (_isTouching) {
            return;
        }

        _stateCalculator.setIsPressed(true);
        updateState();
    }

    this.MouseUp = function () {
        if (_isTouching) {
            _isTouching = false;
            return;
        }

        _stateCalculator.setIsPressed(false);
        updateState();
    }

    this.TouchStart = function () {
        _isTouching = true;

        _stateCalculator.setIsPressed(true);
        updateState();
    }

    this.TouchEnd = function () {
        _stateCalculator.setIsPressed(false);
        updateState();
    }

    this.TouchCancel = function () {
        self.TouchEnd();
    }

    this.Disable = function () {
        _stateCalculator.setIsDisabled(true);
        updateState();
    }

    this.Enable = function () {
        _stateCalculator.setIsDisabled(false);
        updateState();
    }

    this.Click = function () {
        if (!_isCheckButton || _stateCalculator.getIsDisabled()) {
            return;
        }

        self.setIsChecked(self.getIsChecked() ^ true);
        updateState();
    }


    function updateState() {
        var buttonState = _stateCalculator.calculate();
        var basePosition = useCheckedCss() ? _checkedPosition : _normalPosition;
        var baseImage = useCheckedCss() ? _checkedImage : _normalImage;
        var position = _strategy.calculateBackgroundPosition(buttonState, basePosition, _spriteOffset);
        var image = _strategy.calculateBackgroundImage(buttonState, baseImage);
        var isDisabled = buttonState == Mediasite.ImageButton.ButtonState.Disabled;
        var cursor = isDisabled ? 'default' : 'pointer';
        var title = useCheckedCss() ? _checkedTitle : _normalTitle;

        _element.setIsDisabled(isDisabled);
        _element.setCursor(cursor);
        _element.setTitle(title);
        _element.setBackground(position, image);
    }

    function useCheckedCss() {
        return _isCheckButton && _isChecked;
    }

    function onCheckChanged(isChecked) {
        _element.trigger('checkchanged', { isChecked: isChecked });
    }


    init();
}

// -===========================================================================


Mediasite.ImageButton.AdaptButtonElement = function (button) {
	if (button.getBackgroundPosition) {
		// Looks like this object already had the required interface.
		return button;
	}

	var tagName = button.tagName || button.get(0).tagName;
	if (tagName == 'DIV' || tagName == 'SPAN') {
		return new Mediasite.ImageButton.FancyButtonAdapter(button);
	} else {
		return new Mediasite.ImageButton.BasicButtonAdapter(button);
	}
}


// ImageButton.BasicButtonAdapter
// +===========================================================================

Mediasite.ImageButton.BasicButtonAdapter = function (element) {
	this._element = $(element);
}

Mediasite.ImageButton.BasicButtonAdapter.prototype = {

    revert: function () { /* noop */ },

    bind: function (events) { this._element.bind(events); },

    unbind: function (eventType) { this._element.unbind(eventType); },

    trigger: function () { this._element.trigger.apply(this._element, arguments); },

    getBackgroundPosition: function () {
        return this._getElementBackgroundPosition(this._element);
    },

    getBackgroundImage: function () {
        return this._getElementBackgroundImage(this._element);
    },

    setBackground: function (backgroundPosition, backgroundImage) {
        this._element.css('backgroundPosition', backgroundPosition);
        this._element.css('backgroundImage', backgroundImage);
    },

    getSize: function () {
        return { width: this._element.width(), height: this._element.height() };
    },

	getIsDisabled: function () {
		return this._element.prop('disabled') || false;
	},

	setIsDisabled: function (value) {
		this._element.prop('disabled', value);
	},
    getCursor: function () {
        return this._element.css('cursor') || '';
    },

    setCursor: function (value) {
        this._element.css('cursor', value);
    },

    getTitle: function () {
        return this._element.attr('title');
    },

    setTitle: function (value) {
        this._element.attr('title', value);
    },


    // Private by convention. (i.e. Don't mess with my underscore members!)
    _element: null, // expect contructor to set this.


    _getElementBackgroundPosition: function (element) {
        var position = element.css('backgroundPosition');
        if (!position) {
            position = new Mediasite.Css.BackgroundPosition(
				Mediasite.Css.Length.parse(element.css('background-position-x')),
				Mediasite.Css.Length.parse(element.css('background-position-y'))
			).toString();
        }
        return position;
    },

    _getElementBackgroundImage: function (element) {
        return element.css('backgroundImage') || '';
    }
};

// -===========================================================================


// ImageButton.FancyButtonAdapter
// +===========================================================================

Mediasite.ImageButton.FancyButtonAdapter = function (element) {
	Mediasite.ImageButton.BasicButtonAdapter.apply(this, arguments);

	this._divs = [this._makeImageDiv(1, 1), this._makeImageDiv(2, 0)];

	this._initialPosition = Mediasite.ImageButton.BasicButtonAdapter.prototype.getBackgroundPosition.call(this);
	this._initialImage = Mediasite.ImageButton.BasicButtonAdapter.prototype.getBackgroundImage.call(this);

	this._currentDivIndex = 0;
	this._setBackgroundCss(this._getCurrentDiv(), this._initialPosition, this._initialImage);

	this._changeOpacity(this._getCurrentDiv(), '1.0', '0ms');
	this._changeOpacity(this._getOtherDiv(), '0.0', '0ms');

	this._element.css('background', 'transparent');
	//Mediasite.ImageButton.BasicButtonAdapter.prototype.setBackground.call(this, 'top left', 'url("")');
};

Mediasite.ImageButton.FancyButtonAdapter.prototype = $.extend({}, new Mediasite.ImageButton.BasicButtonAdapter(), {
    revert: function () {
        Mediasite.ImageButton.BasicButtonAdapter.prototype.setBackground.call(this, this._initialPosition, this._initialImage);

        this._divs[0].remove();
        this._divs[1].remove();
    },

    getBackgroundPosition: function () {
        return this._getElementBackgroundPosition(this._getCurrentDiv());
    },

    getBackgroundImage: function () {
        return this._getElementBackgroundImage(this._getCurrentDiv());
    },

    setBackground: function (backgroundPosition, backgroundImage) {
        this._currentDivIndex ^= 1; // toggle between zero and one.

        this._setBackgroundCss(this._getCurrentDiv(), backgroundPosition, backgroundImage);

        this._changeOpacity(this._getCurrentDiv(), '1.0', this._transitionDuration);
        this._changeOpacity(this._getOtherDiv(), '0.0', this._transitionDuration);
        //this._getCurrentDiv().animate({ 'opacity': '1.0' }, this._transitionDuration, 'linear');
        //this._getOtherDiv().animate({ 'opacity': '0.0' }, this._transitionDuration, 'linear');
    },


    _divs: null,
    _currentDivIndex: 0,
    _transitionDuration: 150, // milliseconds
    _initialPosition: null,
    _initialImage: null,


    _setBackgroundCss: function (element, position, image) {
        element.css({
            'backgroundPosition': position,
            'backgroundImage': image
        })
    },

    _changeOpacity: function (element, opacity, transitionDuration) {
        var prefixes = ['-moz-', '-ms-', '-o-', '-webkit-', ''];
        var cssMap = $.extend(
			this._expandCssPropertyWithPrefixes('transition-property', 'opacity', prefixes),
			this._expandCssPropertyWithPrefixes('transition-duration', transitionDuration + 'ms', prefixes),
			this._expandCssPropertyWithPrefixes('transition-timing-function', 'linear', prefixes)
		);
        cssMap['opacity'] = opacity;

        element.css(cssMap);
    },

    _expandCssPropertyWithPrefixes: function (prop, value, prefixes) {
        var result = {};
        var i;
        for (i = 0; i < prefixes.length; i++) {
            result[prefixes[i] + prop] = value;
        }
        return result;
    },

    _getCurrentDiv: function () {
        return this._divs[this._currentDivIndex];
    },
    _getOtherDiv: function () {
        return this._divs[this._currentDivIndex ^ 1];
    },

    _makeImageDiv: function (zIndex, opacity) {
        return $('<div></div>').css({
            'width': '100%',
            'height': '100%',
            'position': 'absolute',
            'top': 0,
            'left': 0,
            'z-index': zIndex,
            'opacity': opacity
        }).appendTo(this._element);
    }
});

// -===========================================================================


// ImageButton.SpriteStrategy
// +===========================================================================

Mediasite.ImageButton.SpriteStrategy = function () {

	this.calculateBackgroundPosition = function (buttonState, baseCssPosition, spriteOffset) {
		if (!Mediasite.ImageButton.ButtonState.isDefined(buttonState)) {
			return baseCssPosition;
		}

		var basePosition = Mediasite.Css.BackgroundPosition.parse(baseCssPosition);

		var isPixelLeft = basePosition.getLeft().getUnit() == 'px' || basePosition.getLeft().getQuantity() == 0;

		if (!isPixelLeft) {
			return baseCssPosition;
		}

		var newLeftQuantity = basePosition.getLeft().getQuantity() - (spriteOffset * buttonState);

		var newPosition = new Mediasite.Css.BackgroundPosition(
			new Mediasite.Css.Length(newLeftQuantity, 'px'), // new calculated left.
			basePosition.getTop()); // preserve the previous top.

		return newPosition.toString();
	}

	this.calculateBackgroundImage = function (buttonState, baseCssImage) {
		return baseCssImage;
	}

}

// -===========================================================================


// ImageButton.MultiFileStrategy
// +===========================================================================

Mediasite.ImageButton.MultiFileStrategy = function () {

	var insertionLookup = {};
	insertionLookup[Mediasite.ImageButton.ButtonState.Hovered] = 'hovered';
	insertionLookup[Mediasite.ImageButton.ButtonState.Pressed] = 'pressed';
	insertionLookup[Mediasite.ImageButton.ButtonState.Disabled] = 'disabled';


	this.calculateBackgroundPosition = function (buttonState, baseCssPosition, spriteOffset) {
		return baseCssPosition;
	}

	this.calculateBackgroundImage = function (buttonState, baseCssImage) {
		if (insertionLookup[buttonState]) {
			return makeUrl(baseCssImage, insertionLookup[buttonState]);
		} else {
			return baseCssImage;
		}
	}


	var fileExtensionRegex = /(^\s*url\(['"]?.+)(\.\w+(?:[\?#].*)?['"]?\)\s*$)/i;

	function makeUrl(normalUrl, insertion) {
		if (typeof (normalUrl) != 'string') {
			return normalUrl;
		}

		var matches = normalUrl.match(fileExtensionRegex);

		if (matches == null || matches.length != 3) {
			return normalUrl;
		}

		var left = matches[1]; // e.g. 'url("myimage'
		var right = matches[2]; // e.g. '.png")'

		return left + '.' + insertion + right;
	}
}

// -===========================================================================


// ImageButton.ButtonStateCalculator
// +===========================================================================

Mediasite.ImageButton.ButtonStateCalculator = function () {

	var _isHovered = false;
	var _isPressed = false;
	var _isDisabled = false;


	this.getIsHovered = function () { return _isHovered; }
	this.setIsHovered = function (value) { _isHovered = value; }

	this.getIsPressed = function () { return _isPressed; }
	this.setIsPressed = function (value) { _isPressed = value; }

	this.getIsDisabled = function () { return _isDisabled; }
	this.setIsDisabled = function (value) { _isDisabled = value; }


	this.calculate = function () {
		if (_isDisabled) {
			return Mediasite.ImageButton.ButtonState.Disabled;
		} else if (_isPressed) {
			return Mediasite.ImageButton.ButtonState.Pressed;
		} else if (_isHovered) {
			return Mediasite.ImageButton.ButtonState.Hovered;
		} else {
			return Mediasite.ImageButton.ButtonState.Normal;
		}
	}

}

// -===========================================================================


// ImageButton.ButtonState
// +===========================================================================

Mediasite.ImageButton.ButtonState = {
	Normal: 0,
	Hovered: 1,
	Pressed: 2,
	Disabled: 3,

	isDefined: function (value) {
		return typeof (value) == 'number' &&
			value >= this.Normal &&
			value <= this.Disabled;
	}
};

// -===========================================================================


// jQuery.imageButton plugin
// +===========================================================================

(function() {
	
	var methods = {
			
			init: function (options) {
				var settings = $.extend({
					mode: 'sprite'
				}, options);
				settings.strategy = selectStrategy(settings.mode);
				settings.mode = undefined;
				
				return this.each(function() {

					var button = $(this);

					destroyButton(button);

					

					var data = new Mediasite.ImageButton(button, settings);
					button.data('imageButton', data);

				});
			},
			
			destroy: function () {
				return this.each(function() {
					
					var button = $(this);
					destroyButton(button);
				
				});
			},

			disable: function () {
				performActionOnImageButtons(this, function (button, data) {
					data.Disable();
				});
			},

			enable: function () {
				performActionOnImageButtons(this, function (button, data) {
					data.Enable();
				});
			},

            getIsDisabled: function() {
                var button = this.first();
				var data = button.data('imageButton');
				if (!data) {
					return; // has not been initialized as an imageButton.
				}

				return data.getIsDisabled();
            },

			getIsChecked: function() {
				var button = this.first();
				var data = button.data('imageButton');
				if (!data) {
					return; // has not been initialized as an imageButton.
				}

				return data.getIsChecked();
			},

			setIsChecked: function(isChecked) {
				return performActionOnImageButtons(this, function (button, data) {
					data.setIsChecked(isChecked);
				});
			}
			
		};

		var _strategies = {
			sprite: new Mediasite.ImageButton.SpriteStrategy(),
			multifile: new Mediasite.ImageButton.MultiFileStrategy()
		};

		
		function selectStrategy (mode) {
			return _strategies[mode.toLowerCase()];
		}
		
		function destroyButton(button) {
			var data = button.data('imageButton');

			if (data) {
				data.destroy();
				button.removeData('imageButton');
				delete data;
			}
		}

		function performActionOnImageButtons(buttons, action) {
			return buttons.each(function() {
				var button = $(this);
				var data = button.data('imageButton');
				if (!data) {
					return; // has not been initialized as an imageButton.
				}

				action(button, data);
			});
		}
		
		
		$.fn.imageButton = function (method) {
			if ( methods[method] ) {
				return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
			} else if ( typeof method === 'object' || ! method ) {
				return methods.init.apply( this, arguments );
			} else {
				$.error( 'Method ' +  method + ' does not exist on jQuery.imageButton' );
			}
		}
	
})();

// -===========================================================================
