"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/// <reference path="../../../typings/index.d.ts" />
var express = require("express");
var Settings = require("./settings");
var pathModule = require("path");
var context_1 = require("../../../cli/lib/context");
var Leaf = require("../../../build/leaf.export");
var fs = require("fs");
var child_process = require("child_process");
var wrench = require("wrench");
var ws = require("ws");
var http = require("http");
var chokidar = require("chokidar");
var colors = require("colors");
var findFreePort = require("find-free-port");
if (Settings.debug) {
    console.log("Debug mode: On");
}
var ComposerServerDesign = (function () {
    function ComposerServerDesign(parser) {
        this.parser = parser;
    }
    return ComposerServerDesign;
}());
var ComposerServer = (function (_super) {
    __extends(ComposerServer, _super);
    function ComposerServer(parser) {
        var _this = _super.call(this, parser) || this;
        _this.port = 3780;
        _this.stylePath = "/style/main.css";
        _this.styleFilePath = "/style/main.css";
        _this.scriptPath = "/script/R.js";
        _this.rpcManager = new RPCManager();
        _this.fireTimer = null;
        _this.changeTimerDebounce = 300;
        _this.app = express();
        _this.server = http.createServer(_this.app);
        _this.wsServer = new ws.Server({ server: _this.server });
        _this.wsServer.on("connection", function (con) {
            var service = new RPCService();
            var adapter = new RPCWSAdapter(con);
            service.setConnection(adapter);
            _this.rpcManager.add(service);
            adapter.events.listenBy(_this, "close", function () {
                _this.rpcManager.remove(service);
                adapter.events.stopListenBy(_this);
            });
        });
        _this.app.get(_this.scriptPath, function (req, res, next) {
            var context = _this.createContext();
            var result;
            try {
                result = context.compile();
            }
            catch (e) {
                console.error("compile error", e);
                if (_this.RJSCache) {
                    res.end(_this.RJSCache);
                }
                else {
                    res.end("console.error('Compile error')");
                }
            }
            if (result != _this.RCache) {
                fs.writeFileSync(pathModule.join(Settings.tempFolder, "R.ts"), result);
                fs.writeFileSync(pathModule.join(Settings.tempFolder, "tsconfig.json"), JSON.stringify({
                    "compilerOptions": {
                        "target": "ES5",
                        "sourceMap": false,
                        "module": "commonjs"
                    }
                }));
                try {
                    console.log(child_process.execSync("tsc -p " + pathModule.join(Settings.tempFolder, "tsconfig.json")));
                }
                catch (e) {
                    console.error(colors.red("Fail to compile R"), e.stdout.toString());
                }
                var content = fs.readFileSync(pathModule.join(Settings.tempFolder, "R.js"), "utf8");
                _this.RCache = result;
                _this.RJSCache = content;
                res.end(content);
            }
            else {
                res.end(_this.RJSCache);
            }
        });
        _this.app.get("/", function (req, res, next) {
            var html = fs.readFileSync(pathModule.join(Settings.staticPath, "index.html"), "utf8");
            html = html.replace("{{stylePath}}", _this.stylePath).replace("{{scriptPath}}", _this.scriptPath);
            res.end(html);
        });
        _this.app.get("/index.html", function (req, res, next) {
            res.redirect("/");
        });
        console.error(_this.parser.getRootPath(), colors.green("run at"));
        _this.app.use("/", express.static(_this.parser.getRootPath()));
        _this.app.use("/composer", express.static(Settings.staticPath));
        _this.applyWatcher();
        return _this;
    }
    ComposerServer.prototype.setStylePath = function (path) {
        console.log("set style path");
        this.styleFilePath = path;
        this.stylePath = pathModule.join("/", path);
        this.applyWatcher();
    };
    ComposerServer.prototype.applyWatcher = function () {
        var _this = this;
        if (this.tsWatcher) {
            this.tsWatcher.close();
            this.styleWatcher.close();
        }
        this.tsWatcher = chokidar.watch(this.parser.getTemplatePath());
        this.styleWatcher = chokidar.watch(pathModule.join(this.parser.getRootPath(), this.styleFilePath));
        var change = function (filename) {
            _this.fireChangeEvent();
        };
        this.tsWatcher.on("add", change)
            .on("change", change);
        this.styleWatcher.on("add", change)
            .on("change", change);
    };
    ComposerServer.prototype.createContext = function () {
        var _this = this;
        var context = new context_1.Context(this.parser.getTemplatePath());
        context.rootName = "TestR";
        var files = wrench.readdirSyncRecursive(this.parser.getTemplatePath()).filter(function (item) {
            try {
                return fs.statSync(pathModule.join(_this.parser.getTemplatePath(), item)).isFile();
            }
            catch (e) {
                return false;
            }
        });
        for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
            var file = files_1[_i];
            context.includeWidgetByRelativeFilePath(file);
        }
        return context;
    };
    ComposerServer.prototype.start = function (port) {
        var _this = this;
        this.port = port;
        findFreePort(parseInt(this.port.toString()), function (err, port) {
            if (err) {
                console.error(colors.red("fail to find any available port"));
                return;
            }
            _this.server.listen(parseInt(port.toString()), Settings.host, function () {
                _this.port = port;
                console.log(colors.green("Composer server listen at http://0.0.0.0:" + port));
            });
        });
    };
    ComposerServer.prototype.fireChangeEvent = function () {
        var _this = this;
        clearTimeout(this.fireTimer);
        this.fireTimer = setTimeout(function () {
            for (var _i = 0, _a = _this.rpcManager.rpcs; _i < _a.length; _i++) {
                var rpc = _a[_i];
                rpc.emitRemoteEvent("change");
            }
        }, this.changeTimerDebounce);
    };
    return ComposerServer;
}(ComposerServerDesign));
exports.ComposerServer = ComposerServer;
var RPCManager = (function () {
    function RPCManager() {
        this.rpcs = [];
    }
    RPCManager.prototype.add = function (rpc) {
        this.rpcs.push(rpc);
    };
    RPCManager.prototype.remove = function (rpc) {
        this.rpcs = this.rpcs.filter(function (item) { return item !== rpc; });
    };
    return RPCManager;
}());
var RPCService = (function (_super) {
    __extends(RPCService, _super);
    function RPCService() {
        return _super.apply(this, arguments) || this;
    }
    return RPCService;
}(Leaf.RPCComposer));
var RPCWSAdapter = (function (_super) {
    __extends(RPCWSAdapter, _super);
    function RPCWSAdapter(con) {
        var _this = _super.call(this) || this;
        _this.con = con;
        _this.con.once("close", function () {
            _this.events.emit("close");
        });
        return _this;
    }
    RPCWSAdapter.prototype.send = function (message) {
        this.con.send(message);
    };
    return RPCWSAdapter;
}(Leaf.RPCConnectionBase));
//# sourceMappingURL=composer.js.map