/*
    Copyright 2015 Adobe Systems Incorporated.  All rights reserved. 

    Purpose:
        DevicePreviewMain.js is responsible for managing the preview session.
        This module communicated with Preview Service and starts the WebSocket
        App for Dw-device peer-to-peer communication.
*/

/*jslint nomen: true, vars:true, plusplus:true*/
/*global $, console, QRCode, URI*/

function Main() {
    'use strict';
    this._accessToken = '';
    this._adobeId = '';
    this._userId = '';
    this._sessionId = '';
    this._sessionExpiryTimeout = null;
    this._currentError = window.preview.config.PREVIEW_ERRORS.ERROR_NONE;
    this._isReInitilised = false;
    this._locale = '';
    this.primaryBrowser = '';
    this._isInitializedFirstTime = false;
    this.m_dragInProgress = false;
    this.m_offset = {"x":0,"y":0};

    this.setAccessToken = function (accessToken) {
        this._accessToken = accessToken;
    };
    
    this.getAccessToken = function () {
        return this._accessToken;
    };

    this.setAdobeId = function (adobeId) {
        this._adobeId = adobeId;
    };

    this.getAdobeId = function () {
        return this._adobeId;
    };

    this.setUserId = function (userId) {
        this._userId = userId;
    };

    this.getUserId = function () {
        return this._userId;
    };
    
    this.setCurrentError = function (error) {
        this._currentError = error;
    };

    this.getCurrentError = function () {
        return this._currentError;
    };

    this.getIsReInitialised = function () {
        return this._isReInitilised;
    };
    
    this.setIsReInitialised = function (isReInitialise) {
        this._isReInitilised = isReInitialise;
    };
    
    this.setSessionId = function (sessionId) {
        this._sessionId = sessionId;
    };

    this.getSessionId = function () {
        return this._sessionId;
    };
 
    this.setLocale = function (locale) {
        this._locale = locale;
    };
    
    this.getLocale = function () {
        return this._locale;
    };
    
    this.getIsInitializedFirstTime = function () {
        return this._isInitializedFirstTime;
    };
    
    this.setSessionExpiryTimeout = function (timeLeft) {
        if (this._sessionExpiryTimeout) {
            window.clearInterval(this._sessionExpiryTimeout);
        }
        
        this._sessionExpiryTimeout = window.setTimeout($.proxy(this.handleSessionExpiry, this), timeLeft);
    };
    
    this.mouseMoveHandler = function(event) {
        if (this.m_dragInProgress) {
            var argsobj = {"x":this.m_offset.x, "y":this.m_offset.y};
            window.DWDevicePreviewCallJsBridgingFunction("movePreviewWindow", argsobj);
        }
    }
    
    this.mouseDownHandler = function(event) {
        if (event.which == 1) { 
            this.m_dragInProgress = true;
            this.m_offset.x = event.clientX;
            this.m_offset.y = event.clientY;
        }
    }
    
    this.mouseUpHandler = function(event) {
        if (event.which == 1) {
            this.m_dragInProgress = false;
            this.m_offset.x = 0;
            this.m_offset.y = 0;
            window.DWDevicePreviewCallJsBridgingFunction("resizeIfRequired"); 
        }
    }
    
    this.initHandlers = function () {
        window.DWDevicePreviewCallJsBridgingFunction("updateDeviceCount", {count: window.preview.utils.getDevicesCount()});
        window.DWDevicePreviewCallJsBridgingFunction("getPrimaryBrowser", {'callback': $.proxy(this.setPrimaryBrowser, this)});
        window.DWDevicePreviewCallJsBridgingFunction("getDefaultbrowserList", {'callback': $.proxy(this.refreshBrowserList, this)});
        
        // event handlers for moving preview window
        window.addEventListener('mousemove', this.mouseMoveHandler.bind(this));
        window.addEventListener('mousedown', this.mouseDownHandler.bind(this));
        window.addEventListener('mouseup',   this.mouseUpHandler.bind(this));
        
        $('.browserListHeader').text(window.preview.utils.translate('PREVIEW_IN_BROWSER'));
        $('.devicelistHeader').text(window.preview.utils.translate('PREVIEW_IN_DEVICE'));
        $('.editList').text(window.preview.utils.translate('EDIT_BROWSER'));
        $(".editList").unbind("click");
        $('.editList').on('click', $.proxy(window.preview.handlers.onEditBrowserList, window.preview.handlers));
        $('.headerTable > .arrow-down').unbind("click");
        $('.headerTable > .arrow-down').click(function () {
            if ($('.deviceListToggle').is(":visible")) {
                $('.browserListToggle').toggle();
                $(this).toggleClass("arrow-right").toggleClass("arrow-down");
                window.preview.main.adjustWindowHeight();
            }
        });
        $('.browserListHeader').unbind("click");
        $('.browserListHeader').click(function () {
            if ($('.deviceListToggle').is(":visible")) {
                $('.browserListToggle').toggle();
                if ($('.headerTable > .arrow-down').length) {
                    $('.headerTable > .arrow-down').toggleClass("arrow-right").toggleClass("arrow-down");
                } else {
                    $('.headerTable > .arrow-right').toggleClass("arrow-right").toggleClass("arrow-down");
                }
                window.preview.main.adjustWindowHeight();
            }
        });
        
        $('.deviceHeader > .arrow-down').unbind("click");
        $('.deviceHeader > .arrow-down').click(function () {
            if ($('.browserListToggle').is(":visible")) {
                $('.deviceListToggle').toggle();
                $(this).toggleClass("arrow-right").toggleClass("arrow-down");
                window.preview.main.adjustWindowHeight();
            }
        });
        
        $('.devicelistHeader').unbind("click");
        $('.devicelistHeader').click(function () {
            if ($('.browserListToggle').is(":visible")) {
                $('.deviceListToggle').toggle();
                if ($('.deviceHeader > .arrow-down').length) {
                    $('.deviceHeader > .arrow-down').toggleClass("arrow-right").toggleClass("arrow-down");
                } else {
                    $('.deviceHeader > .arrow-right').toggleClass("arrow-right").toggleClass("arrow-down");
                }
                window.preview.main.adjustWindowHeight();
            }
        });
    };
    
    //--------------------------------------------------------------------
    // FUNCTION: initApp
    //
    // DESCRIPTION:
    //  Entry point for Preview Service in Dreamweaver. It starts the preview session
    //  which in turn will start the Websocket server to establish peer-to-peer connections
    //  with devices.
    //
    // ARGUMENTS:
    //  userId: Adobe ID of the user
    //  accessToken: Access Token
    //  isEnvProd: Stage or Production environment
    //
    // RETURNS:
    //  nothing 
    //--------------------------------------------------------------------
    this.initApp = function (adobeId, userId, accessToken, isEnvProd, serverIP, serverPort, locale, isReInitialize, localAddress) {
        if (isEnvProd === "true") {
            window.preview.environment = window.preview.config.PRODUCTION;
        } else {
            window.preview.environment = window.preview.config.STAGE;
        }
        if (isReInitialize === "true") {
            this._isReInitilised = true;
        }
        window.preview.config.DW_SERVER_HOSTS = serverIP;
        window.preview.config.DW_LOCAL_HOST = localAddress;
        window.preview.config.DW_SERVER_PORT = serverPort;
        this.setAccessToken(accessToken);
        this.setAdobeId(adobeId);
        this.setUserId(userId);
        this.setLocale(locale);
        this.initHandlers();

        
        /* initApp can get called again if Dw refreshes the access token.
        * Any code beyond this point should handle multiple init calls gracefully without destroying the existing session. 
        */
    };
    
    //--------------------------------------------------------------------
    // FUNCTION: initLocalApp
    //
    // DESCRIPTION:
    //  Entry point for Preview Service in Dreamweaver in case of no internet or if DW is not able to fetch userID, accesstoken etc 
    //  It starts the local preview session
    //
    // ARGUMENTS:
    //  localAddress: localhost
    //  localPort: port on which the connection is made
    //
    // RETURNS:
    //  nothing 
    //--------------------------------------------------------------------
    this.initLocalApp = function (localAddress, localPort, isInitializedFirstTime) {
        window.preview.environment = window.preview.config.PRODUCTION;
        this._isReInitilised = false;
        window.preview.config.DW_SERVER_HOSTS = localAddress;
        window.preview.config.DW_LOCAL_HOST = localAddress;
        window.preview.config.DW_SERVER_PORT = localPort;
        this._isInitializedFirstTime = JSON.parse(isInitializedFirstTime);
        this.initHandlers();
       
        
        /* initLocalApp can get called again if Dw refreshes the access token.
        * Any code beyond this point should handle multiple init calls gracefully without destroying the existing session. 
        */
        this.startLocalPreviewSession(true);
    };
    
    
    //--------------------------------------------------------------------
    // FUNCTION: setCurrentViewState
    //
    // DESCRIPTION:
    //  Shows appropriate error message if non-HTML or non-PHP document is being viewed.
    //
    // ARGUMENTS:
    //  isHTML: if the document viewed is HTML or PHP then true else false
    //
    // RETURNS:
    //  nothing 
    //--------------------------------------------------------------------
    this.setCurrentViewState = function (isHTML) {
        if (JSON.parse(isHTML) === true) {
            $(".devicesConnected").show();
            $(".nonHTMLerror").hide();
        } else {
            $(".nonHTMLerror > span").text(window.preview.utils.translate('NON_HTML_PHP_DOC_ERROR'));
            $(".browserListToggle > .nonHTMLerror").css({
                "height": Math.max($(".browserListToggle > .devicesConnected > tbody").height(), parseInt($(".browserListToggle > .nonHTMLerror").css("min-height"), 10)) + "px",
                "display": "table-cell"
            });
            $(".browserListToggle > .devicesConnected").hide();
        }
    };


    //--------------------------------------------------------------------
    // FUNCTION:
    //   setPrimaryBrowser
    //
    // DESCRIPTION:
    //  Sets primary browser
    //
    // ARGUMENTS:
    //   None
    //
    // RETURNS:
    //   nothing 
    //--------------------------------------------------------------------
    this.setPrimaryBrowser = function (browser) {
		this.primaryBrowser = browser;
    };
	//--------------------------------------------------------------------
    // FUNCTION:
    //   refreshBrowserList
    //
    // DESCRIPTION:
    //   Adds installed browsers
    //
    // ARGUMENTS:
    //   None
    //
    // RETURNS:
    //   nothing 
    //--------------------------------------------------------------------
    this.refreshBrowserList = function (browsers) {
        var i = 0, browserName;
        
        var needsUpdate = false;
         
        for (i = 0; i < browsers.length; i += 2) {
            var presentBrowserName = $($($('.pibList')[parseInt(i / 2, 10)]).children().children()[0]).text();
            // First part checks if the position of browser is changed or it is removed from the list
            // Second part checks if the shortcut is changed or not
            // In these cases only we need to update the refresh browser list
            if (presentBrowserName !== browsers[i] || (i + 1 < browsers.length && this.primaryBrowser !== browsers[i + 1] && $($('.pibList')[parseInt(i / 2, 10)]).children().children()[1])) {
                needsUpdate = true;
                break;
            }
        }
        
            
        if (needsUpdate === true) {
            $('.pibList').remove();
            for (i = 0; i < browsers.length; i += 2) {
                browserName = browsers[i];
                var shortcut = '';
                var shortcutTemplate = '';
                
                if (this.primaryBrowser === browsers[i + 1]) {
                    if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) { // Mac OS 10.3 uses F12 for expose, so use Opt+F12 instead
                        shortcut += "Opt+F12";
                    } else {
                        shortcut += "F12";
                    }
                    shortcutTemplate = "<span class=\"shortcut\">" + shortcut + "</span>";
                }
                
                var previewStr = window.preview.utils.translate('PREVIEW_IN');

                var deviceTemplate =  "<tr id=\"" + browserName + "\" class=\"deviceEntryRow pibList\"><td class=\"deviceInfo\"><span>" + browserName + "</span>" + shortcutTemplate + "</td></tr>";
                $('.browserList > tbody').append(deviceTemplate);
                //window.preview.handlers.installHoverHandler($('.deviceEntryRow').last());
            }
            $('.deviceEntryRow').off('click', $.proxy(window.preview.handlers.onLaunchURLinBrowser, window.preview.handlers));
            $('.deviceEntryRow').on('click', $.proxy(window.preview.handlers.onLaunchURLinBrowser, window.preview.handlers));
            window.preview.handlers.applyTheme();
        }
        this.adjustWindowHeight();
    };

    //--------------------------------------------------------------------
    // FUNCTION:
    //   createConsumerSession
    //
    // DESCRIPTION:
    //   Communicates with Preview Service and starts a preview session
    //
    // ARGUMENTS:
    //   None
    //
    // RETURNS:
    //   nothing 
    //--------------------------------------------------------------------
    this.createConsumerSession = function () {
        var clientData = {
            "ip": window.preview.config.DW_SERVER_HOSTS,
            "port":  window.preview.config.DW_SERVER_PORT,
            "locale": this.getLocale(),
            "adobeid": URI.encode(this.getAdobeId())
        }, self = this,
            successCallback = function (response) {
                var location = this.getResponseHeader("location"),
                    sessionId = response.guid,
                    libLoadSuccess = function (ip) {
                        window.preview.socketApp.initializeSocketApp(ip);
                        if (self.getIsReInitialised) {
                            window.preview.socketApp.kickAllDevicesOut();
                        }
                        self.updatePreviewUrl(location);
                    },
                    libLoadFail = function (showFirewallError) {
                        var msg = showFirewallError ? window.preview.utils.translate('DEVICE_FIREWALL_ERROR') : window.preview.utils.translate('DEVICE_PROXY_ERROR'),
                            link = window.preview.utils.translate('DEVICE_TROUBLESHOOT'),
                            url = window.preview.utils.getTroubleshootingPageURL(),
                            icon = window.preview.config.ICON_PATHS.GENERIC_ERROR;
                        
                        url += window.preview.config.TROUBLESHOOTING_ANCHOR.FIREWALL_ID;
                        window.preview.handlers.setMessageUrlIcon(msg, link, url, icon);
                        if (showFirewallError) {
                            window.preview.metricsController.trackEvent('firewall-error', null, true);
                        }
                    };
                
                window.preview.config.PREVIEW_SHORT_URL = location;
                self.setSessionId(sessionId);
                self.setSessionExpiryTimeout(window.preview.config.INITIAL_SESSION_EXPIRY_TIME);
                self.keepSessionAlive();
                
				self.loadLibraries(libLoadSuccess, libLoadFail);
                self.adjustWindowHeight();
            },
            errorCallback = function () {
                var msg = '', link = '', url = '', icon = '';
                if (window.navigator.onLine) {
                    self.setCurrentError(window.preview.config.PREVIEW_ERRORS.ERROR_NO_SESSION_ID);
                    msg = window.preview.utils.translate('DEVICE_CANNOT_GENERATE_SESSION');
                    link = window.preview.utils.translate('DEVICE_TROUBLESHOOT');
                    url = window.preview.utils.getTroubleshootingPageURL();
                    icon = window.preview.config.ICON_PATHS.GENERIC_ERROR;
                    window.preview.metricsController.trackEvent('preview-service-failure', null, true);
                } else {
                    self.setCurrentError(window.preview.config.PREVIEW_ERRORS.ERROR_NO_INTERNET);
                    msg = window.preview.utils.translate('DEVICE_NOT_CONNECTED_TO_INTERNET');
                    icon = window.preview.config.ICON_PATHS.NO_INTERNET_ERROR;
                }
                window.preview.handlers.setMessageUrlIcon(msg, link, url, icon);
                self.adjustWindowHeight();
            };
        
        window.preview.utils.doAjaxRequest(
            window.preview.utils.sprintf(window.preview.config.PAIRING_SERVICE_URLS.API_CREATE_CONSUMER_SESSION, "dreamweaver"),
            'POST',
            JSON.stringify(clientData),
            successCallback,
            errorCallback
        );
    };
	
	//--------------------------------------------------------------------
    // FUNCTION:
    //   adjustWindowHeight
    //
    // DESCRIPTION:
    //   Increases or Decreases the height of the DP window according to the content
    //
    // ARGUMENTS:
    //   None
    //
    // RETURNS:
    //   nothing 
    //--------------------------------------------------------------------
	this.adjustWindowHeight = function () {
		if (window.DWDevicePreviewCallJsBridgingFunction && $(".mainContainer").outerHeight() !== $("body").outerHeight()) {
			window.DWDevicePreviewCallJsBridgingFunction("increaseWindowHeight", {height: $(".mainContainer").outerHeight() - $("body").outerHeight()});
        }
	};


    this.touchSession = function () {
        var sessionId = this.getSessionId(),
            self = this,
            successCallback = function (response) {
                if (this.status === window.preview.config.NO_CONTENT_STATUS_CODE) {
                    var sessionExpiryTime = new Date(this.getResponseHeader('expires'));
                    var currentTime = new Date(this.getResponseHeader('Date'));
                    var timeLeft = sessionExpiryTime - currentTime;
                    self.setSessionExpiryTimeout(timeLeft);
                }
            },
            errorCallback = function () {
                if (window.navigator.onLine) {
                    self.handleSessionExpiry(); //this is a bit redundant, but wise
                }
            };
        
        window.preview.utils.doAjaxRequest(
            window.preview.utils.sprintf(window.preview.config.PAIRING_SERVICE_URLS.API_PING_SESSION, sessionId),
            'PUT',
            null,
            successCallback,
            errorCallback
        );
    };
    
    this.keepSessionAlive = function () {
        var self = this;
        setInterval(function () {
            self.touchSession();
        }, window.preview.config.SESSION_PING_INTERVAL);
    };
    
    this.startPreviewSession = function () {
        /*
        * If a session already exists then just ping the session. It it has been invalidated we would create a new session anyway.
        * If there is no existing session, create a new session.
        */
        if (window.navigator.onLine) {
            if (this.getSessionId()) {
                this.touchSession();
            } else {
                this.createConsumerSession();
            }
        }
    };
    
    this.startLocalPreviewSession = function (forceKick) {
        var self = this;
		var libLoadSuccess = function (ip) {
                window.preview.socketApp.initializeSocketApp(ip);
                if (self.getIsReInitialised || forceKick) {
                    window.preview.socketApp.kickAllDevicesOut();
                }
            },
            libLoadFail = function (showFirewallError) {
                var msg = showFirewallError ? window.preview.utils.translate('DEVICE_FIREWALL_ERROR') : window.preview.utils.translate('DEVICE_PROXY_ERROR'),
                    link = window.preview.utils.translate('DEVICE_TROUBLESHOOT'),
                    url = window.preview.utils.getTroubleshootingPageURL(),
                    icon = window.preview.config.ICON_PATHS.GENERIC_ERROR;

                url += window.preview.config.TROUBLESHOOTING_ANCHOR.FIREWALL_ID;
                window.preview.handlers.setMessageUrlIcon(msg, link, url, icon);
                if (showFirewallError) {
                    window.preview.metricsController.trackEvent('firewall-error', null, true);
                }
            };

        this.loadLibraries(libLoadSuccess, libLoadFail);
    };
    
    this.updatePreviewUrl = function (url) {
        
        var urlObj = new URI(url);
        
        if (urlObj.protocol() === "") {
            urlObj.protocol("http");
        }
        
        window.preview.socketApp.sendPreviewUrl(window.preview.environment.PREVIEW_SERVICE.HOST +
            '/public/dreamweaver/index.html?dwses=' + this.getSessionId() +
            '&adobeid=' + URI.encode(this.getAdobeId()) +
            '&guid=' + this.getUserId()
            );
        
        var browserPreviewURL = window.preview.environment.PREVIEW_SERVICE.HOST +
            '/public/dreamweaver/index.html?dwses=' + this.getSessionId() +
            '&adobeid=' + URI.encode(this.getAdobeId()) +
            '&guid=' + this.getUserId();
        window.DWDevicePreviewCallJsBridgingFunction("setbrowserpreviewURL", browserPreviewURL);
        $('#link').attr('href', urlObj.toString());

        if (window.preview.main.getIsReInitialised() && $("#link").html() !== urlObj.toString()) {
            $('#devicePreviewDesc').text(window.preview.utils.translate('DEVICE_PREVIEW_DESCRIPTION_RESCAN'));
        } else {
            $('#devicePreviewDesc').text(window.preview.utils.translate('DEVICE_PREVIEW_DESCRIPTION'));
        }

        $('#link').html(urlObj.toString());
        if ($('#link').get(0).offsetWidth < $('#link').get(0).scrollWidth) {
            $('#link').attr('title', urlObj.toString());
        } else {
            $('#link').attr('title', '');
        }
         
        $('#link').removeClass('disabled');
        urlObj.addSearch("m", "q");
        var qrcode = new QRCode(document.getElementById("qrCode"), {
            width : 60,
            height : 60,
            correctLevel: QRCode.CorrectLevel.L
        });

        qrcode.makeCode(urlObj.toString());
        $("#qrCode").removeAttr('title');
        $('#imageHolder').hide();
        this.adjustWindowHeight();
        this.setCurrentError(window.preview.config.PREVIEW_ERRORS.ERROR_NONE);
    };
	
	this.loadLibraries = function (loadCallback, failCallback) {
        var doc = window.document,
            scripts = document.head.getElementsByTagName('script'),
            clientScriptPreNode = scripts[0];
		
        var ipArray = window.preview.config.DW_SERVER_HOSTS.split(',');
        
        if (ipArray && ipArray.length > 0) {
            var index  = 0,
                self = this,
                ipArrayLength = ipArray.length,
                currentErrorCount = 0,
                foundSuccessIP = false,
                errorCallback = function () {
                    currentErrorCount += 1;
                    if (currentErrorCount === ipArrayLength) {
                        //since we clearly failed, we would like to know if we failed due to firewall/proxy issue
                        //we will fire another request based through raw xmlhttprequest to identify that, if we succeed then it is
                        //proxy issue and if we timeout then it is firewall
                        var req = new XMLHttpRequest(),
                            failCalled = false;
                        req.timeout = 5000;
                        req.ontimeout = function () {
                            if (!failCalled) {
                                failCallback(true);
                            }
                        };
                        req.onreadystatechange = function () {
                            if (req.readyState === 4) {
                                failCalled = true;
                                if (req.status === 403) {
                                    failCallback(false);
                                } else {
                                    failCallback(true);
                                }
                            }
                        };
                        req.open('GET', window.preview.config.DW_SERVER_PROTOCOL + "://" + this.ip + ":" + window.preview.config.DW_SERVER_PORT + "/socket.io/socket.io.js", true);
                        req.send(null);
                    }
                },
                successCallback = function () {
                    if (!foundSuccessIP) {
                        foundSuccessIP = true;
                        loadCallback(this.ip);
                    }
                };
            
            for (index = 0; index < ipArrayLength; ++index) {
                var socketLib = doc.createElement('script');
                socketLib.onload = successCallback;
                socketLib.onerror = errorCallback;
                socketLib.ip = ipArray[index];
                socketLib.src = window.preview.config.DW_SERVER_PROTOCOL + "://" + ipArray[index] + ":" + window.preview.config.DW_SERVER_PORT + "/socket.io/socket.io.js";
                doc.head.insertBefore(socketLib, clientScriptPreNode);
            }
        }
    };
    
    this.setUpLocalization = function (successCallback) {
        /*
         * Set up strings for localization.
         */
        if ($ !== 'undefined') {
            var stringsPath = 'strings/';

            $.i18n.properties({
                name: 'DevicePreviewStrings',
                language: ' ',
                path: stringsPath,
                mode: 'map',
                callback: successCallback
            });
        }
    };
    
    this.handleSessionExpiry = function () {
        window.preview.socketApp.kickAllDevicesOut();
        window.location.reload(true); //just do refresh, if we are connected internet, we will fetch new session id 
    };
}

window.preview.main = window.preview.main || new Main();
