/* RESTful module */

(function() {
    'use strict';
    
    if (!global.cloudcmd)
        return console.log(
             '# rest.js'                                        + '\n'  +
             '# -----------'                                    + '\n'  +
             '# Module is part of Cloud Commander,'             + '\n'  +
             '# used for work with REST API.'                   + '\n'  +
             '# If you wont to see at work set rest: true'      + '\n'  +
             '# and apiURL in config.json'                      + '\n'  +
             '# http://cloudcmd.io'                             + '\n');
    
    var main        = global.cloudcmd.main,
        fs          = main.fs,
        path        = main.path,
        crypto      = main.crypto,
        Util        = main.util,
        pipe        = main.pipe,
        CloudFunc   = main.cloudfunc,
        
        onFSGet     = main.srvrequire('rest/fs/get').onGet,
        onFSPut     = main.srvrequire('rest/fs/put').onPut,
        onDelete    = main.srvrequire('rest/fs/delete').onDelete,
        markdown    = main.srvrequire('rest/markdown'),
        
        JSONDIR     = main.JSONDIR,
        OK          = 200,
        sendError   = main.sendError,
        sendResponse= main.sendResponse,
        Header      = main.generateHeaders({
            name:'api.json'
        }),
        
        DIR         = './',
        
        flop        = require(DIR + 'flop'),
        pack        = require(DIR + 'pack'),
        mellow      = require(DIR + 'mellow'),
        format      = require(DIR + 'format'),
        isWin32     = process.platform === 'win32',
        
        
        NOT_LOG     = true;
        
        
    /**
     * rest interface
     * @pParams {request, responce}
     */
    exports.api = function(request, response, callback) {
        var apiURL, name, ret;
        
        if (request && response) {
            apiURL  = CloudFunc.apiURL;
            name    = main.getPathName(request);
            ret     = Util.isContainStr(name, apiURL);
            
            if (ret) {
                name = Util.rmStrOnce(name, apiURL) || '/';
                sendData({
                    request     : request,
                    response    : response,
                    name        : name
                });
            }
        }
        if (!ret)
            Util.exec(callback);
        
        return ret;
    };
    
    /**
     * send data
     * 
     * @param pRes
     * @param pData
     */
    function send(params) {
        var res     = params.response,
            data    = params.data,
            str     = Util.stringifyJSON(data);
        
        res.writeHead(OK, Header);
        res.end(str);
    }
    
    /**
     * getting data on method and command
     * 
     * @param pParams {command, method, body, requrest, response}
     */
    function sendData(params) {
        var p, isFS, isMD,
            ret     = main.checkParams(params);
        
        if (ret) {
            p       = params;
            isFS    = Util.isContainStrAtBegin(p.name, CloudFunc.FS),
            isMD    = Util.isContainStrAtBegin(p.name, '/markdown');
            
            if (isFS)
                onFS(params);
            else if (isMD)
                markdown.operate(p.name, p.request, function(error, data) {
                    if (error)
                        sendError(p, error);
                    else
                        sendResponse(p, data, NOT_LOG);
                });
            else {
                if (p.name[0] === '/')
                    p.command = Util.rmStrOnce(p.name, '/');
                
                switch(p.request.method) {
                case 'GET':
                    ret = onGET(params);
                    break;
                    
                case 'PUT':
                    pipe.getBody(p.request, function(error, body) {
                        if (error)
                            sendError(params, error);
                        else {
                            p.body = body;
                            onPUT(params);
                        }
                    });
                    break;
                }
            }
        }
        return ret;
    }
    
    function onFS(params) {
        var p, query, path,
            ret     = main.checkParams(params);
        
        if (ret) {
            p       = params;
            query   = main.getQuery(p.request);
            p.name  = Util.rmStrOnce(p.name, CloudFunc.FS) || '/';
            path    = mellow.convertPath(p.name);
            
            switch (p.request.method) {
            case 'GET':
                onFSGet(query, path, function(error, data) {
                    var str,
                        isStr   = Util.isString(data),
                        params  = {
                            gzip: true,
                            name: path,
                            request: p.request,
                            response: p.response,
                        };
                    
                    if (error) {
                        if (error.code === 'ENOTDIR')
                            main.sendFile(params);
                        else
                            sendError(params, error);
                    } else {
                        data.path = format.addSlashToEnd(p.name);
                        params.name += '.json';
                        params.query = query;
                        
                        if (isStr)
                            str = data;
                        else
                            str = Util.stringifyJSON(data);
                        
                        sendResponse(params, str, NOT_LOG);
                    }
                });
            
            break;
                
            case 'PUT':
                onFSPut(query, path, p.request, function(error, msg) {
                    if (error)
                        sendError(params, error);
                    else
                        sendResponse(params, msg);
                });
                break;
                
            case 'DELETE':
                pipe.getBody(p.request, function(error, body) {
                    var files;
                    
                    if (error)
                        sendError(p, error);
                    else {
                        files   = Util.parseJSON(body);
                        
                        onDelete(query, path, files, function(error) {
                            var names;
                            
                            if (error) {
                                sendError(params,error);
                            } else {
                                if (files && files.length)
                                    names = files;
                                else
                                    names = p.name;
                                
                                sendMsg(params, 'delete', names);
                            }
                        });
                    }
                });
                break;
            }
        }
        
        return ret;
    }
    
    /**
     * process data on GET request
     * 
     * @param pParams {command, method, body, requrest, response}
     */
    function onGET(pParams) {
        var p, cmd, json, ret = main.checkParams(pParams);
        if (ret) {
            p   = pParams,
            cmd = p.command;
            
            switch(cmd) {
            case '':
                p.data = {
                    info: 'Cloud Commander API v1'
                };
                send(p);
                break;
            
            case 'config':
                main.sendFile({
                    name    : JSONDIR + 'config.json',
                    request : p.request,
                    response: p.response,
                    cache   : false
                });
                break;
            
            default:
                json = Util.stringifyJSON({
                    error: 'command not found'
                });
                
                sendError(p, json);
                break;
            }
        }
        
        return ret;
    }
    
    /**
     * process data on PUT request
     * 
     * @param pParams {command, method, body, requrest, response}
     */
    function onPUT(params) {
        var p, cmd, files, name, json, config, data, from, to,
            ret             = main.checkParams(params, ['body']);
        
        if (ret) {
            p       = params,
            cmd     = p.command,
            files   = Util.parseJSON(p.body);
            
            switch(cmd) {
            case 'auth':
                main.auth(p.body, function(error, token) {
                    if (error)
                        sendError(p, error);
                    else
                        send({
                            response: p.response,
                            data: token
                        });
                });
                break;
            
            case 'mv':
                if (!files.from || !files.to) {
                    sendError(params, p.data);
                } else if (isRootWin32(files.to)) {
                    sendError(params, getWin32RooMsg('to'));
                } else if (isRootWin32(files.from)) {
                    sendError(params, getWin32RooMsg('from'));
                } else {
                    files.from  = mellow.convertPath(files.from);
                    files.to    = mellow.convertPath(files.to);
                    
                    if (files.names)
                        data    = Util.slice(files.names);
                    else
                        data    = files;
                        
                    copyFiles(files, flop.move, function(error) {
                            if (error)
                                sendError(params, error);
                            else
                                sendMsg(params, 'move', data);
                        });
                }
                
                break;
            
            case 'cp':
                if (!files.from || !files.names || !files.to) {
                    sendError(params, p.data);
                } else if (isRootWin32(files.to)) {
                    sendError(params, getWin32RooMsg('to'));
                } else if (isRootWin32(files.from)) {
                    sendError(params, getWin32RooMsg('from'));
                } else {
                    files.from  = mellow.convertPath(files.from);
                    files.to    = mellow.convertPath(files.to);
                    
                    files.namesAll    = Util.slice(files.names);
                    copyFiles(files, flop.copy, function(error) {
                        if (error)
                            sendError(params, error);
                        else
                            sendMsg(params, 'copy', files.namesAll);
                    });
                }
                break;
            
            case 'zip':
                if (!files.from) {
                    sendError(params, p.data);
                } else {
                    from    = mellow.convertPath(files.from);
                    
                    if (files.to)
                        to  = mellow.convertPath(files.to);
                    else
                        to  = from + '.zip';
                    
                    pack.gzip(from, to, function(error) {
                        var name = path.basename(files.from);
                        
                        if (error)
                            sendError(params, error);
                        else
                            sendMsg(params, 'zip', name);
                    });
            }
                break;
            
            case 'unzip':
                if (!files.from) {
                    sendError(params, p.data);
                } else {
                    from        = mellow.convertPath(files.from);
                    
                    if (files.to)
                        to      = mellow.convertPath(files.to);
                    else
                        to      = Util.rmStrOnce(files.from, ['.zip', '.gzip']);
                    
                  pack.gunzip(from, to, function(error) {
                        var name = path.basename(files.from);
                        
                        if (error)
                            sendError(params, error);
                        else
                            sendMsg(params, 'unzip', name);
                    });
                }
                    
                break;
            
            case 'config':
                var passwd  = files && files.password,
                    sha     = crypto.createHash('sha1');
                    config  = main.config;
                
                if (passwd) {
                    sha.update(passwd);
                    passwd          = sha.digest('hex');
                    files.password = passwd;
                }
                
                for (name in files)
                    config[name] = files[name];
                
                json = Util.stringifyJSON(config) + '\n';
                
                fs.writeFile(JSONDIR + 'config.json', json, function(error) {
                    if (error)
                        sendError(params, error);
                    else
                        sendMsg(params, 'config', name);
                });
                
                break;
            
            default:
                send(params);
                break;
            }
        }
        
        return ret;
    }
    
    function copyFiles(files, processFunc, callback) {
        var names           = files.names,
            
            copy            = function() {
                var isLast, name,
                    from    = files.from,
                    to      = files.to;
                
                if (names) {
                    isLast  = !names.length,
                    name    = names.shift(),
                    from    += name;
                    to      += name;
                } else {
                    isLast  = false;
                    names   = [];
                }
                
                if (isLast)
                    callback();
                else
                    processFunc(from, to, function(error) {
                        if (error)
                            callback(error);
                        else
                            copy();
                    });
            };
        
        Util.checkArgs(arguments, ['files', 'processFunc', 'callback']);
        
        copy();
    }
    
    function isRootWin32(path) {
        var isRoot      = path === '/';
        
        return isWin32 && isRoot; 
    }
    
    function getWin32RooMsg(direction) {
        var messageRaw  = 'Could not copy {{ direction }} root on windows!',
            message     = Util.render(messageRaw, {
                direction: direction
            }),
            error       = Error(message);
        
        return error;
    }
    
    function sendMsg(sendParam, msgParam, dataParam, status) {
        var msg, data,
            isObj = Util.isObject(dataParam);
        
        if (isObj)
            data = Util.stringifyJSON(dataParam);
        else
            data = dataParam;
            
        msg = CloudFunc.formatMsg(msgParam, data, status);
        
        sendResponse(sendParam, msg);
    }
    
})();
