import http = require('http');
import bsock = require('bsock');

import { ExtendedRpcInfo, SocketConf, SocketioRPC } from './Types';
import { rpcHooker } from './Util';
import { Exporter } from './interfaces/Exporter';
import { Socket } from "./interfaces/Socket";

export class Server{
    
    private io = bsock.createServer()
    private wsServer = http.createServer()

    constructor(
        private port:number,
        private rpcExporters: Exporter[] = [],
        private conf: SocketConf = {
            errorHandler: (socket:Socket) => (error:any) => { socket.destroy(); console.error(error) },
            closeHandler: (socket:Socket) => () => { console.log("Socket closing") },
            connectionHandler: (socket:Socket) => { console.log("New websocket connection in port "+socket.port)},
            visibility: "127.0.0.1"
        }
    ){
        this.startWebsocket()
    }

    private startWebsocket(){
        try{
            this.io.attach(this.wsServer)
            this.io.on('socket', (socket:Socket) => {
                socket.on('error', this.conf.errorHandler(socket))
                socket.on('close', this.conf.closeHandler(socket))
                if(this.conf.visibility === "127.0.0.1")
                    this.initRPCs(socket)
                else
                    this.initPublicRPCs(socket)
            })
            this.wsServer.listen(this.port, this.conf.visibility)
        }catch(e){
            //@ts-ignore
            this.errorHandler(undefined)("Unable to connect to socket")
        }
    }

    protected initRPCs(socket:Socket){
        const infoRPC:SocketioRPC[] = [
            {
                name: 'info',
                type: 'Call',
                func: async () => rpcInfos
            }
        ]
        const rpcInfos:ExtendedRpcInfo[] = [
            ...rpcHooker(socket, "RPC", infoRPC, false),
            ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()]))
        ]
    }

    protected initPublicRPCs(socket:Socket){}
}