'use strict';

// Components
var utils = angular.module('utils', []);

define(["moment", 'viewer', 'pusher', 'clipboard', 'plupload', 'THREE'], function (moment, viewer, pusher, clipboard, plupload, THREE) {

    // Prevent warning from moment
    // “Accessing Moment through the global scope” warning message
    utils.factory("moment", function () {
        return moment;
    });

    // Require HTML5 viewer
    utils.factory("HTML5Viewer", function () {
        return viewer;
    });

    utils.factory("pusher", function () {
        return pusher;
    });

    utils.factory('ClipboardJS', function () {
        return clipboard;
    });

    utils.factory('plupload', function () {
        return plupload;
    });
    utils.factory('THREE', function () {
        return THREE;
    })
});


// jQuery
utils
    .factory('$', function () {

        return window.$;
    })

    // Lodash
    .factory('_', function () {

        return window._;
    })

    // Toast
    .factory('noty', function () {

        return window.noty;
    })

    // Bootstrap modal
    .factory('BootstrapDialog', function () {

        return window.BootstrapDialog;
    })

    // Utilities
    .factory('util', [
        '_', 'moment', 'app', "$ocLazyLoad", "$i18next", "$http", 'configServer',
        function (_, moment, app, $ocLazyLoad, $i18next, $http, configServer) {

            return {

                // Get the key from the path
                translate: function (path, opts) {

                    return $i18next(path, opts);
                },

                // Get moment object
                // @param {Object|String} date : Can be date object or string format date
                // @param {String} format
                // @return {Object} Moment object
                getMoment: function (date, format, timezone) {
                    var d = angular.isObject(date) ? moment.utc(date) : moment.utc(date, format || app.DATE_FORMAT);

                    return (timezone) ? d.add(timezone, 'hours') : d;
                },
                // Format date with specific format
                // @param {Object|String} date : Can be date object or string format date
                // @param {String} format
                // @return {String} String date with specific format
                formatDate: function (date, format, lang, timezone) {
                    switch (lang) {
                        case 'vi':
                        case 'ja':
                            moment.locale(lang);
                            break;
                        default:
                            moment.locale('en');
                    }
                    return this.getMoment(date, app.DATE_FORMAT, timezone).format(format || app.DATE_FORMAT);
                },

                setTimeForStartDate: function (start) {
                    var newsdate = new Date(start);
                    newsdate.setHours(0);
                    newsdate.setMinutes(0);
                    newsdate.setSeconds(0);
                    return newsdate;
                },

                setTimeForEndDate: function (end) {
                    var newedate = new Date(end);
                    newedate.setHours(23);
                    newedate.setMinutes(59);
                    newedate.setSeconds(59);
                    return newedate;
                },

                // Get the diff between 2 days
                // @param date1 ( required ) If the date is using to compare
                // @param date2 ( optional ) If the value is not set, the date 1 will be compare with now
                // @param unit ( optional ). Default: "days"
                getDiff: function (date1, date2, unit) {

                    var d1 = date1, d2 = date2, u = unit;

                    if (typeof unit === "undefined") {

                        d1 = moment();
                        d2 = date1;
                        u = date2;
                    }

                    var d2 = moment(d2, app.DATE_FORMAT);

                    var diff = d1.diff(d2, u || "days");

                    return diff;
                },

                // Format date based on the difference between 2 days
                formatDateBasedOnDiff: function (date1, date2, lang, timeZone) {
                    var d1 = date1, d2 = date2;
                    if (typeof date2 === "undefined" || date2 === "") {

                        d1 = moment();
                        d2 = this.getMoment(date1);
                    }

                    if (d1.isSame(d2, "day")) {
                        return this.formatDate(date1, "LTS", lang, timeZone);
                    }

                    return this.formatDate(date1, "MM/DD/YYYY HH:mm:ss", lang, timeZone);
                },

                // Default is using calendar format
                // Convert from calendar date to bookend date
                formatBookendDate: function (date, timezone, format) {

                    var d = moment(date, format || app.CALENDAR_FORMAT);
                    timezone && d.subtract(timezone, 'hours');

                    return d.format(app.BOOKEND_DATE_FORMAT);
                },

                // Check whether date is valid or not
                isValidDate: function (date, format) {

                    return moment(date, format).isValid();
                },

                // Check whether a list contains a value or not
                contains: function (list, value) {

                    return _.contains(list, value);
                },

                // Convert HEX for input including /n
                // Example: /u => <script>
                convertHexToString: function (text) {

                    var r = /\\u([\d\w]{4})/gi;

                    var t = text.replace(r, function (match, grp) {

                        return String.fromCharCode(parseInt(grp, 16));
                    });

                    return t;
                },

                // Convert then escape HEX then replace /n with <br />
                escape: function (text) {

                    return _.escape(text).replace(/\r?\n/g, '<br />');
                },

                // Should support multiple module
                // @param {Array/String} module
                // @param {Boolean} isFile if file is true, module is array of file then load file
                loadModule: function (module, isFile) {

                    if (isFile)
                        return $ocLazyLoad.load(module);

                    var modules = [];

                    if (angular.isArray(module)) {

                        angular.forEach(module, function (v) {

                            modules.push($ocLazyLoad.getModuleConfig(v));
                        });
                    } else
                        modules = module;

                    return $ocLazyLoad.load(modules);
                },

                // Create an http call
                createRequest: function (url, param, callback, error, isPusher) {
                    var link = url, method = "POST", params = param, cb = callback, err = error;

                    if (angular.isObject(url)) {

                        method = url.method || "POST";
                        link = url.name || url.url;
                    }

                    // No data posted
                    if (angular.isFunction(param)) {

                        cb = param;
                        err = callback;
                        params = {};
                    }

                    var opts = {
                        url: configServer.BACKEND_API_URL + link,
                        method: method
                    };

                    if (url.headers) {
                        opts.headers = url.headers;
                    }

                    // Shorthand
                    (method === "GET" || method === "DELETE") ? (opts.params = params) : (opts.data = params);

                    return $http(opts).success(function (data) {
                        var code;

                        if (data) {
                            code = data.status;
                        }

                        if (code === 200) {

                            cb && cb(data);
                        } else {
                            err && err(data);
                        }

                    }).error(function (error) {
                        // Handle error
                        err && err(error);
                    });
                },

                changePaymentPackpageLang: function (param) {
                    var paymentPackage = [];

                    for (var i = 0; i < param.length; i++) {
                        var planPayment = {
                            "payment_id": "",
                            "description": "",
                            "price": ""
                        };
                        planPayment.payment_id = param[i].payment_id;
                        planPayment.price = param[i].price;
                        switch (param[i].payment_id) {
                            case 1:
                                planPayment.description = this.translate('label.payment_package.plan1');
                                break;
                            case 2:
                                planPayment.description = this.translate('label.payment_package.plan2');
                                break;
                            case 3:
                                planPayment.description = this.translate('label.payment_package.plan3');
                                break;
                            case 4:
                                planPayment.description = this.translate('label.payment_package.plan4');
                                break;
                            case 5:
                                planPayment.description = this.translate('label.payment_package.plan5');
                                break;
                            case 6:
                                planPayment.description = this.translate('label.payment_package.plan6');
                                break;
                            default:
                                break;
                        }
                        paymentPackage.push(planPayment);
                    }
                    return paymentPackage;
                },

                convertBytesToGB: function (value) {
                    return value / 1024 / 1024 / 1024;
                },

                convertGBToBytes: function (value) {
                    return value * 1024 * 1024 * 1024;
                }
            };
        }])

    // Util
    // This utility is copied from v1.5
    .factory('AppUtil', [
        '$', '$http', 'noty', '$log', "BootstrapDialog", "userConfig", 'util', "configServer", "$state",
        function ($, $http, noty, $log, BootstrapDialog, userConfig, util, configServer, $state) {

            // Show error
            function showError(opts) {
                var defaults = {

                    text: "There's an error occur",
                    type: 'error',
                    theme: 'bootstrapTheme',
                    layout: 'topCenter',
                    // closeWith: ['button'],
                    timeout: 5000 // 5s
                };

                // Accept content as text
                if (opts) {

                    defaults.text = opts.content || opts.msg || opts.text;
                }

                return noty($.extend({}, defaults, opts));
            }

            function translate(path, opts) {

                return $i18next(path, opts);
            }

            // Create an http call
            function createRequest(url, param, callback, error) {

                var link = url, method = "POST", params = param, cb = callback, err = error;

                if (angular.isObject(url)) {

                    method = url.method || "POST";
                    link = url.name || url.url;
                }

                // No data posted
                if (angular.isFunction(param)) {

                    cb = param;
                    err = callback;
                    params = {};
                }

                var opts = {
                    url: configServer.BACKEND_API_URL + link,
                    method: method
                };

                // Shorthand
                (method === "GET" || method === 'DELETE') ? (opts.params = params) : (opts.data = params);

                return $http(opts).success(function (data) {
                    var code;

                    if (data) {
                        code = data.status;
                    }

                    if (code === 200) {
                        cb && cb(data);
                    } else {
                        if (data.status === 401) {
                            $state.go("adminLogin");
                        }
                        err && err(data);
                    }

                }).error(function (error) {
                    if (error.status === 401) {
                        $state.go("adminLogin");
                    }
                    // Write log
                    err && err(data);
                    $log.log(error);
                });
            }

            return {

                showError: showError,
                createRequest: createRequest,
                translate: translate,

                // Pretty size
                convertFileSize: function (bytes) {
                    var thresh = 1024;
                    if (bytes < thresh)
                        return bytes + ' B';
                    var units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
                    var u = -1;
                    do {
                        bytes /= thresh;
                        ++u;
                    } while (bytes >= thresh);
                    return bytes.toFixed(1) + ' ' + units[u];
                },

                // Get extension based on its name
                getFileExtension: function (fileName) {

                    var extension = fileName.substr((fileName.lastIndexOf('.') + 1));
                    return extension.toLowerCase();
                },

                // Create a toast information
                // http://ned.im/noty/#options
                createToast: function (opts) {

                    var defaults = {
                        text: '',
                        type: 'information',
                        theme: 'bootstrapTheme',
                        layout: 'topCenter',
                        timeout: 5000 // 5s
                    };

                    return noty($.extend({}, defaults, opts));
                },

                createToastTimeOut: function (opts, timeout) {

                    var defaults = {
                        text: '',
                        type: 'information',
                        theme: 'bootstrapTheme',
                        layout: 'topCenter',
                        timeout: timeout // 5s
                    };

                    return noty($.extend({}, defaults, opts));
                },

                //Show success
                showSuccess: function (opts) {

                    var defaults = {
                        text: '',
                        type: 'success',
                        theme: 'bootstrapTheme',
                        layout: 'topCenter',
                        timeout: 5000 // 5s
                    };

                    return noty($.extend({}, defaults, opts));
                },

                showErrorTimeOut: function (opts, timeout) {
                    var defaults = {

                        text: "There's an error occur",
                        type: 'error',
                        theme: 'bootstrapTheme',
                        layout: 'topCenter',
                        // closeWith: ['button'],
                        timeout: timeout // 5s
                    };

                    // Accept content as text
                    if (opts) {

                        if (opts.content)
                            defaults.text = opts.content;
                    }

                    return noty($.extend({}, defaults, opts));

                },

                // Create toast notification with much more option
                closeToast: function (obj) {

                    if (obj) {
                        obj.closeCleanUp();
                    } else {
                        // Close all
                        $.noty.closeAll();
                    }
                },

                // Modal confirm
                confirm: function (opts) {

                    return BootstrapDialog.show({
                        title: opts.title,
                        message: function () {
                            return opts.message;
                        },
                        buttons: opts.buttons,
                        // animate: false, // Disable animation
                        draggable: false,
                        closable: (typeof opts.closable !== 'undefined') ? opts.closable : true,
                        onshown: function (m) {

                            if (typeof opts.onshown === 'function')
                                opts.onshown(m);

                        },
                        onhidden: function (m) {

                            // $( 'html' ).toggleClass('no-scroll');

                        }
                    });
                },

                confirm_model_2: function (opts) {

                    var modal = new BootstrapDialog.show({
                        type: opts.type,
                        title: opts.title,
                        closeByKeyboard: false,
                        buttons: opts.buttons,
                        message: function () {
                            return opts.message;
                        },
                        // animate: false, // Disable animation
                        draggable: false,
                        closable: (typeof opts.closable !== 'undefined') ? opts.closable : true,
                        onshown: function (m) {

                            if (typeof opts.onshown === 'function')
                                opts.onshown(m);

                        },
                        onhidden: function (m) {

                            // $( 'html' ).toggleClass('no-scroll');

                        }
                    });
                    modal.getModalFooter().css('text-align', 'center');
                    return modal;
                },

                // Check whether contains speicial character or not
                // I think we should use REXP to do this
                isContainSpecialChar: function (value, sChars) {

                    var check = false;

                    for (var i in sChars) {
                        if (value.indexOf(sChars[i]) > -1) {
                            check = true;
                            break;
                        }
                    }
                    return check;
                },

                /**
                 * Message-Digest algorithm 5 (MD5)
                 * @param {type} string
                 */
                MD5: function (string) {
                    function RotateLeft(lValue, iShiftBits) {
                        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
                    }

                    function AddUnsigned(lX, lY) {
                        var lX4, lY4, lX8, lY8, lResult;
                        lX8 = (lX & 0x80000000);
                        lY8 = (lY & 0x80000000);
                        lX4 = (lX & 0x40000000);
                        lY4 = (lY & 0x40000000);
                        lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
                        if (lX4 & lY4) {
                            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
                        }
                        if (lX4 | lY4) {
                            if (lResult & 0x40000000) {
                                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
                            } else {
                                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
                            }
                        } else {
                            return (lResult ^ lX8 ^ lY8);
                        }
                    }

                    function F(x, y, z) {
                        return (x & y) | ((~x) & z);
                    }

                    function G(x, y, z) {
                        return (x & z) | (y & (~z));
                    }

                    function H(x, y, z) {
                        return (x ^ y ^ z);
                    }

                    function I(x, y, z) {
                        return (y ^ (x | (~z)));
                    }

                    function FF(a, b, c, d, x, s, ac) {
                        a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
                        return AddUnsigned(RotateLeft(a, s), b);
                    }

                    function GG(a, b, c, d, x, s, ac) {
                        a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
                        return AddUnsigned(RotateLeft(a, s), b);
                    }

                    function HH(a, b, c, d, x, s, ac) {
                        a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
                        return AddUnsigned(RotateLeft(a, s), b);
                    }

                    function II(a, b, c, d, x, s, ac) {
                        a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
                        return AddUnsigned(RotateLeft(a, s), b);
                    }

                    function ConvertToWordArray(string) {
                        var lWordCount;
                        var lMessageLength = string.length;
                        var lNumberOfWords_temp1 = lMessageLength + 8;
                        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
                        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
                        var lWordArray = Array(lNumberOfWords - 1);
                        var lBytePosition = 0;
                        var lByteCount = 0;
                        while (lByteCount < lMessageLength) {
                            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
                            lBytePosition = (lByteCount % 4) * 8;
                            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
                            lByteCount++;
                        }
                        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
                        lBytePosition = (lByteCount % 4) * 8;
                        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
                        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
                        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
                        return lWordArray;
                    }

                    function WordToHex(lValue) {
                        var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
                        for (lCount = 0; lCount <= 3; lCount++) {
                            lByte = (lValue >>> (lCount * 8)) & 255;
                            WordToHexValue_temp = "0" + lByte.toString(16);
                            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
                        }
                        return WordToHexValue;
                    }

                    function Utf8Encode(string) {
                        string = string.replace(/\r\n/g, "\n");
                        var utftext = "";

                        for (var n = 0; n < string.length; n++) {

                            var c = string.charCodeAt(n);

                            if (c < 128) {
                                utftext += String.fromCharCode(c);
                            } else if ((c > 127) && (c < 2048)) {
                                utftext += String.fromCharCode((c >> 6) | 192);
                                utftext += String.fromCharCode((c & 63) | 128);
                            } else {
                                utftext += String.fromCharCode((c >> 12) | 224);
                                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                                utftext += String.fromCharCode((c & 63) | 128);
                            }

                        }

                        return utftext;
                    }

                    var x = Array();
                    var k, AA, BB, CC, DD, a, b, c, d;
                    var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
                    var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
                    var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
                    var S41 = 6, S42 = 10, S43 = 15, S44 = 21;

                    string = Utf8Encode(string);

                    x = ConvertToWordArray(string);

                    a = 0x67452301;
                    b = 0xEFCDAB89;
                    c = 0x98BADCFE;
                    d = 0x10325476;

                    for (k = 0; k < x.length; k += 16) {
                        AA = a;
                        BB = b;
                        CC = c;
                        DD = d;
                        a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
                        d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
                        c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
                        b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
                        a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
                        d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
                        c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
                        b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
                        a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
                        d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
                        c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
                        b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
                        a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
                        d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
                        c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
                        b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
                        a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
                        d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
                        c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
                        b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
                        a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
                        d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
                        c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
                        b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
                        a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
                        d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
                        c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
                        b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
                        a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
                        d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
                        c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
                        b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
                        a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
                        d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
                        c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
                        b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
                        a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
                        d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
                        c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
                        b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
                        a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
                        d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
                        c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
                        b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
                        a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
                        d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
                        c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
                        b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
                        a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
                        d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
                        c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
                        b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
                        a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
                        d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
                        c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
                        b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
                        a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
                        d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
                        c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
                        b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
                        a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
                        d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
                        c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
                        b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
                        a = AddUnsigned(a, AA);
                        b = AddUnsigned(b, BB);
                        c = AddUnsigned(c, CC);
                        d = AddUnsigned(d, DD);
                    }

                    var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);

                    return temp.toLowerCase();
                },

                // Pattern match email
                // patternEmail: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,

                // patternEmail: /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/,
                patternEmail: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/,
                //  patternEmail: /^[a-z][a-z0-9_\.]{2,32}@[a-z0-9]{2,}(\.[a-z]{2,4}){1,2}$/,
                // Check valid email base on regrex in validationEngine
                isValidEmail: function (email) {

                    return this.patternEmail.test(email);
                },

                patternDomain: /(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/,
                isValidDomain: function (domain) {

                    return this.patternDomain.test(domain);
                },
                // Check valid list email string which is separated by comma (,)
                isValidGroupEmail: function (gEmail) {

                    var array = gEmail.split(',');
                    for (var i = 0; i < array.length; i++) {
                        if (this.patternEmail.test($.trim(array[i])) === false) {
                            return false;
                        }
                    }
                    return true;
                },


                // Transform JSON file to File object
                transformObject: function (jsonResult, constructor) {

                    if (!constructor)
                        return jsonResult;

                    var model = new constructor();
                    angular.extend(model, jsonResult);
                    return model;
                },

                // Transform from JSON file to list File or File object
                transformResult: function (jsonResult, constructor) {
                    var $this = this;
                    if (angular.isArray(jsonResult)) {
                        var models = [];
                        angular.forEach(jsonResult, function (object) {
                            models.push($this.transformObject(object, constructor));
                        });
                        return models;
                    } else {
                        return $this.transformObject(jsonResult, constructor);
                    }
                },
                getLocation: function (currentUser) {
                    var location;
                    if (angular.isObject(currentUser)) {
                        switch (currentUser.role_id) {
                            case userConfig.SUPER_ADMIN.role:
                                location = userConfig.SUPER_ADMIN.landing_page_state;
                                break;
                            case userConfig.COMPANY_ADMIN.role:
                                location = userConfig.COMPANY_ADMIN.landing_page_state;
                                break;
                            case userConfig.ADMIN.role:
                                location = userConfig.ADMIN.landing_page_state;
                                break;
                            case userConfig.MANAGER.role:
                                location = userConfig.MANAGER.landing_page_state;
                                break;
                            case userConfig.MEMBER.role:
                                location = userConfig.MEMBER.landing_page_state;
                                break;
                            default:
                                location = userConfig.GUEST.landing_page_state;
                                break;
                        }
                    } else {
                        location = 'login';
                    }

                    return location;
                },

                convertSecondToTime: function (secondInput) {
                    var seconds = parseInt(secondInput, 10);

                    var hrs = Math.floor(seconds / 3600);
                    seconds -= hrs * 3600;
                    var mnts = Math.floor(seconds / 60);
                    seconds -= mnts * 60;

                    var strHrs = (hrs > 0) ? ((hrs > 9) ? hrs : "0" + hrs) + ":" : "";
                    var strMin = ((mnts > 9) ? mnts : "0" + mnts) + ":";
                    var strSec = (seconds > 9) ? seconds : "0" + seconds;

                    return strHrs + strMin + strSec;
                },

                getPasswordPolicyError: function (passwordSetting) {
                    var passwordError = util.translate("errors.password_policy.default", {
                        min: passwordSetting.min,
                        max: passwordSetting.max
                    });
                    if (passwordSetting.capital !== 0 || passwordSetting.lower !== 0
                        || passwordSetting.number !== 0 || passwordSetting.special !== 0) {
                        passwordError += util.translate("errors.password_policy.include");

                        if (passwordSetting.capital !== 0) {
                            passwordError += util.translate("errors.password_policy.capital", {
                                number: passwordSetting.capital
                            }) + ",";
                        }
                        if (passwordSetting.lower !== 0) {
                            passwordError += util.translate("errors.password_policy.lower", {
                                number: passwordSetting.lower
                            }) + ",";
                        }
                        if (passwordSetting.number !== 0) {
                            passwordError += util.translate("errors.password_policy.number", {
                                number: passwordSetting.number
                            }) + ",";
                        }
                        if (passwordSetting.special !== 0) {
                            passwordError += util.translate("errors.password_policy.special", {
                                number: passwordSetting.special
                            }) + ",";
                        }
                        passwordError = passwordError.slice(0, passwordError.length - 1);
                        passwordError += util.translate("errors.password_policy.characters");
                    }

                    return passwordError;
                },

            };

        }])

    // Using for file util
    .factory('fileutil', ['MimeTypes', 'MimeTypeDetail', "_", function (MimeTypes, MimeTypeDetail, _) {

        return {

            // Check a file is a specific type
            isFile: function (file, type) {

                var contentType = file.content_type || file.mine_type;

                switch (type) {

                    case "pdf":
                        return MimeTypeDetail.PDF.type === contentType;

                    case "word":
                        return MimeTypeDetail.WORD.type === contentType;

                    case "pptx":
                        return MimeTypeDetail.PPTX.type === contentType;

                    case "excel":
                        return MimeTypeDetail.EXCEL.type === contentType;

                    default:
                        return false;
                }
            },

            // Get group from mime type
            getGroupFromMimeType: function (file) {
                if (file === undefined)
                    return null;

                var group = null;

                switch (file.file_type) {

                    case 1:
                    case 2:
                    case 3:
                    case 4:

                        group = 'secure';

                        break;

                    default:
                        if (file.content_type === undefined || file.content_type === null)
                            return null;
                        // Not in secure file
                        _.forIn(MimeTypes, function (value, key) {

                            if (group === null) {

                                var list = value.types || value;

                                for (var i in list) {

                                    if (file.content_type === list[i]) {

                                        group = key;

                                        break;
                                    }
                                }
                            }
                        });

                        break;
                }
                return group;
            },

            // It's incorrect
            getIconFromMimeType: function (mimeType) {

                var src;

                // Lookup in group
                _.forIn(MimeTypes, function (value, key) {

                    var list = value.types;

                    if (src === undefined) {

                        for (var i in list) {

                            if (list[i] === mimeType) {

                                src = value.icon;
                                break;
                            }
                        }
                    }
                });

                var details = [];

                _.forIn(MimeTypeDetail, function (v) {

                    details.push(v);
                });

                // Then lookup in detail
                for (var i in details) {

                    var obj = details[i];

                    if (obj.type === mimeType) {

                        src = obj.icon;
                        break;
                    }
                }
                return src || MimeTypes["default"].icon;
            }
        };
    }])

    // Checkbox factory
    // Provide checkbox service
    .factory('checkbox', ['_', '$', function (_, $) {

        var array = [];

        return {

            // This function should be put in #drawCallback of datatable
            // I use this way to increase performance instead using directive
            // @param {string} wrapper selector wrapper these checkbox
            // @param {function} callback : the callback after item has been checked
            bindCheck: function (wrapper, callback) {
                // Get checkbox
                var parent = $(wrapper).find('input:checkbox[role=parent-checkbox]'),
                    children = $(wrapper).find('input:checkbox[role=child-checkbox]');

                // Render first
                var isAll = true;

                _.forEach(children, function (child) {

                    if (_.contains(array, child.id)) {

                        child.checked = true;
                    } else
                        isAll = false;
                });

                // Enable check all
                if (isAll && children.length > 0) {
                    parent.prop('checked', true);
                    // add check class
                    parent.parent().addClass("checked");
                } else {
                    parent.prop('checked', false);
                    // remove check classs
                    parent.parent().removeClass("checked");
                }

                // Find button check-all
                // Prevent mutiple bind click
                parent.unbind('change').bind('change', function () {

                    if (this.checked) {

                        // Add all
                        _.forEach(children, function (child) {

                            if (!child.checked) {

                                if (!_.contains(array, child.id))
                                    array.push(child.id);
                            }
                        });
                    } else {
                        // Remove all
                        _.forEach(children, function (child) {

                            if (child.checked) {

                                if (_.contains(array, child.id))
                                    _.remove(array, function (item) {

                                        return item === child.id;
                                    });
                            }
                        });
                    }

                    // Check/uncheck children
                    (this.checked) ? children.prop('checked', true) : children.prop('checked', false);
                    children.trigger('tblUpdateCheckbox'); // directive icheckTable update icheck state

                    // Invoke callback
                    callback && callback(array);
                });

                // Find child button
                children.unbind('change').bind('change', function () {

                    var that = this, id = that.id;

                    // Check
                    if (that.checked) {

                        if (!_.contains(array, id))
                            array.push(id);
                    } else {
                        // Remove
                        _.remove(array, function (item) {

                            return item === id;
                        });
                    }

                    // Length of item is not checked
                    var l = children.filter(function () {

                        return this.checked === false;

                    }).length;

                    // Enable check all
                    (l > 0) ? parent.prop('checked', false) : parent.prop('checked', true);
                    parent.trigger('tblUpdateCheckbox'); // directive icheckTable update icheck state

                    // Invoke callback
                    callback && callback(array);
                });
            },

            // Get/set array
            list: function (value) {

                if (value) {

                    array = value;
                }
                return array;
            },

            // Cleanup
            cleanUp: function () {
                array = [];
            }
        };
    }])

    .factory('ClientInfo', ['$window', function ($window) {

        return {
            getOS: function (params) {

                //  navigator
                var nav = $window.navigator;

                //	query param
                if (params == undefined) {
                    params = {};
                }
                var pack_ios = params.pack_ios ? true : false;
                if (params.navigator != undefined) {
                    nav = params.navigator;
                }

                //	Check OS
                if (nav.platform.indexOf('Win') != -1) {
                    return "Windows";
                } else if (nav.platform.indexOf('Mac') != -1) {
                    return "Mac";
                } else if (nav.platform.indexOf('iPhone') != -1 || nav.appVersion.indexOf('iPhone') != -1) {
                    return pack_ios ? "iOS" : "iPhone";
                } else if (nav.platform.indexOf('iPad') != -1 || nav.appVersion.indexOf('iPad') != -1) {
                    return pack_ios ? "iOS" : "iPad";
                } else if (nav.platform.indexOf('iPod') != -1 || nav.appVersion.indexOf('iPod') != -1) {
                    return pack_ios ? "iOS" : "iPod";
                } else if (nav.appVersion.indexOf('Android') != -1) {
                    return "Android";
                }
                return "Other";
            },
            getOSVersion: function (params) {
                //  navigator
                var nav = $window.navigator;

                var version = "";
                if (params === undefined) {
                    params = {};
                }
                var unique_name = params.unique_name ? true : false;
                if (params.navigator !== undefined) {
                    nav = params.navigator;
                }

                //  version
                var _ver = null;
                switch (this.getOS(params)) {
                    //  Windows
                    case "Windows":
                        _ver = nav.oscpu ?
                            nav.oscpu.match(/Windows NT ((\d+\.?)+)/) : //  Firefox
                            nav.appVersion.match(/Windows NT ((\d+\.?)+)/);  //  IE, Safari, Chrome, Opera
                        if (_ver) {
                            version = _ver[1];
                        }
                        //
                        if (unique_name) {
                            switch (version) {
                                case "6.3":
                                    version = "8.1";
                                    break;
                                case "6.2":
                                    version = "8";
                                    break;
                                case "6.1":
                                    version = "7";
                                    break;
                                case "6.0":
                                    version = "Vista";
                                    break;
                                case "5.1":
                                    version = "XP";
                                    break;
                                case "5.0":
                                    version = "2000";
                                    break;
                            }
                        }
                        break;
                    //  Mac
                    case "Mac":
                        if (window.opera) {
                            _ver = nav.appVersion.match(/Intel Mac OS X ((\d+\.?)+)/);  //  Opera
                        } else {
                            _ver = nav.oscpu ?
                                nav.oscpu.match(/Intel Mac OS X ((\d+\.?)+)/) : //  Firefox
                                nav.appVersion.match(/Intel Mac OS X ((\d+_?)+)/);  //  Safari, Chrome
                        }
                        if (_ver) {
                            version = _ver[1].replace(/_/g, ".");
                        }
                        break;
                    //  Android
                    case "Android":
                        _ver = nav.appVersion.match(/Android ((\d+\.?)+)/);
                        if (_ver) {
                            version = _ver[1];
                        }
                        break;
                    //  iOS
                    case "iPhone":
                    case "iPod":
                        _ver = nav.appVersion.match(/iPhone OS ((\d+_?)+)/);
                        if (_ver) {
                            version = _ver[1].replace(/_/g, ".");
                        }
                        break;
                    case "iPad":
                        _ver = nav.appVersion.match(/OS ((\d+_?)+)/);
                        if (_ver) {
                            version = _ver[1].replace(/_/g, ".");
                        }
                        break;
                }
                return version;
            },
            getBrowser: function (params) {

                //  navigator
                var nav = $window.navigator;

                //
                if (params == undefined) {
                    params = {};
                }
                if (params.navigator != undefined) {
                    nav = params.navigator;
                }

                //  "appName" include "Internet Explorer"
                if (nav.appName.indexOf("Internet Explorer") != -1 || nav.appVersion.match(/rv:((\d+\.?)+)/) != null) {
                    if (nav.cpuClass == "x64") {
                        return "IE64";
                    }
                    return "IE";
                }
                //  "appName" include "Opera"
                else if (nav.appName.indexOf("Opera") != -1) {
                    if (nav.appVersion.indexOf("Opera Mini") != -1) {
                        return "Opera Mini";
                    } else if (nav.appVersion.indexOf("Opera Mobi") != -1) {
                        return "Opera Mobile";
                    } else {
                        return "Opera";
                    }
                }
                //  "appName" include "Netscape"
                else if (nav.appName == "Netscape") {
                    //  for minor browsers
                    //  *Thease browser check first bcause many browsers have same UserAgent for Chrome, Firefox, Safari.
                    if (nav.appVersion.indexOf("iLunascape") != -1) {
                        return "iLunascape";
                    } else if (nav.appVersion.indexOf("Mercury") != -1) {
                        return "Mercury";
                    }
                        //  Google Chrome
                    //  *The string "CriOS" used on the iOS.
                    else if (nav.appVersion.indexOf("Chrome") != -1 || nav.appVersion.indexOf("CriOS") != -1) {
                        return "Chrome";
                    }
                    //  Firefox
                    else if (nav.userAgent.indexOf("Firefox") != -1) {
                        return "Firefox";
                    }
                        //  Safari
                    //  *Most browsers matched this.
                    else if (nav.appVersion.indexOf("Safari") != -1) {
                        return "Safari";
                    }
                    //  旧バージョンの beobj.js と互換性を取るための判定
                    else if (nav.vendor.indexOf("Apple") != -1) {
                        return "Safari";
                    }
                }
                return "Other";
            },
            getBrowserVersion: function (params) {
                //  navigator
                var nav = $window.navigator;

                //
                if (params === undefined) {
                    params = {};
                }
                if (params.navigator != undefined) {
                    nav = params.navigator;
                }

                var version = "";
                switch (this.getBrowser({
                    navigator: nav
                })) {
                    case "IE":
                    case "IE64":
                        var _ver = nav.appVersion.match(/MSIE ((\d+\.?)+)/);  //  IE10
                        if (_ver == null) {
                            _ver = nav.appVersion.match(/rv:((\d+\.?)+)/);
                        }  //  IE11
                        if (_ver) {
                            version = _ver[1];
                        }
                        break;
                    case "Opera":
                        var _ver = nav.userAgent.match(/Version\/((\d+\.?)+)/);
                        if (_ver) {
                            version = _ver[1];
                        }
                        break;
                    case "Safari":
                        var _ver = nav.appVersion.match(/Version\/((\d+\.?)+)/);
                        if (_ver) {
                            version = _ver[1];
                        }
                        break;
                    case "Chrome":
                        var _ver = nav.appVersion.match(/Chrome\/((\d+\.?)+)/);
                        if (_ver) {
                            version = _ver[1];
                        }
                        break;
                    case "Firefox":
                        var _ver = nav.userAgent.match(/Firefox\/((\d+\.?)+)/);
                        if (_ver) {
                            version = _ver[1];
                        }
                        break;
                }
                return version;
            },
            getAdobeFlashPlayerVersion: function () {
                var ver = "";
                $window.navigator.plugins.refresh(false);
                if (this.getBrowser() === "IE") {
                    try {
                        var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                        ver = axo.GetVariable("$version").split(" ")[1].split(",").join(".");
                    } catch (e) {
                    }
                } else {
                    $window.navigator.plugins.refresh(false);
                    var x = $window.navigator.plugins["Shockwave Flash"];
                    if (x && x.description) {
                        ver = x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".");
                    }
                }
                return ver;
            },
            checkFlashPlayerInstall: function () {
                var flashPlayerVer = this.getAdobeFlashPlayerVersion();
                if (flashPlayerVer.length === 0) {
                    return false;
                } else
                    return true;
            }
        };

    }])

    .factory('logo', [function () {
        // Setting support logo
        var settings = {
            "app.689cloud.com": {
                LARGE: "img/689Cloud_large.png",
                SMALL: "img/689Cloud_small.png",
                TITLE: "689Cloud"
            },
            "localhost": {
                LARGE: "img/689Cloud_large.png",
                SMALL: "img/689Cloud_small.png",
                TITLE: "689Cloud"
            },
            "default": {
                LARGE: "img/689Cloud_large.png",
                SMALL: "img/689Cloud_small.png",
                TITLE: "689Cloud"
            }
        };
        // Get current domain
        var domain = window.location.hostname;
        var logoObj = settings[domain];
        if (logoObj)
            return logoObj;
        else
            return settings['default'];
    }]);
