YAHOO.namespace("sputnik");

YAHOO.sputnik.playerUtil = function() {
	return {
		getTimeString: function (left)
		{
			var hours = Math.floor(left/60/60);
			if(hours > 0)
				left = left - (hours * 60 * 60);
			var minutes = Math.floor(left/60);
			if(minutes > 0)
				left = left - (minutes * 60);
			var seconds = Math.round(left);
			return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);
		}
	}
}();

YAHOO.sputnik.player = function(container, objectID) {

    // Initialize YUI libraries
    this.dom = YAHOO.util.Dom;
    this.event = YAHOO.util.Event;
    this.rpc = YAHOO.util.Connect;
    this.json = YAHOO.lang.JSON;

    // Initialize properties
    this.container = this.dom.get(container);
    this.objectID = objectID;

    //Holds data on current program
    this.programData = {};

    //Holds data on current user
    this.userData = {};

    //Placeholder for dialog container
    this.dialog = null;

    //Is it a broadcast
    this.isBroadcast = false;

    /**
     * Statecodes for the different states the player can have
     */
    this.states = {
    	INITIALIZING: 			'initializing',
    	FETCHING_PROGRAM: 		'fetching_program',
    	VALIDATING_IP: 			'validating_ip',
        CONNECTION_ERROR: 		'connection_error',
        FETCHING_USER: 			'fetching_user',
        IP_ERROR: 				'ip_error',
        VALIDATE_PROGRAM: 		'valdiate_program',
        USER_DATA_ERROR: 		'user_data_error',
        VALIDATE_USER: 			'validate_user',
        USER_NO_PRODUCT: 		'user_no_product',
        FETCHING_TICKET: 		'fetching_ticket',
        USER_NOT_AVAILABLE: 	'user_not_available',
        PRODUCT_NOT_AVAILABLE:	'product_not_available',
        TICKET_NOT_AVAILABLE: 	'ticket_not_available',
        PLAYING: 				'playing',
        PROGRAM_END: 			'program_end',
        IP_ERROR_BLACKLIST: 	'ip_error_blacklist',
        IP_ERROR_DK: 			'ip_error_dk',
        IP_ERROR_EBU: 			'ip_error_ebu',
        STREAM_CLOSED: 			'stream_closed',
        USER_LIMIT_EXCEEDED: 	'user_limit_exceeded'
    }

    //Events
    /**
     * Triggered when the state of the player changes
     */
    this.onStateChange = new YAHOO.util.CustomEvent("onStateChange", this);

    /**
     * Triggered when program data has been successfully fetched
     */
    this.onProgramDataReady =
        new YAHOO.util.CustomEvent("onProgramDataReady", this);

    /**
     * Triggered when program data couldnt be successfully fetched
     */
    this.onProgramDataError =
        new YAHOO.util.CustomEvent("onProgramDataError", this);

    /**
     * Triggered when there was an error validating remote address.
     * Not triggered when validation returns false, but responseText is invalid
     */
    this.onRemoteAddressValidationError =
        new YAHOO.util.CustomEvent("onRemoteAddressValidationError", this);

    /**
     * Triggered when remote address is valid
     */
    this.onRemoteAddressIsValid =
        new YAHOO.util.CustomEvent("onRemoteAddressIsValid", this);

    /**
     * Triggered when remote address is NOT valid
     */
    this.onRemoteAddressIsNotValid =
        new YAHOO.util.CustomEvent("onRemoteAddressIsNotValid", this);

    /**
     * Triggered when userdata has been successfully fetched
     */
    this.onUserDataReady = new YAHOO.util.CustomEvent("onUserDataReady", this);

    /**
     * Triggered when userdata couldnt be successfully fetched
     */
    this.onUserDataError = new YAHOO.util.CustomEvent("onUserDataError", this);

    /**
     * Triggered when DRM is required to see the program
     */
    this.onDRMRequired = new YAHOO.util.CustomEvent("onDRMRequired", this);

    /**
     * Triggered when all client checks has been completed and client is ready
     * and capable to play the program
     */
    this.onClientReady =
        new YAHOO.util.CustomEvent("onClientReady", this);

    /**
     * Triggered when individualization is needed
     */
    this.onTriggerIndividualization =
        new YAHOO.util.CustomEvent("onTriggerIndividualization", this);

    /**
     * Triggered when all user related checks has been completed and client
     * is authorized to play the program
     */
    this.onUserReady = new YAHOO.util.CustomEvent("onUserReady", this);

    /**
     * Triggered when we cant fetch a user
     */
    this.onUserUnavailable =
        new YAHOO.util.CustomEvent("onUserUnavailable", this);

    /**
     * Triggered when max user per account i exceeded
     */
    this.onUserLimitExceeded =
    	new YAHOO.util.CustomEvent("onUserLimitExceeded", this);

    /**
     * Triggered when product is invalid to current user
     */
    this.onProductNotValid =
        new YAHOO.util.CustomEvent("onProductNotValid", this);

    /**
     * Triggered when we have an invalid ticket
     */
    this.onTicketUnavailable
        = new YAHOO.util.CustomEvent("onTicketUnavailable", this);

    /**
     * Triggered when we want to hide the player
     */
    this.onHidePlayer = new YAHOO.util.CustomEvent("onHidePlayer", this);

    /**
     * Triggered when we want to show the player
     */
    this.onShowPlayer = new YAHOO.util.CustomEvent("onShowPlayer", this);

    /**
     * Triggered when we want to show the player
     */
    this.onHideControls = new YAHOO.util.CustomEvent("onHideControls", this);

    /**
     * Triggered when we want to enable the controls
     */
    this.onEnableControls = new YAHOO.util.CustomEvent("onEnableControls", this);

    /**
     * Triggered when we want to disable the controls
     */
    this.onDisableControls = new YAHOO.util.CustomEvent("onDisableControls", this);

    /**
     * Initialize and run object
     */
    this.construct = function()
    {
        //handle states
        this.onStateChange.subscribe(this.handleStates, this);

        //Validate client
    	this.onProgramDataReady.subscribe(this.validateClient, this);

        //Handle user access to program
        this.onClientReady.subscribe(this.validateUser, this);

        //lets give the user a ticket and get him out of here if possible
        //this.onUserReady.subscribe(this.playProgram, this);

    	this.setState(this.states.INITIALIZING);

        //Get the program data
    	this.getProgramData();
    }

    /**
     * Validate client specific stuff where no user is needed
     * DRM, IP, blaclisting etc
     */
    this.validateClient = function()
    {
    	//Check to see if the stream is closed
    	if(this.programData.isClosed)
    	{
    		this.setState(this.states.STREAM_CLOSED);
    	}
    	else
    	{
	        //When program data is ready, we validate the IP against blacklist and GEO ip
	        this.validateRemoteAddress();
	        //If ip is valid continue, and check DRM support
	        this.onRemoteAddressIsValid.subscribe(this.checkDRM, this);
	        //else display ip error message
	        this.onRemoteAddressIsNotValid.subscribe(this.handleIpError, this);
    	}
    }

    /**
     * Validates user specific stuff
     * Program is chargable, user logged in, has product etc
     */
    this.validateUser = function(type, args, me)
    {
    	this.setState(this.states.VALIDATE_PROGRAM);

        //User validation handlers
        this.onUserDataReady.subscribe(this.handleUserData, this);
        this.onUserDataError.subscribe(this.handleUserError, this);

        //Check if user has access to product
        this.onUserUnavailable.subscribe(this.handleUserUnavailable, this);
        this.onUserLimitExceeded.subscribe(this.handleUserLimitExceeded, this);
        this.onProductNotValid.subscribe(this.handleProductNotValid, this);

        //When ticket is unavailable
        this.onTicketUnavailable.subscribe(this.handleTicketUnavailable, this);

        //Check if program is free of charge
        if(!this.programData.nocharge)
        {
            //It is not - we need a user
            this.getUserData();
        }
        else
        {
        	//User is allowed to view the program.
        	this.fetchTicket();
        }

    }

    /**
     * Handles userdata event
     */
    this.handleUserData = function(type, args, me)
    {
        if(this.userData.isValidUser)
        {
            //We need a user has access to the product of the program
            //if he doesnt have the product, we present him with the apropriate products
		    this.setState(this.states.VALIDATE_USER);
		    var d = new Date();
		    this.rpc.asyncRequest(
		        'POST',
		        '/player/validate/product/?'+d.getTime(),
		        {
		            success: function (o)
		            {
                        var response = this.json.parse(o.responseText);
                        if(response.hasProduct)
                        {
                            this.fetchTicket();
                        }
                        else
                        {
                            this.onProductNotValid.fire();
                        }
                    },
		            failure: this.connectionError,
		            scope: this
		        },
		        'object='+this.objectID+'&isBroadcast='+(this.isBroadcast?1:0)
		    );
        }
        else
        {
        	//If user has maxed his subscription
        	if(this.userData.maxUsers)
        	{
                this.onUserLimitExceeded.fire();
        	}
        	else
        	{
                //if we dont have a user, we need to login or create one
                this.onUserUnavailable.fire();
        	}
        }
    }

    /**
     * Handle user data error
     */
    this.handleUserError = function(type, args, me)
    {
    	this.setState(this.states.USER_DATA_ERROR);
    }

    /**
     * Handler for product not valid
     */
    this.handleProductNotValid = function(type, args, me)
    {
        this.setState(this.states.PRODUCT_NOT_AVAILABLE);

        this.hidePlayer();

        this.showProducts()

        var login = this._getPlayerLoginElm();
        this.dom.addClass(login, "loggedin");
        this.hideDialog();
        login.style.display = "block";
    }

    /**
     * Handler for user unavailable event
     */
    this.handleUserUnavailable = function(type, args, me)
    {
        this.setState(this.states.USER_NOT_AVAILABLE);

        this.hidePlayer();

        this.showProducts()

        var login = this._getPlayerLoginElm();
        this.hideDialog();
        login.style.display = "block";

        if (SPUTNIK.login) {
            SPUTNIK.login.buildInPageLoginBox("player-login-ctx");
        }
    }

    /**
     * Handler for user limit
     */
    this.handleUserLimitExceeded = function(type, args, me)
    {
        this.setState(this.states.USER_LIMIT_EXCEEDED);
    }

    /**
     * Hides the player area
     */
    this.hidePlayer = function()
    {
        this.onHidePlayer.fire();
    }

    /**
     * Show the player area
     */
    this.showPlayer = function()
    {
        this.onShowPlayer.fire();
    }

    /**
     * Hides player controls
     */
    this.hideControls = function()
    {
        this.onHideControls.fire();
    }

    /**
     * Enables player controls
     */
    this.enableControls = function()
    {
    	this.onEnableControls.fire();
    }

    /**
     * Disables player controls
     */
    this.disableControls = function()
    {
    	this.onDisableControls.fire();
    }

    /**
     * Show player end dialog
     */
    this.showPlayerEnd = function()
    {
    	var end = this._getPlayerEndElm()
    	if(end)
    	{
    		end.style.display = "block";
    	}
    }

    /**
     * Show products
     */
    this.showProducts = function()
    {
        var productContainer = document.getElementById("player-products");
        if(productContainer)
        {
            var products = this.programData.products;
            if(products.length==0)
                productContainer.style.display = "none";

            productContainer.innerHTML = "";

			if (products.length>3) {
				if (products[0].title = 'Sputnik Guld')	products.shift();
				while (products.length>3) products.pop();
			}

            for(var i=0; i<products.length; i++)
            {
                if(i>=5) break;
                productContainer.appendChild(this._createProduct(
                    products[i].title,
                    products[i].description,
                    products[i].buyUrl,
                    products[i].buyLabel,
                    products[i].priceStr,
                    (products[i].altBuyUrl ? true : false)
                ));
                if (products[i].altBuyUrl && products[i].altBuyUrl.length>0) {
                    //display alternative purchase url for this product
                    var link = document.createElement("a");
                    link.appendChild(document.createTextNode('Betal med SMS, andet kreditkort eller kampagnekode.'));
                    link.href = products[i].altBuyUrl;
                    productContainer.appendChild(link);
                }
            }

        }
    }

    /**
     * Crate a product node
     * @return DOMNode
     */
    this._createProduct = function(title, description, buyUrl, buyLabel, priceStr, oneclick)
    {
    	var product = document.createElement("div");

		this.dom.addClass(product, "product");
		if (title.search(/sport/i) > -1) {
			this.dom.addClass(product, "product_sport");
		} else {
		    var top = 2*99;//enkeltkøb

		    if (title.search(/film/i) > -1)
		        top = 1*99;
		    else if (title.search(/guld/i) > -1)
			    top = 0;
	        else if (title.search(/basis/i) > -1)
	            top = 3*99;
	        else if (title.search(/live/i) > -1)
	            top = 4*99;
		    product.style.backgroundPosition = '0 -'+top+'px';
		}

	    product.title = title;
	    var alreadyClicked = false;
	    if (oneclick && TV2.Sputnik.getOneclickOption() == 'ask') {
	        $("#oneclick-buy-btn").click(function() {
	            alreadyClicked = true;
	            var dontask = $("#oneclick-dontask-chk").attr('checked');
	            if (dontask)
	                $.ajax( {url: "/login/oneclicksave/", data: {newOcOption: 'auto'}, async: false, type: "post"});
                SPUTNIK.overlay.close();
                location.href = buyUrl;
            });
            $("#oneclick-cancel-btn").click(function() {
                SPUTNIK.overlay.close();
            });
	        $(product).click(function(event) {
	            if (alreadyClicked)
    	            return;
	            SPUTNIK.login.openConfirmationDialog(null,'sputnik-oneclickconfirmation');
	        });
	    } else
	        $(product).click(function(event) {
	            if (!alreadyClicked) {
	                alreadyClicked = true;
	                location.href = buyUrl;
	            }
	        });

    	var headElm = document.createElement("h3");
    	headElm.appendChild(document.createTextNode(title));
    	product.appendChild(headElm);

    	var descElm = document.createElement("p");
    	descElm.appendChild(document.createTextNode(description));
	    if (priceStr.length) {
	        var bpriceElm = document.createElement("b");
	        bpriceElm.appendChild(document.createTextNode(' '+priceStr));
	        descElm.appendChild(bpriceElm);
	    }
    	product.appendChild(descElm);

    	var buyCont = document.createElement("p");
    	product.appendChild(buyCont);

	    YAHOO.util.Event.on(
            product,
            'mouseover',
            function(e) {
                //add hover element if it doesn't already exist
                var existing = YAHOO.util.Dom.getElementsByClassName('producthover', 'div', this );
                if (existing.length)
                    return;

	        var hover = document.createElement("div");
                YAHOO.util.Dom.addClass(hover, "producthover");

                //have the hover element remove itself when mouseout on it
                hover.product = this;
                YAHOO.util.Event.on(
                    hover,
                    'mouseout',
                    function(e) {
                        this.product.removeChild(this);
		    }
                );

                this.appendChild(hover);
	    }
        );

        return product;
    }

    /**
     * Get placeholder for login
     * @return DOMNode
     */
    this._getPlayerLoginElm = function()
    {
        var elms = this.dom.getElementsByClassName("player-login", "div", this.container);
        if(elms.length > 0)
           return elms[0];
        return null;
    }

    /**
     * Get placeholder for player end dialog
     * @return DOMNode
     */
    this._getPlayerEndElm = function()
    {
        var elms = this.dom.getElementsByClassName("player-end", "div", this.container);
        if(elms.length > 0)
           return elms[0];
        return null;
    }

    /**
     * Handler for unavailable ticket
     */
    this.handleTicketUnavailable = function(type, args, me)
    {
        this.setState(this.states.TICKET_NOT_AVAILABLE);
    }

    /**
     * Fetches ticket from server and fires appropriate event
     */
    this.fetchTicket = function(type, args, me)
    {
    	this.setState(this.states.FETCHING_TICKET);
    	var d = new Date();
        this.rpc.asyncRequest(
            'POST',
            '/player/ticket/?'+d.getTime(),
            {
                success: function (o)
                {
                    var response = this.json.parse(o.responseText);
                    if(response.ticket)
                    {
                        this.onUserReady.fire(response.ticket);
                    }
                    else
                    {
                        if(response.errorcode) {
                            this.onUserReady.fire(response.ticket, response.errorcode);
                        }else{
                            this.onUserReady.fire(); //File did not produce a valid key
                        }
                    }
                },
                failure: this.connectionError,
                scope: this
            },
            'object='+this.objectID+'&isBroadcast='+(this.isBroadcast?1:0)
        );
    }

    /**
     * Play the program and embed the object
     */
    this.playProgram = function(type, args, me)
    {
        this.setState(this.states.PLAYING);
    }

    /**
     * Send request for adding view
     */
    this.addProgramView = function(wmpid)
    {
        var d = new Date();
        this.rpc.asyncRequest(
            'POST',
            '/player/view/?'+d.getTime(),
            {},
            'object='+this.objectID+'&isBroadcast='+(this.isBroadcast?1:0)+(wmpid?'&wmpid='+wmpid:'')
        );
    }

    /**
     * Crate a param for Object
     * @param string nameValue
     * @param string value
     * @return DOMNode
     */
    this._createParam = function(nameValue, value)
    {
    	var param = document.createElement("param");
    	param.name = nameValue;
    	param.value = value;
    	return param;
    }

    /**
     * Set state of player
     */
    this.setState = function(code)
    {
    	this.onStateChange.fire(code);
    }

    /**
     * Show dialog with id
     */
    this.showDialog = function(dialogid)
    {
        if(dialogid && document.getElementById(dialogid))
        {
            if(this.dialog)
                this.dialog.style.display = "none";
            this.dialog = document.getElementById(dialogid);
            this.dialog.style.display = "block";
        }
    }

    /**
     * Handle state changes
     */
    this.handleStates = function(type, args, me)
    {
    	switch(args[0])
    	{
    		case this.states.INITIALIZING:
    		case this.states.FETCHING_PROGRAM:
    		case this.states.VALIDATE_PROGRAM:
    		case this.states.FETCHING_TICKET:
                this.hidePlayer();
    		    this.showDialog("dialog-initializing");
    		    break;

    		case this.states.IP_ERROR:
            case this.states.IP_ERROR_BLACKLIST:
                this.hidePlayer();
                this.showDialog("dialog-ip");
    		    break;

            case this.states.IP_ERROR_DK:
                this.hidePlayer();
                this.showDialog("dialog-ip-dk");
    		    break;

            case this.states.IP_ERROR_EBU:
                this.hidePlayer();
                this.showDialog("dialog-ip-ebu");
    		    break;

    		case this.states.CONNECTION_ERROR:
                this.hidePlayer();
                this.showDialog("dialog-error");
                break;

    		case this.states.STREAM_CLOSED:
    			this.hidePlayer();
    			this.showDialog("dialog-stream-closed");
    			//record this in omniture!
    			if (this.isBroadcast) {
    			    if (s_pageError)
    			        s_pageError('broadcast closed','playBroadcast');
    			} else {
    			    if (s_pageError)
    			        s_pageError('vod closed','playVod');
    			}
    			break;

    		case this.states.USER_LIMIT_EXCEEDED:
    			this.hidePlayer();
    			this.showDialog("dialog-max-users");
    			break;

            case this.states.PLAYING:
            	this.hideDialog();
            	this.showPlayer();
            	this.enableControls();
                break;

            case this.states.PROGRAM_END:
        		this.hideDialog();
        		this.hidePlayer();
                this.hideControls();
                this.showPlayerEnd();
                break;
    	}
    }

    /**
     * Hide dialog container
     */
    this.hideDialog = function()
    {
    	if(this.dialog)
    	   this.dialog.style.display = "none";
    }

    /**
     * Validate the ip of the user
     */
    this.validateRemoteAddress = function()
    {
    	this.setState(this.states.VALIDATING_IP);
    	var d = new Date();
    	this.rpc.asyncRequest(
    	    'POST',
    	    '/player/validate/ip/?'+d.getTime(),
    	    {
    	    	success: function (o) {
			        try
			        {
			            var response = this.json.parse(o.responseText)
			            if(response.isValidIp)
			            {
			               this.onRemoteAddressIsValid.fire();
			            }
			            else
			            {
			               this.onRemoteAddressIsNotValid.fire(response.cause)
			            }
			        }
			        catch (e)
			        {
			            this.onRemoteAddressValidationError.fire(e);
			        }
    	    	},
    	    	failure: this.connectionError,
    	    	scope: this
    	    },
    	    'object='+this.objectID+'&isBroadcast='+(this.isBroadcast?1:0)
    	);
    }

    /**
     * Handles remote address validation and triggers correct event
     */
    this.handleRemoteAddressValidation = function(type, args, me)
    {
    	try
        {
            var response = this.json.parse(args[0].responseText)
            if(response.isValidIp)
            {
               this.onRemoteAddressIsValid.fire();
            }
            else
            {
               this.onRemoteAddressIsNotValid.fire()
            }
        }
        catch (e)
        {
            this.onRemoteAddressValidationError.fire(e);
        }
    }

    /**
     * Handle ip error event
     */
    this.handleIpError = function(type, args, me)
    {
        switch(args[0])
        {
            case 'blacklist':
                this.setState(this.states.IP_ERROR_BLACKLIST);
                break;
            case 'DK':
                this.setState(this.states.IP_ERROR_DK);
                break;
            case 'EBU':
                this.setState(this.states.IP_ERROR_EBU);
                break;

            default:
                this.setState(this.states.IP_ERROR);
                break;
        }
    }

    /**
     * Check if DRM is required
     */
    this.checkDRM = function(type, args, me)
    {
    	if(this.programData.hasDRM)
        {
    		this.onDRMRequired.fire();
        }
    	else
    	{
            //Client is ready and not more client validation is needed.
    		this.onClientReady.fire();
    	}
    }

    /**
     * Get program data
     */
    this.getProgramData = function()
    {
    	 this.setState(this.states.FETCHING_PROGRAM);
         var d = new Date();

    	 this.rpc.asyncRequest(
    	    'POST',
    	    '/player/program/?' + d.getTime(),
    	    {
    	        success: function(o)
                {
				    try
				    {
				        this.programData = this.json.parse(o.responseText)
				        this.onProgramDataReady.fire();
				    }
				    catch (e)
				    {
				        this.onProgramDataError.fire(e);
				    }
				},
    	        failure: this.connectionError,
    	        scope: this
    	    },
    	    'object='+this.objectID+'&isBroadcast='+(this.isBroadcast?1:0)
        );
    }

    /**
     * Get userdata
     */
    this.getUserData = function()
    {
    	this.setState(this.states.FETCHING_USER);
    	var d = new Date();
    	this.rpc.asyncRequest('GET', '/player/user/?'+d.getTime(), {
    		success: function(o)
		    {
		        try {
		            this.userData = this.json.parse(o.responseText);
		            this.onUserDataReady.fire();
		        }
		        catch (e)
		        {
		            this.onUserDataError.fire(e);
		        }
		    },
    		failure: this.connectionError,
    		scope: this
    	});
    }

    /**
     * General fallback RPC errorhandler
     */
    this.connectionError = function(o)
    {
    	this.setState(this.states.CONNECTION_ERROR);
    }

}

