
import * as api_util from './util';
import * as testImapAccount from './testImapAccount'
import * as smtp from './smtpClass';
import * as express from 'express';
import isOnline = require ('is-online');
import * as Async from 'async'


export const sockRoute = ( io: SocketIO.Server ) => {

    let Session: ISockSession = null;

    let imapConnect: testImapAccount.connectImap = null;
    let echoTimeOut = null;
    let windowsSocketClient: SocketIO.Socket = null;

    const checkOnline = ( CallBack: ICallBack ) => {
        isOnline (( err, online) => {
            if ( err )
                return CallBack ( err )
            online
                ? CallBack ()
                : CallBack ( new Error ( 'offline' ))
        })
    }

    api_util.defineSession ( null, ( err, data: ISockSession ) => {
        if ( err )
            return console.log ( 'api_util.defineSession ERROR: ', err )
        Session = data
    })

    io.on ( 'connection', socket => {
        
        const First_Socket_On = ( password, CallBack ) => {

            api_util.defineSession ( password, ( err, data: ISockSession ) => {
                if ( err )
                    return console.log ( 'api_util.defineSession ERROR: ', err )
                
                if ( data.keyPair.keyPasswordOK ) {
                    if ( ! Session || !Session.keyPair.keyPasswordOK )
                        Session = data
                    needConnect ()
                }

                CallBack ( null, data )
            })
        }
        socket.on ( 'first', First_Socket_On )

        socket.on ( 'deletePassword', CallBack  => {

            if ( ! Session.keyPair.keyPasswordOK ) {
                if ( Session.vpnServerConnectData && Session.vpnServerConnectData.active )
                    return CallBack ( 'UnAllowAccess' )
            }
            
            api_util.deleteSystemPassword ( Session, err => {

                if ( err )
                    return CallBack ( err )
                
                io.emit ( 'deletePassword' )

            })
            
        });

        //          generate key pair 
        socket.on ( 'NewKeyPair', ( data: INewKeyPair , CallBack: ICallBack ) => {

            if ( Session.keyPair && Session.keyPair.keyPasswordOK )
                return CallBack ( 'UnAllowAccess' )

            socket.once ( 'cancelCreateKeyPair', CallBack => {
                console.log ('cancelCreateKeyPair come')

                if ( ! Session.newKeyPairProcessRunning || ! Session.newKeyProcessPid )
                    return CallBack ( 'UnAllowAccess' )
                Session.keyPair.keyPasswordOK = false;
                Session.newKeyProcessPid.stop ();
                Session.newKeyProcessPid = null;
                io.emit ( 'cancelCreateKeyPair' )
                
            })

            const keyPair = Session.keyPair;

            keyPair.email = data.email;
            keyPair.userName = data.nikeName;
            keyPair.keyPasswordOK = true;
            keyPair.keyPassword = Session.keyPairPassword = data.password;
            keyPair.keyBitLength = parseInt ( data.keyLength );
            CallBack ( null, Session )
            Session.newKeyPairProcessRunning = true;

            Async.series ([
                next => {
                    const command = `views/api/NewKeyPair`
                    Session.newKeyProcessPid = new api_util.runCallBack ( command, Session.keyPair, null, next )
                },
                next => api_util.checkInitFile ( Session, next ),
                next => api_util.saveConfig ( Session, next )
                
            ], err => {

                if ( err )
                    return console.log ( 'aWait_NewKeyPair had error:', err )
                socket.join ('/root')
                Session.newKeyPairProcessRunning = false
                Session.newKeyProcessPid = null
                io.emit ( 'NewKeyPair-CallBack', Session.keyPair )
            })
            io.emit ( 'NewKeyPairRunning' )
        })

        socket.on ( 'testImapConnect', ( n: IinputData, CallBack ) => {
            if ( n.systemPassword !== Session.keyPairPassword ) 
                return CallBack ( 'no_login' )
            Async.series ([
                next => checkOnline ( next ),
                next => testImapAccount.testImapCommand ( n.imapUserName, n.imapUserPassword, n.imapServer, n.imapSsl, parseInt ( n.imapPortNumber ), next ),
                next => {
                    socket.emit ( 'testImapOK' )
                    n.imapTested = true;
                    smtp.testSMTP ( n.smtpServer, n.smtpUserName, n.smtpUserPassword, parseInt( n.smtpPortNumber ), n.smtpSsl, next )
                }, next => {
                    socket.emit ( 'testSmtpOK')
                    n.smtpTested = true;
                    Session.imapArray = n;
                    sendConnectrequestEmailToVenEmail ( next )
                }, next => api_util.saveConfig ( Session, next )
                ], ( err, data ) => {
                    if ( err ) {
                        return CallBack ( err.message )
                    }
                    socket.emit ( 'sentMailOK') 
                    needConnect()
            })
        })

                //          delete imap config
        socket.on( 'deleteImapArray', ( n: IinputData, CallBack ) => {

            if ( ! Session.keyPair.keyPasswordOK || ! Session.imapArray || !n || Session.imapArray.account != n.account ) 
                return CallBack ( 'no_login' )

            if ( Session.imapConnected && imapConnect ) {

                imapConnect.destroy ()

                Session.imapConnected = false
                imapConnect = null

            }
            Session.imapArray = null;
            Session.vpnServerConnectData = null;
            return api_util.saveConfig ( Session, () => {
                CallBack ()
            })

        })

        socket.on ( 'sendActivePassword', ( text: string, CallBack ) => {
            sendActiveCommand ( text, CallBack )
        })

        socket.on ( 'windowsSocketReady', () => {
            windowsSocketClient = socket;
            console.log ('windowsSocketClient ready!')
        })
 
    })

    //          send mail to vpnEmail via smtp
        const sendConnectrequestEmailToVenEmail = ( CallBack: ICallBack ) => {
            const timeRound = 1000 * 60 //      one min
                const oldSentTime = Session.sentEmail
                const nowTime = new Date().getTime();

                if ( nowTime - oldSentTime < timeRound )
                    return CallBack();
            console.log ( 'needConnect: try send email to request vpn.email connect' )
            Session.sentEmail = nowTime;
            Session.imapArray.machineID = api_util.machineuuid ()
            Session.imapArray.serverMailBoxName = api_util.machineuuid ()
            smtp.tryConnectEmail ( Session )
            CallBack ()

        }

        //          callback listen event
        const newEmailCallBack = datas  => {

            const vpnData = Session.vpnServerConnectData;

            const command: IClientEmailCommand = datas;
            if ( command.command ) {
                if ( /^echo$/.test ( command.command )) {
                    if ( echoTimeOut ) {
                        clearTimeout ( echoTimeOut )
                        echoTimeOut = null;

                    } else {
                        sendEcho()
                    }

                }
            }
            Session.vpnEmailServerKeepConnected = true;
            console.log ( 'get new email & do /root emit: ', vpnData )
            io.emit ( 'vpnEmailOnline', true )
            io.to( '/root').emit ( 'email', vpnData )
            api_util.saveConfig ( Session, ()=> {})
        }
        //          connect 
        const needConnect = () => {
            
            if ( ! Session.keyPair.keyPasswordOK || ! Session.imapArray || ! Session.imapArray.imapTested || ! Session.imapArray.smtpTested ) {
                    console.log ( 'needConnect: imapArray error!' )
                    return;
                }

            if ( ! Session.imapConnected ) {
                console.log ( 'needConnect: try connect to IMAP server!' )
                imapConnect = new testImapAccount.connectImap ( Session, newEmailCallBack );
            }

            if ( ! Session.vpnEmailServerKeepConnected ) {

                return trySendEchoCommand ()

            }
            api_util.saveConfig ( Session, () => {})
            io.emit ( 'vpnEmailOnline', true )

        }

        const sendCommandToDcoker = ( command: IClientEmailCommand, CallBack ) => {

            imapConnect.saveToVpnEmail ( Session.vpnServerConnectData.serverMailFolder, command, CallBack )
        }

        const sendActiveCommand = ( text: string, CallBack ) => {

            needConnect ()
            if ( ! Session.vpnEmailServerKeepConnected ) {
                return setTimeout (() => {
                    sendActiveCommand ( text, CallBack )
                }, 1000 * 10 )
            }
            Async.waterfall ([
                next => api_util.Decryption ( text, Session.keyPairPassword, Session.keyPair.privateKeyUTF8, Session.vpnEmailPublicKey, next ),
                ( data, next ) => {
                    const command :IClientEmailCommand = {
                        command: 'activePassword',
                        data: data.data,
                        account: Session.imapArray.account
                    }
                    sendCommandToDcoker ( command, next )
                }
            ], ( err, data ) => {
                if ( err )
                    CallBack ( err )
                CallBack ()
            })
        }

        const trySendEchoCommand = ( ) => {
            if ( ! Session.dockerPublicKey || ! Session.dockerPublicKey.length || ! Session.keyPair || ! Session.keyPair.keyPasswordOK ||
                ! Session.vpnServerConnectData || Session.vpnServerConnectData.accountType === 'free' ) {
                return sendConnectrequestEmailToVenEmail(() => {})
            }
            sendEcho()
            
        }

        const sendEcho = () => {
            const command :IClientEmailCommand = {
                command: 'echo',
                data: null,
                account: Session.imapArray.account
            }
            sendCommandToDcoker ( command, ()=>{})
            echoTimeOut = setTimeout (() => {
                Session.vpnEmailServerKeepConnected = false;
                io.emit ( 'vpnEmailOnline', false )
                sendConnectrequestEmailToVenEmail(() => {})
            }, 1000 * 10 )
        }
    //


}