
const http = require('http')
    ,fs = require('fs')

    ,GridManager = require('./GridManager.js')
    ,LetterCell = require('./Grid.js').LetterCell;

function HttpServer(config) {
    var ctx = this;

    this.config = config;
    this.httpServ = http.createServer(function(req, res) {
        req.reqT = new Date();
        res.ended = false;
        res.once('end', () => {
            res.ended = true;
        });
        try {
            if (ctx.isRequestPublic(req.url)) {
                ctx.servePublic(req.url, res);
            } else {
                var url = req.url.substr(req.url.indexOf("/api/") +5);
                ctx.serveApi(req, url, res);
            }
        } catch (e) {
            if (e instanceof HttpServer.Error404) {
                res.writeHeader("404", "Not found");
                ctx.servePublic("/404.html", res);
            } else {
                console.error("Unknown error", e);
            }
        }
        ctx.logRequest(req, res);
    });
};

HttpServer.prototype.run = function() {
    this.httpServ.listen(this.config.port, () => {
        console.log("[http] listening on " +this.config.port);
    });
};

HttpServer.prototype.logRequest = function(req, res) {
    console.log("[http] "
            +req.reqT.toISOString()
            +" "
            +req.socket.remoteAddress
            +" "
            +res.statusCode);
};

HttpServer.prototype.isRequestPublic = function(url) {
    return url.indexOf("/api/") === -1;
};

HttpServer.prototype.servePublic = function(url, res) {
    url.replace(/\W/g, '');
    var pathTokens = url.split('/')
        ,localPath = './public';

    pathTokens.forEach(function(i) {
        i = i.trim();
        if (i != '' && i[0] != '.') {
            localPath += '/' +i;
        }
    });
    var stat;
    try {
        var stat = fs.lstatSync(localPath);
        if (stat.isDirectory()) {
            localPath += "/index.html";
            stat = fs.lstatSync(localPath);
        }
    } catch (e) {
        throw new HttpServer.Error404(localPath);
    }
    if (!res.headersSent) {
        if (!this.config.isDebug) {
            res.setHeader("Cache-Control", "private, max-age=900");
        }
        res.setHeader("Content-Length", stat.size);
    }
    fs.createReadStream(localPath).pipe(res, { end: true });
};

HttpServer.parseUrlParams = function(urlSearch) {
    var urlToken = {};

    urlSearch.split('&').forEach((token) => {
        var tokenSplit = token.indexOf('=')
            ,tokenKey
            ,tokenValue;

        if (tokenSplit === -1) {
            tokenKey = token;
            tokenValue = true;
        } else {
            tokenKey = token.substr(0, tokenSplit);
            tokenValue = token.substr(tokenSplit +1);
        }
        if (urlToken[tokenKey]) {
            urlToken[tokenKey].push(tokenValue);
        } else {
            urlToken[tokenKey] = [ tokenValue ];
        }
    });
    return urlToken;
};

HttpServer.prototype.serveApi = function(req, url, res) {
    var pos = url.indexOf('?')
        ,urlToken = {};

    if (pos !== -1) {
        urlToken = HttpServer.parseUrlParams(url.substr(pos +1));
        url = url.substr(0, pos);
    }
    if (url === "create") {
        // TODO limit grid creation 1 minute by ip
        var gridId = urlToken["gridId"] ? urlToken["gridId"][0] : null;
        if (!gridId) {
            var dd = req.reqT.getDate()
                ,mm = req.reqT.getMonth() +1
                ,yy = req.reqT.getFullYear() -2000;
            gridId = (dd < 10 ? '0' +dd :dd) +(mm < 10 ? '0' +mm : mm) +yy;
        }
        GridManager.createGrid(GridManager.hash('' +req.socket.remoteAddress +req.reqT.getTime() +gridId), gridId, (grid)=>{
            if (grid) {
                grid.minVersion = req.reqT.getTime();
                res.setHeader("Location", "/game.html#" +grid.publicId);
                res.writeHeader("302");
            } else {
                res.writeHeader("400", "Bad request");
            }
            res.end();
        });
    } else if (url === "poll") {
        if (!urlToken["v"] || !urlToken["grid"]) {
            res.writeHeader("400", "Bad request");
            res.end();
            return;
        }
        var knownVersion = parseFloat(urlToken["v"][0])
            ,grid = GridManager.get(urlToken["grid"][0]);
        if (!grid) {
            throw new HttpServer.Error404();
        }
        var data = grid.toStatic(knownVersion);
        if (data) {
            res.setHeader("Content-Type", "application/json; charset=utf-8");
            res.end(JSON.stringify(data));
        } else {
            res.writeHeader("204");
            res.end();
        }

    } else if (url === "put") {
        if (!urlToken["x"] || !urlToken["y"] || !urlToken["key"] || !urlToken["grid"]) {
            res.writeHeader("400", "Bad request");
            res.end();
            return;
        }
        var x = parseFloat(urlToken["x"][0])
            ,y = parseFloat(urlToken["y"][0])
            ,grid = GridManager.get(urlToken["grid"][0]);
        if (!grid) {
            throw new HttpServer.Error404();
        }
        var cell = grid.getCell(x, y);
        if (!cell || !cell instanceof LetterCell || cell.found) {
            res.writeHeader("400", "Bad request");
            res.end();
            return;
        }
        if (cell.letter !== urlToken["key"][0]) {
            res.writeHeader("403");
            res.end();
            return;
        }
        cell.found = true;
        cell.version = req.reqT.getTime();
        res.writeHeader("204");
        res.end();
    } else {
        throw new HttpServer.Error404(url);
    }
};

HttpServer.Error404 = function(localPath) {
    console.log("[http] file not found: " +localPath);
};

module.exports.serve = function(config) {
    var server = new HttpServer(config);
    server.run();
};

