(function () {

    var _timeout = 10000;

    function uuid(hyphenation) {
        var array = [];
        var hex = "0123456789abcdef";
        for (var i = 0; i < 36; i++) array[i] = hex.substr(Math.floor(Math.random() * 16), 1);
        array[14] = "4";
        array[19] = hex.substr((array[19] & 0x3) | 0x8, 1);
        array[8] = array[13] = array[18] = array[23] = "-";
        var result = array.join("");
        if (hyphenation !== true) result = result.replace(/-/gm, "");
        return result;
    }

    function getLocalStorage(_key) {
        var value = localStorage.getItem(_key);
        if (typeof value == "string") {
            if (value.length < 1) {
                localStorage.removeItem(_key);
                value = null;
            }
        }
        return value;
    }

    function setLocalStorage(_key, _value) {
        var value = _value;
        if (typeof value == "string") {
            if (value.length < 1) value = null;
        } else value = null;
        if (value == null) {
            var current = localStorage.getItem(_key);
            if (typeof current == "string") localStorage.removeItem(_key);
        } else localStorage.setItem(_key, value);
    }

    function getSession() {
        var session = getLocalStorage("Account.Session");
        if (session && typeof session == "string") return session;
        session = uuid(false);
        setLocalStorage("Account.Session", session);
        return session;
    }

    function getTicket() {
        return getLocalStorage("Account.Ticket");
    }

    function setTicket(value) {
        return setLocalStorage("Account.Ticket", value);
    }

    function getUrl() {
        return "http://127.0.0.1:53294/";
    }

    function invoke(_function, _json) {
        if (typeof window.showLoading == "function") showLoading(false);
        if (_json) {
            if (_json.status == "ok") // console.info("api", _json.status, _json);
                if (_json.status != "ok") {
                    // console.error("api", _json.status, _json.message, _json, _json.xhr);
                    // if (typeof toast == "function") toast(_json.message, 3000);
                }
        }
        if (typeof _function == "function") _function(_json);
    }

    function _get(_application, _function, _data, _succeed, _failed) {
        if (typeof window.showLoading == "function") showLoading(true);
        var url = getUrl();
        url += encodeURIComponent(_application);
        url += "/";
        url += encodeURIComponent(_function);
        if (_data) {
            for (var i in _data) {
                url = url + "&";
                url = url + encodeURIComponent(i);
                url = url + "=";
                url = url + encodeURIComponent(_data[i]);
            }
        }
        var ticket = getTicket();
        if (ticket) url += "&ticket=" + ticket;

        if (window.XDomainRequest) {
            var xdr = new XDomainRequest();
            xdr.onerror = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            };
            xdr.ontimeout = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Timeout", xdr: xdr });
            };
            xdr.onprogress = function () { };
            xdr.onload = function () {
                var response = xdr.responseText;
                var json = null;
                try { json = JSON.parse(response); } catch (ex) { }
                if (json) return invoke(json.status == "ok" ? _succeed : _failed, json);
                invoke(_failed, { status: "text", message: "Invalid Response", text: response, xhr: xhr });
            };
            xdr.timeout = _timeout;
            try {
                xdr.open("get", url);
                xdr.send();
            } catch (ex) {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            }
            return xdr;
        }

        var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open("GET", url, true);
        xhr.timeout = _timeout;
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if ((xhr.status == 200) || (xhr.status == 304)) {
                    var response = xhr.responseText;
                    var json = null;
                    try { json = JSON.parse(response); } catch (ex) { }
                    if (json) return invoke(json.status == "ok" ? _succeed : _failed, json);
                    invoke(_failed, { status: "text", message: "Invalid Response", text: response, xhr: xhr });
                } else {
                    invoke(_failed, { status: "error", message: "Invalid Status " + String(xhr.status), xhr: xhr });
                }
            }
        };
        try {
            xhr.send();
        } catch (ex) {
            invoke(_failed, { status: "error", message: ex.message, xhr: xhr });
        }
        return xhr;
    }

    function _post(_application, _function, _data, _succeed, _failed) {
        if (typeof window.showLoading == "function") showLoading(true);
        var content = {
            "application": _application,
            "function": _function,
            "ticket": getTicket(),
            "page": window.location.href,
            "session": getSession(),
            "data": _data
        };
        var url = getUrl();
        if (url && _application) {
            if (url.substring(url.length - 1, url.length) != "/") url += "/";
            url += String(_application);
            if (_function) url += "/" + String(_function);
        }

        if (window.XDomainRequest) {
            var xdr = new XDomainRequest();
            xdr.onerror = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            };
            xdr.ontimeout = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Timeout", xdr: xdr });
            };
            xdr.onprogress = function () { };
            xdr.onload = function () {
                var response = xdr.responseText;
                var json = null;
                try { json = JSON.parse(response); } catch (ex) { }
                if (json) return invoke(json.status == "ok" ? _succeed : _failed, json);
                invoke(_failed, { status: "text", message: "Invalid Response", text: response, xhr: xhr });
            };
            xdr.timeout = _timeout;
            try {
                xdr.open("post", url);
                xdr.send(JSON.stringify(content));
            } catch (ex) {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            }
            return xdr;
        }

        var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open("POST", url, true);
        xhr.timeout = _timeout;
        xhr.setRequestHeader("Content-Type", "text/plain");
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if ((xhr.status == 200) || (xhr.status == 304)) {
                    var response = xhr.responseText;
                    var json = null;
                    try { json = JSON.parse(response); } catch (ex) { }
                    if (json) return invoke(json.status == "ok" ? _succeed : _failed, json);
                    invoke(_failed, { status: "text", message: "Invalid Response", text: response, xhr: xhr });
                } else {
                    invoke(_failed, { status: "error", message: "Invalid Status " + String(xhr.status), xhr: xhr });
                };
            };
        };
        try {
            xhr.send(JSON.stringify(content));
        } catch (ex) {
            invoke(_failed, { status: "error", message: ex.message, xhr: xhr });
        }
        return xhr;
    }

    window.mesKeeper = {
        get: function (_application, _function, _data, _succeed, _failed) {
            return _get(_application, _function, _data, _succeed, _failed);
        },
        post: function (_application, _function, _data, _succeed, _failed) {
            return _post(_application, _function, _data, _succeed, _failed);
        },
        tcpAgent: {
            pool: function (succeed, failed) { return _post("TcpAgent", "Pool", null, succeed, failed); },
            add: function (ip, port, succeed, failed) { return _post("TcpAgent", "Add", { ip: ip, port: port, await: false }, succeed, failed); },
            get: function (ip, port, succeed, failed) { return _post("TcpAgent", "Get", { ip: ip, port: port }, succeed, failed); },
            close: function (ip, port, succeed, failed) { return _post("TcpAgent", "Close", { ip: ip, port: port }, succeed, failed); },
            send: function (ip, port, content, succeed, failed) {
                switch (typeof content) {
                    case "string": return _post("TcpAgent", "Send", { ip: ip, port: port, text: content }, succeed, failed);
                    case "number": return _post("TcpAgent", "Send", { ip: ip, port: port, text: String(content) }, succeed, failed);
                    case "object":
                        if (content) {
                            return _post("TcpAgent", "Send", { ip: ip, port: port, text: String(content) }, succeed, failed);
                        } else {
                            if (typeof failed == "function") failed({ status: "error", message: "未指定有效的发送内容。" });
                        }
                        return;
                    default:
                        if (typeof failed == "function") failed({ status: "error", message: "指定了不支持的发送内容。" });
                        return;
                }
            },
            receive: function (succeed, failed) {
                return _post("TcpAgent", "Receive", null, succeed, failed);
            },
            clear: function (succeed, failed) {
                return _post("TcpAgent", "Clear", null, succeed, failed);
            }
        }
    };

})();

(function () {

    // 初始化旧版打印狗。
    function initPrintDog() {
        var wsClient = null;

        function wsInit() {
            var provider = window.WebSocket || window.MozWebSocket;
            try {
                wsClient = new provider('ws://127.0.0.1:14501/');
                wsClient.onopen = wsPing;
                wsClient.onclose = wsInit;
                wsClient.onmessage = wsReceived;
                window.ws = {
                    send: function (text) { wsSend(text); },
                    onmessage: null
                };
            } catch (err) {
                setTimeout(wsInit, 2000);
            }
        }

        function wsPing() {
            wsClient.send("PING");
            setTimeout(wsPing, 10000);
        }

        function wsSend(text) {
            wsClient.send(text);
        }

        function wsReceived(evt) {
            var data = evt.data;
            if (!data) return;
            if (data == "PING") return wsClient.send("PONG");
            if (data == "PONG") return;
            if (window.ws && window.ws.onmessage && typeof window.ws.onmessage == "function") {
                try { window.ws.onmessage(evt); } catch (error) {
                    console.error(error)
                }
            }
        }

        wsInit();
    }

    // 初始化旧版下载狗。
    function initDownloadDog() {
        var wsClient = null;

        function wsInit() {
            var provider = window.WebSocket || window.MozWebSocket;
            try {
                wsClient = new provider('ws://127.0.0.1:14502/');
                wsClient.onopen = wsPing;
                wsClient.onclose = setTimeout(wsInit, 3000);
                wsClient.onmessage = wsReceived;
                window.dldog = {
                    send: function (text) {
                        return wsSend(text)
                    }
                }
            } catch (e) {
                setTimeout(wsInit, 2000)
            }
        }

        function wsPing() {
            wsClient.send('PING')
            setTimeout(wsPing, 10000)
        }

        function wsSend(text) {
            if (!wsClient) { return false }
            try {
                wsClient.send(text)
                return true
            } catch {
                return false
            }
        }

        function wsReceived(evt) {
            var data = evt.data
            if (!data) return
            if (data == 'PING') return wsClient.send('PONG')
            if (data == 'PONG') return
            if (window.dldog && window.dldog.onmessage && typeof window.dldog.onmessage === 'function') {
                try { window.dldog.onmessage(evt) } catch (error) {
                    console.error(error)
                }
            }
        }

        wsInit();
    }

    // 使用旧版狗（WebSocket）。
    function useDogs() {
        initPrintDog();
        initDownloadDog();
    }

    // 下载 MesKeeper。
    function downloadKeeper() {
        var url = "/meskeeper.exe";
        var a = document.createElement("a");
        a.href = url;
        a.setAttribute("target", "_blank");
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    // 检查 MesKeeper。
    function checkKeeper() {
        mesKeeper.get("mes", "info", null, function (r) {
            console.log("MesKeeper", "Info", r.data);
            if (window.mesKeeper) window.mesKeeper.info = r.data;
        }, function (r) {
            if (typeof toast == "function") toast("请安装 MES 插件。");
            downloadKeeper();
        });
    }

    // 使用 MesKeeper 取代 Dogs。
    function useMesKeeper() {
        mesKeeper.get("system", "info", null, function (r) {
            console.log("MesKeeper", "Info", r.data);
        });
        window.ws = {
            send: function (text) {
                var data = JSON.parse(text);
                mesKeeper.post("Mes", "Print", data, function (r) {
                    // if (typeof toast == "function") toast("已调用打印。");
                    // if (typeof window.ws.onmessage == "function") window.ws.onmessage({ data: "已调用打印" });
                }, function (r) {
                    if (typeof toast == "function") toast(r.message);
                    checkKeeper();
                });
            },
            onmessage: null
        };
        window.dldog = {
            send: function (text) {
                var data = JSON.parse(text);
                data.items = data.files;
                data.files = null;
                mesKeeper.post("Mes", "DownEL", data, function () {
                }, function (r) {
                    if (typeof toast == "function") toast("调用下载失败，请安装 MES 插件后重试。");
                    checkKeeper();
                });
                return true;
            },
            onmessage: null
        }
        window.mesKeeper.check = checkKeeper;
    }

    // 尝试连接 MesKeeper。
    useMesKeeper();

    // 尝试连接旧版狗。
    // useDogs();

})();