/*
 *  Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */

var CONSTANTS = {
    urlSeperator: '/',
    queryParamStreamName : '?streamname=',
    queryParamStreamVersion : '&version=',
    queryParamLastUpdatedTime : '&lastUpdatedTime=',
    urlSecureTransportWebsocket : 'wss://',
    urlSecureTransportHttp : 'https://',
    colon : ':',
    defaultIntervalTime : 10 * 1000,
    defaultHostName : 'localhost',
    defaultSecurePortNumber : '9443',
    defaultMode : 'AUTO',
    processModeHTTP : 'HTTP',
    processModeWebSocket : 'WEBSOCKET',
    processModeAuto : 'AUTO',
    numThousand : 1000,
    websocketTimeAppender : 400,
    websocketSubscriptionEndpoint : 'portal/uipublisher/websocketSubscriptionEndpoint.jag',
    httpEventRetrievalEndpoint : 'portal/uipublisher/httpEventRetrievalEndpoint.jag'
};

var websocket = null;
var webSocketUrl;
var httpUrl;
var cepHostName;
var cepPortNumber;
var isErrorOccured = false;
var lastUpdatedtime = -1;
var polingInterval;
var stream;
var streamVersion;
var firstPollingAttempt;
var processMode;
var onSuccessFunction;
var onErrorFunction;
var terminateWebsocketInstance = false;
var pollingContinue = true;

function subscribe(streamName,version,intervalTime,
                   listeningFuncSuccessData,listeningFuncErrorData,cepHost,cepPort,mode){
    stopPollingProcesses();
    stream = streamName;
    streamVersion = version;
    onSuccessFunction = listeningFuncSuccessData;
    onErrorFunction = listeningFuncErrorData;

    if(intervalTime == null || intervalTime == ""){
        polingInterval = CONSTANTS.defaultIntervalTime;
    } else{
        polingInterval = intervalTime * CONSTANTS.numThousand;
    }

    if(cepHost == null || cepHost == ""){
        cepHostName = CONSTANTS.defaultHostName;
    } else{
        cepHostName = cepHost;
    }

    if(cepPort == null || cepPort == ""){
        cepPortNumber = CONSTANTS.defaultSecurePortNumber;
    } else{
        cepPortNumber = cepPort;
    }

    if(mode == null || mode == ""){
        processMode = CONSTANTS.defaultMode;
    } else{
        processMode = mode;
    }

    webSocketUrl = CONSTANTS.urlSecureTransportWebsocket + cepHostName + CONSTANTS.colon + cepPortNumber +
        CONSTANTS.urlSeperator + CONSTANTS.websocketSubscriptionEndpoint;

    if(processMode == CONSTANTS.processModeHTTP){
        firstPollingAttempt = true;
        pollingContinue = true;
        startPoll();
    } else{
        initializeWebSocket(webSocketUrl);
    }
}

/**
 * Initializing Web Socket
 */
function initializeWebSocket(webSocketUrl){
    websocket = new WebSocket(webSocketUrl);
    websocket.onopen = webSocketOnOpen;
    websocket.onmessage = webSocketOnMessage;
    websocket.onclose = webSocketOnClose;
    websocket.onerror = webSocketOnError;
}

function getWebsocketSubscriptionMessage(streamName, streamVersion, streamProperties, streamValues) {
    if (streamProperties.length === streamValues.length) {
        var message = {};
        message.streamName = streamName;
        message.streamVersion = streamVersion;
        var i;
        for (i = 0; i < streamProperties.length; i++) {
            message.filterProps = [];
            message.filterProps.push({
                'name': streamProperties[i],
                'value': streamValues[i]
            });
        }
        return JSON.stringify(message);
    } else {
        console.log('stream properties and values are not in equal size');
    }
}

/**
 * Get the parameters as query parameters.
 * This method parses those parameters and returns.
 * */
function getAllQueryParamsFromURL() {
    var queryParamList = {}, qParam;
    var urlQueryString = decodeURIComponent(window.top.location.search.substring(1));

    if (urlQueryString) {
        var queryStringPairs = urlQueryString.split('&');
        for (var i = 0; i < queryStringPairs.length; i++) {
            qParam = queryStringPairs[i].split('=');
            queryParamList[qParam[0]] = qParam[1];
        }
        return queryParamList;

    } else {
        return null;
    }
}

/**
 * Web socket On Open
 */
var webSocketOnOpen = function () {
    var params = getAllQueryParamsFromURL();
    var deviceId;
    var owner;
    if (params) {
        owner = params["owner"];
        deviceId = params["deviceId"];
    }
    var filterPropNames = ["meta_owner", "meta_deviceId"];
    var filterPropVals = [owner, deviceId];
    var data = getWebsocketSubscriptionMessage(stream, streamVersion, filterPropNames, filterPropVals);
    websocket.send(data);
};

/**
 * On server sends a message
 */
var webSocketOnMessage = function (evt) {
    var event = evt.data;
    var array = JSON.parse(event);
    constructPayload(array);
};

/**
 * On server close
 */
var webSocketOnClose =function (e) {

    if(isErrorOccured){
        if(processMode != CONSTANTS.processModeWebSocket){
            firstPollingAttempt = true;
            pollingContinue = true;
            startPoll();
        }
    } else{
        if(!terminateWebsocketInstance){
            waitForSocketConnection(websocket);
        } else{
            terminateWebsocketInstance = false;
        }

    }
};

/**
 * On server Error
 */
var webSocketOnError = function (err) {
    var error = "Error: Cannot connect to Websocket URL:" + webSocketUrl + " .Hence closing the connection!";

    onErrorFunction(error);
    isErrorOccured = true;

};

/**
 * Gracefully increments the connection retry
 */
var waitTime = CONSTANTS.numThousand;
function waitForSocketConnection(socket, callback){
    setTimeout(
        function () {
            if (socket.readyState === 1) {
                initializeWebSocket(webSocketUrl);
                console.log("Connection is made");
                if(callback != null){
                    callback();
                }
                return;
            } else {
                websocket = new WebSocket(webSocketUrl);
                waitTime += CONSTANTS.websocketTimeAppender;
                waitForSocketConnection(websocket, callback);
            }
        }, waitTime);
}

/**
 * Polling to retrieve events from http request periodically
 */
function startPoll(){

    (function poll(){
        setTimeout(function(){
            httpUrl = CONSTANTS.urlSecureTransportHttp + cepHostName + CONSTANTS.colon + cepPortNumber +
                      CONSTANTS.urlSeperator + CONSTANTS.httpEventRetrievalEndpoint + CONSTANTS.queryParamStreamName + stream +
                      CONSTANTS.queryParamStreamVersion + streamVersion + CONSTANTS.queryParamLastUpdatedTime + lastUpdatedtime;;
            $.getJSON(httpUrl, function(responseText) {
                if(firstPollingAttempt){
                    /*var data = $("textarea#idConsole").val();
                     $("textarea#idConsole").val(data + "Successfully connected to HTTP.");*/
                    firstPollingAttempt = false;
                }
                var eventList = $.parseJSON(responseText.events);
                if(eventList.length != 0){
                    lastUpdatedtime = responseText.lastEventTime;
                    for(var i=0;i<eventList.length;i++){
                        var arr = eventList[i];
                        constructPayload(arr);
                    }
                }
                if(pollingContinue){
                    startPoll();
                }
            })
                .fail(function(errorData) {
                    var errorData = JSON.parse(errorData.responseText);
                    onErrorFunction(errorData.error);
                });
        }, polingInterval);
    })()
}

function stopPollingProcesses(){

    //stopping the Websocket
    if(websocket != null){
        terminateWebsocketInstance = true;
        websocket.close();
    }
    //stopping the HTTPS Request
    pollingContinue = false;

}

function constructPayload(eventsArray){

    var streamId = stream + CONSTANTS.colon + streamVersion;
    var twoDimentionalArray = [eventsArray];
    onSuccessFunction(streamId,twoDimentionalArray);

}
