import * as uuid from "uuid/v4"

import { HookRPC, HookInfo, SocketioRPC, RpcInfo, ExtendedRpcInfo } from "./Types";
import { Socket } from "./interfaces/Socket";

export const rpcToRpcinfo = (rpc : SocketioRPC, owner: string):RpcInfo => {
    switch(rpc.type){
        case "Call" :
            return {
                owner: owner,
                argNames: extractArgs(rpc.func),
                type: rpc.type,
                name: rpc.name,
                func: rpc.func,
            }
        case "Unhook" :
            return {
                owner: owner,
                argNames: extractArgs(rpc.func),
                type: rpc.type,
                name: rpc.name,
                func: rpc.func,
            }
        case "Hook" :
            const generator = hookGenerator(rpc)
            return {
                owner: owner,
                argNames: extractArgs(generator(undefined)),
                type: rpc.type,
                name: rpc.name,
                unhook: rpc.unhook,
                generator: generator,
            }
    }
}

export const rpcHooker = (socket: Socket, owner:string, RPCs: SocketioRPC[], makeUnique = true):ExtendedRpcInfo[] => {
    const suffix = makeUnique?"-"+uuid().substr(0,4):""
    return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
    .map(info => {
        const ret:any = info
        ret.uniqueName = info.name+suffix

        switch(info.type){
            case "Hook":
                socket.hook(ret.uniqueName, info.generator(socket))
                break;
            default:
                socket.hook(ret.uniqueName, info.func)
        }
        socket.on('close', () => socket.unhook(info.name))
        return ret
    })
}

const hookGenerator = (rpc:HookRPC): HookInfo['generator'] => { 
    const argsArr = extractArgs(rpc.func)
    argsArr.pop()
    const args = argsArr.join(',')

    return eval(`(socket) => async (`+args+`) => { 
        const res = await rpc.func(`+args+(args.length!==0?',':'')+` (x) => {
            socket.call(res.uid, x)
        })
        if(res.result == 'Success'){
            socket.on('close', async () => {
                const unhookRes = await rpc.unhook(res.uid)
                console.log("Specific close handler for", rpc.name, res.uid, unhookRes)
            })

        }
        return res
    }`)
}

const extractArgs = (f:Function):string[] => {
    let fn = String(f)
    let args = fn.substr(0, fn.indexOf(")"))
    args = args.substr(fn.indexOf("(")+1)
    let ret = args.split(",")
    return ret
}