/* Copyrighted by Infosys Technologies  */
/*global document, navigator  widgetLoader */
/*members E2, E3, E4, E5, E6, E7, E8, IE1, IE2, IE3, alias, appType,
    appendChild, attributes, body, border, browserLanguage, charAt, clean,
    content, createElement, customParams, dataType, empty, filter,
    getElementById, getElementsByTagName, getTimezoneOffset, hasOwnProperty,
    height, hostPageURL, href, httpEquiv, id, indexOf, innerHTML, join,
    language, length, localeDefault, localeMeta, localeURL, location, name,
    p1, p10, p2, p3, p4, p5, p6, p7, p8, p9, push, qs, random, readOnly,
    replace, special, split, substring, systemLanguage, test, title,
    toLowerCase, user, userLanguage, value, values, width, URL
*/
"use strict";
(widgetLoader = function () {

//TODO: Pass all catch blocks to one single function
//TODO: Pass all DOM actions to one single function

    // This array defines the required tests and filters for the meta data parameters passed to the loader
    // will be intrepreted in this format
    // alias will be used to contruct the iframe query string
    // filter will enum its properties to run corresponding filter functions
    // all other properties correspond to mandatory checks to be done on the variable
    // the order of these checks are the order its defined on this object meaning filter will happen first ,  if that element appears first
    // items can repeat , meaning any test can repeat itself if defined again
    //TODO:  this array should finally determine the metadata parameters handled by this script . Hard coded values like id , app should go off
    //TODO:  relate this array to the error messages array , possibly , merge
    var invariants = {
        // Required Metadata
        'id'        :  {'alias' : 'widgetInstanceId', 'dataType' : 'number', 'empty' : false},
        'appType'   :  {'alias' : '', 'filter' : ['trim', 'normal', "lowercase", 'urlencode'], 'dataType' : 'string', 'empty' : false, 'values' : ['rating', 'comment', 'tagging', 'tagcloud', 'bookmark']},
        'width'     :  {'alias' : '', 'dataType' : 'number', 'empty' : false},
        'height'    :  {'alias' : '', 'dataType' : 'number', 'empty' : false},
        // Optional Metadata
        'p1'             :  {'alias' : 'P1', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p2'             :  {'alias' : 'P2', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p3'             :  {'alias' : 'P3', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p4'             :  {'alias' : 'P4', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p5'             :  {'alias' : 'P5', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p6'             :  {'alias' : 'P6', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p7'             :  {'alias' : 'P7', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p8'             :  {'alias' : 'P8', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p9'             :  {'alias' : 'P9', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'p10'            :  {'alias' : 'P10', 'length' : 30, 'filter' : ['trim', 'normal', 'urlencode']},
        'user'           :  {'alias' : 'WID_USER', 'length' : 255, 'filter' : ['trim', 'normal', 'urlencode']},
        'hostPageURL'    :  {'alias' : 'cURL', 'length' : 255, 'special' : 'url', 'filter' : ['trim', 'urlencode'], 'dataType' : 'string'},
        'readOnly'       :  {'alias' : 'WID_READ_ONLY', 'filter' : ['trim', 'normal', 'urlencode'], 'values' : ['yes', 'no']},
        'localeDefault'  :  {'alias' : 'WID_DEFAULT_LOCALE', 'length' : 255, 'filter' : ['trim', 'normal', 'urlencode']},
        'localeMeta'     :  {'alias' : '', 'length' : 255, 'filter' : ['trim', 'normal', 'urlencode']},
        'localeURL'      :  {'alias' : 'WID_URL_LOCALE_MARKER', 'length' : 255, 'filter' : ['trim', 'normal', 'urlencode']},
        'customParams'   :  {'alias' : 'custom', 'length' : 255, 'filter' : ['trim', 'normal', 'urlencode']},
        'border'         :  {'alias' : '', 'dataType' : 'number'}, 
        'sensedcURL'     :  {'alias' : 'cURL', 'filter' : ['trim', 'urlencode']},        
        'sensedcName'    :  {'alias' : 'cName', 'filter' : ['trim', 'normal', 'urlencode']},
        'sensedLocale'   :  {'alias' : 'WID_DEFAULT_LOCALE', 'filter' : ['trim', 'normal', 'urlencode']},        
        'sensedcAuthor'  :  {'alias' : 'cAuthor', 'filter' : ['trim', 'normal', 'urlencode']},
        'sensedcDt'      :  {'alias' : 'cDt', 'filter' : ['trim', 'normal', 'urlencode']},
        'URL'            :  {'alias' : 'URL', 'length' : 2084}
    },

    //TODO:  PS , or someone sane should review the error messages
    errorMessages = {
        'E2' : 'unexpected datatype',
        'E3' : 'unexpected empty or null value',
        'E4' : 'unexpected value. Not empty error',
        'E5' : 'unexpected value. A valid URL of form "[http|https|ftp] : //[www].domain.TLD/[path_to_action][?parameters]" expected',
        'E6' : 'unexpected value. Cant find the specified element in the document DOM',
        'E7' : 'unexpected value. Supplied variable has a value which is outside the permitted value range.',
        'E8' : 'unexpected length. Parameter exceeds its maximum length limit.',
        'IE1' : 'Exception :  Cannot read required embed meta data. Is it provided on the page ?',
        'IE2' : 'Exception :  Invalid/unexpected parameters to function call',
        'IE3' : 'Exception :  Undefined error encountered'
    },
    appGateway = "http://inside.intel.com",

    /* PHP trim equalent js function */
    trim = function (str, charlist) {
        var whitespace, l = 0, i = 0;
        str += '';
        if (!charlist) {
            // default list
            whitespace = " \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";
        } else {
            // preg_quote custom list
            charlist += '';
            whitespace = charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1');
        }
        l = str.length;
        for (i = 0; i < l; i = i + 1) {
            if (whitespace.indexOf(str.charAt(i)) === -1) {
                str = str.substring(i);
                break;
            }
        }
        l = str.length;
        for (i = l - 1; i >= 0; i = i - 1) {
            if (whitespace.indexOf(str.charAt(i)) === -1) {
                str = str.substring(0, i + 1);
                break;
            }
        }
        return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
    },

    // Validator function. Closure makes use of errorLog caching
    // Please note that currently the last check in cache mode to this function will not return the variable even if it passes the test , instead , and error is thrown out
    sanitizeEntity = function () {
        var errorLog = {},

        // Data type checking
        checkData = function (var1, type1) {
            if (typeof var1 === type1) {
                return true;
            } else {
                throw ('E2');
            }
        },

        // Checking whether var is empty
        checkEmpty = function (var1, key1) {
            var isEmpty = false,
                key2;
            if (var1 === "" || var1 === 0 || var1 === "0" || var1 === null || var1 === false || var1 === undefined) {
                isEmpty = true;
            }
            if (typeof var1 === 'object') {
                isEmpty = true;
                for (key2 in var1) {
                    if (var1.hasOwnProperty(key2)) {
                        isEmpty = false;
                    }
                }
            }
            if  (isEmpty !== key1) {
                if  (isEmpty) {
                    throw ('E3');
                } else {
                    throw ('E4');
                }
            } else {
                return true;
            }
        },

        // Checking some special strings
        checkSpecial = function  (var1, key1) {
            if (key1 === 'url') {
                var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
                if (!regexp.test(var1)) {
                    throw ('E5');
                } else {
                    return true;
                }
            } else if (key1 === 'DOM') {
                if (document.getElementById(var1).id === undefined) {
                    throw ('E6');
                }
                return true;
            } else {
                throw new Error(errorMessages.IE2 + " at checkSpecial ExInfo: Invalid paramter : " + key1);
            }
        },
        
		// Checking the char length limit  Only strings should be passed to this function
        checkLength = function  (var1, key1) {
            if (var1.length > key1) {
                throw ('E8');
            } else {
                return true;
            }            
        },
		
        // Checking whether values are from a predefined array
        checkValues = function  (var1, key1) {
            if (new RegExp('^(' + key1.join('|') + ')$').test(var1)) {
                return true;
            } else {
                throw ('E7');
            }
        },

        // Filtering variables as required. No errors from this function
        doFilter = function  (var1, filters) {
            for (var filter in filters) {
                if (filters[filter] === 'trim') {
                    var1 = trim(var1);
                } else if (filters[filter] === 'normal') {
                    var1 = var1.replace(/[.]*[(){}$%#&@<>\/\\+'*][.]*/gi, "");
                } else if (filters[filter] === 'urlencode') {
                    var1 =  encodeURIComponent(var1);
                } else if (filters[filter] === 'lowercase') {
                    var1 = var1.toLowerCase();
                } else {
                    throw new Error(errorMessages.IE2 + " at doFilter ExInfo: Invalid paramter : " + filters[filter]);
                }
            }
            return var1;
        },

        // Clearing the cache and throwing the errorLog . This function shd be deprecated.
        getCachedErrorLog = function () {
            if (errorLog.clean) {
                return true;
            }
            var report = errorLog;
            delete(report.clean);
            errorLog = {};
            errorLog.clean = true;
            throw report;
        };

        errorLog.clean = true;

        // Main extern block in the closure.
        sanitizeEntity = function (entity, check, cacheFlush) {
            if (entity === "" && check === "" && cacheFlush) {
                return getCachedErrorLog();
            }
            var invariant = invariants[check], key, report;
            if (invariant === undefined) {
                throw new Error(errorMessages.IE2 + " at sanitizeEntity ExInfo: Invalid paramter : " + check);
            }
            errorLog[check] = [];
            for (key in invariant) {
                if (invariant.hasOwnProperty(key) && key !== 'alias') {
                    try {
                        if (key === 'filter') {
                            entity = doFilter(entity, invariant[key]);
                        } else if (key === 'dataType') {
                            checkData(entity, invariant[key]);
                        } else if (key === 'length') {
                            checkLength(entity, invariant[key]);
                        } else if (key === 'empty' && (typeof invariant[key] === 'boolean')) {
                            checkEmpty(entity, invariant[key]);
                        } else if (key === 'special') {
                            checkSpecial(entity, invariant[key]);
                        } else if (key === 'values' && (typeof invariant[key] === 'object')) {
                            checkValues(entity, invariant[key]);
                        } else {
                            throw new Error(errorMessages.IE2 + " at sanitizeEntity ExInfo: Invalid parameter : " + key);
                        }
                    }catch (e) {
                        if (e instanceof Error) {
                            throw e;
                        }
                        errorLog[check].push(e);
                    }
                }
            }

            if (errorLog[check].length === 0) {
                delete(errorLog[check]);
            } else {
                errorLog.clean = false;
            }

            if (cacheFlush === true && !errorLog.clean) {
                report = {};
                report = errorLog;
                delete(report.clean);
                errorLog = {};
                errorLog.clean = true;
                throw report;
            } else if (!errorLog[check]) {
                return entity;
            }

        };
        return sanitizeEntity(arguments[0], arguments[1], arguments[2]);
    },


    // Presentation function
    displayError = function (error, optional) {
        var message = "", report, reportItem, errorMessage, displayElement;
        if (error instanceof Error) {
            throw error;
        }
        for (report in error) {
            if (error.hasOwnProperty(report)) {
                for (reportItem in error[report]) {
                    if (error[report].hasOwnProperty(reportItem)) {
                        errorMessage = undefined;
                        errorMessage = errorMessages[error[report][reportItem]];
                        if (!errorMessage) {
                            throw new Error(errorMessages.IE3);
                        }
                        message += '<li>' + (((optional) ? "Optional but " : "") + report + " has an " + errorMessage) + '</li>';
                    }
                }
            }
        }
        try {
            document.write('<div> Initialization of the widget encountered with the following errors <ul>' + message + '</ul></div>');
        } catch (e) {
            throw new Error(message);
        }
    },


    // Optional variables are initialized to a URL qs from this function
    initializeOptional = function (optionalVars) {
        var optionals = {}, key;
        optionals.qs = "";
        for (key in optionalVars) {
            if (optionalVars.hasOwnProperty(key)) {
                if (!(key in invariants)) {
                    throw new Error(errorMessages.IE2 + " at initializeOptional ExInfo: Invalid parameter : " + key);
                }
                if (trim(optionalVars[key]) !== '') {
                    optionals[key] = sanitizeEntity(optionalVars[key], key);
                    if (invariants[key].alias !== '') {
                        optionals.qs += '&' + invariants[key].alias + '=' + optionals[key];
                    }
                }
            }
        }
        sanitizeEntity('', '', true);
        return optionals;
    },


    // Used for finally creating the Iframe.
    decorateDOM = function (applicationURL, wid_width, wid_height, wid_border) {
        document.write('<div> <iframe src="' + applicationURL + '" width="' + wid_width + '" height= "' + wid_height + '"  frameBorder="' + wid_border + '" ></iframe></div>');
    },

    // URL construction
    constructURL = function (widgetId, appGateway, appType, optionalParams) {
        var finalURL, sensedLocaleInfo,

        getClientUTC = function () {
            var currentTime = new Date(),
            month = currentTime.getMonth() + 1,
            day = currentTime.getDate(),
            year = currentTime.getFullYear(),
            date = month + "/" + day + "/" + year,            
            date1 = new Date(currentTime.getFullYear(), 0, 1, 0, 0, 0, 0),
            date2 = new Date(currentTime.getFullYear(), 6, 1, 0, 0, 0, 0),
            temp1 = date1.toGMTString(),
            date3 = new Date(temp1.substring(0, temp1.lastIndexOf(" ") - 1)),
            temp2 = date2.toGMTString(),
            date4 = new Date(temp2.substring(0, temp2.lastIndexOf(" ") - 1)),
            hoursDiffStdTime = (date1 - date3) / (1000 * 60 * 60),
            hoursDiffDaylightTime = (date2 - date4) / (1000 * 60 * 60),
            offset = "";

            if (hoursDiffStdTime === -3 || hoursDiffStdTime === -2 || hoursDiffStdTime === 10.5 || hoursDiffStdTime === 11 || hoursDiffStdTime === 13) { 
                offset = (currentTime.getTimezoneOffset() / 60) * -1;           
            } else {
                offset = hoursDiffStdTime;
            }
            return offset;
        },

        getBroswerLocale = function () {
            if (navigator) {
                if (navigator.language) {
                    return navigator.language;
                } else if (navigator.browserLanguage) {
                    return navigator.browserLanguage;
                } else if (navigator.systemLanguage) {
                    return navigator.systemLanguage;
                } else if (navigator.userLanguage) {
                    return navigator.userLanguage;
                }
            }
        },

        // Fetches the meta data from HEAD
        fetchMeta = function (metaIdentifier) {
            var elems = document.getElementsByTagName('meta'),
                        i;
            for (i = 0; i < elems.length ; i = i + 1) {
                try {
                    if (elems[i].name === metaIdentifier || elems[i].httpEquiv === metaIdentifier) {
                        return elems[i].attributes.content.value;
                    }
                }catch (e1) {
                // Some sort of Find error has happened. Possibly the meta tag we are looking for is not availiable .Lets ignore this as returns are handled below
                }
            }
            return "";
        },

        fetchLocale = function (localeMeta) {
            var localeMetaItems = localeMeta.split('%2C'),
                metaInfo, i;
            for (i = 0; i < localeMetaItems.length ; i = i + 1) {
                metaInfo = fetchMeta(localeMetaItems[i]);
                if (metaInfo !== "") {
                    return metaInfo;
                }
            }
            return '';
        },

        switchGateway = function (appGateway, appType) {
            switch (appType) {
            case "rating":
                appGateway += "/widgets/rating/jsp/rating/getRatings.action?";
                break;
            case "comment":
                appGateway += "/widgets/comment/jsp/comment/Comments.action?";
                break;
            case "tagging":
                appGateway += "/widgets/tagging/jsp/tagging/getTagName.action?";
                break;
            case "tagcloud":
                appGateway += "/widgets/tagging/jsp/tagging/getCloudTagName.action?inputPage=cloud&showCriteria=cloud&";
                break;
            case "bookmark":
                appGateway += "/widgets/bookmark/jsp/bookmarking/getBookmark.action?";
                break;
            case "rss":
                appGateway += "/rss-service/jsp/rssWidget.jsp?";
                break;
            case "rsscenter":
                appGateway += "/rss-service/jsp/rssWidgetCenter.jsp?";
                break;            
            }
            return appGateway;
        };


        appGateway = switchGateway(appGateway, appType);
        finalURL = appGateway;       
        finalURL += 'widgetInstanceId=' + widgetId;
        finalURL += '&timeZone=' + encodeURIComponent(getClientUTC());
        finalURL += '&cName=' + encodeURIComponent(sanitizeEntity(document.title, 'sensedcName'));
        if (optionalParams.qs.indexOf('cURL') === -1) {
            finalURL += '&cURL=' + sanitizeEntity(document.location.href, 'sensedcURL');
        }

        // If localeDefault is passed with the widget embed metadata , simply assign it to WID_DEFAULT_LOCALE
        //  else look for localeMeta , if these csv can fetch a locale info from the metas , pass that along ,
        //  else turn on the WID_URL_OR_META_FLAG  and pass localeURL to WID_URL_LOCALE_MARKER
        // TODO : check flag logic
        sensedLocale = (optionalParams.localeMeta === undefined || optionalParams.localeMeta === '') ? undefined : sanitizeEntity(fetchLocale(optionalParams.localeMeta), 'sensedLocale');
        if (optionalParams.qs.indexOf('WID_DEFAULT_LOCALE') === -1 && sensedLocale !== '' && sensedLocale !== false && sensedLocale !== undefined) {
            finalURL += '&WID_DEFAULT_LOCALE=' + sensedLocale;
        } else {
            finalURL += '&WID_URL_OR_META_FLAG=true';
        }
        finalURL += '&cAuthor=' + sanitizeEntity(fetchMeta('web_author_id'), 'sensedcAuthor');
        finalURL += '&cDt=' + sanitizeEntity(fetchMeta('created_date'), 'sensedcDt');
        finalURL += '&WID_BROWSER_LOCALE=' + getBroswerLocale();
        finalURL += optionalParams.qs + '&rnd=' + (Math.random() * 5);
        return finalURL;
    };

    // Booting up
    return function () {
        // Quick check to see we are ok
        try {
            if (typeof arguments[0] !== 'object') {
                throw new Error(errorMessages.IE1);
            }
            if (
              arguments[0].id === undefined     ||
              arguments[0].appType === undefined    ||
              arguments[0].width === undefined  ||
              arguments[0].height === undefined
            ) {
                throw new Error(errorMessages.IE1);
            }
        }catch (e1) {
            displayError(e1);
            return;
        }

        var widgetId, appType, width, height, optionalParams, applicationURL;

        // Full sanity check for the required meta , caching error till last validation
        try {
            widgetId =   sanitizeEntity(arguments[0].id, 'id');
            appType =  sanitizeEntity(arguments[0].appType, 'appType');
            width =      sanitizeEntity(arguments[0].width, 'width');
            height =     sanitizeEntity(arguments[0].height, 'height', true);
        }catch (e2) {
            displayError(e2);
            return;
        }

        // Checking and setting the optional arguments
        try {
            optionalParams = initializeOptional(arguments[1]);
        }catch (e3) {
            displayError(e3, true);
            return;
        }

        // Checking and setting the final URL 
        try {
            applicationURL = sanitizeEntity(constructURL(widgetId, appGateway, appType, optionalParams), 'URL', true);   
        }catch (e4) {
            displayError(e4);
            return;
        }
        
        // Creating and attaching IFrame to DOM
        decorateDOM(applicationURL, width, height, optionalParams.border);

        // this was fun , so lets return true
        return true;
    };

}());