void* client_thread(void* arg)
{
    BankSocketThread* bankSocketThread = (BankSocketThread*) arg;
    Bank* bank = bankSocketThread->bank;
    BankSession* bankSession = new BankSession();
    bankSession->state = 0;
    bankSession->bank = bank;
    bankSession->key = 0;

    long int csock = (long int)*(bankSocketThread->csock);
    
    printf("[bank] client ID #%ld connected\n", csock);
    
    //input loop
    int length;
    char packet[1024];
    bool fatalError = false;
    std::vector<std::string> tokens;
    while(1)
    {
        fatalError = false;
        tokens.clear();
        
        if(!listenPacket(csock, packet))
        {
            printf("[bank] fail to read packet\n");
            break;
        }
        if(!bankSession->key)
        {
            if(bankSession->state != 0)
            {
                printf("[error] Unexpected state\n");
                break;
            }
            for(unsigned int i = 0; i < bank->keys.size(); ++i)
            {
                if(bank->keysInUse[i])
                {
                    continue;
                }
                if(decryptPacket((char*)packet,bank->keys[i])
                    && std::string(packet).substr(0,9) == "handshake")
                {
                    bankSession->key = bank->keys[i];
                    bank->keysInUse[i] = true;
                    break;
                }
            }
            if(!bankSession->key)
            {
                printf("[error] Key not found.\n");
                break;
            }
        } else {
            if(!decryptPacket((char*)packet, bankSession->key))
            {
                printf("[error] Invalid key\n");
                break;
            }
        }    

        //Parse the packet
        //std::string strPacket = packet;
        split(std::string(packet),',', tokens);

        //We should get something, if not ignore this packet
        if(tokens.size() < 1)
        {
            continue;
        }

        if(tokens[0] == "logout")
        {
            bankSession->endSession();
            break;
        }

        //Now we're compare what we go to what state we expect to be in
        switch(bankSession->state)
        {
            case 0:
            case 1:
                if(tokens.size() == 2 && tokens[0] == "handshake" && tokens[1].size() == 128)
                {
                    bankSession->atmNonce = tokens[1];
                    bankSession->bankNonce = makeHash(randomString(128));
                    if(bankSession->bankNonce.size() == 0)
                    {
                        printf("Unexpected error\n");
                        fatalError = true;
                        break;
                    }
                    buildPacket(packet, "handshakeResponse," + bankSession->atmNonce + "," + bankSession->bankNonce);
                    if(!encryptPacket((char*)packet,bankSession->key))
                    {
                        printf("Unexpected error\n");
                        fatalError = true;
                        break;
                    }
                    if(!sendPacket(csock, packet))
                    {
                        printf("Unexpected error\n");
                        fatalError = true;
                        break;
                    }
                    bankSession->state = 2;
                }
                break;
            //Expecting a login
            case 2:
                if(!bankSession->validateNonce(std::string(packet)))
                {
                    printf("Unexpected error\n");
                    fatalError = true;
                    break;
                }
                if(tokens.size() == 5 && tokens[0] == "login" && tokens[1].size() == 128)
                {
                    //Now we'll try to find the account
                    //bankSession->account = bank->tryLoginHash(tokens[1]);
                    bankSession->account = bank->getAccountByName(tokens[2]);
                    if(!bankSession->account || !bankSession->account->tryHash(tokens[1]))
                    {
                        //Failed login
                        //TODO Blacklist hash
                        bankSession->error = true;
                        //printf("[notice] Failed login!\n");
                    }
                    bankSession->account->inUse = true;
                    bankSession->state = 5;
                    if(!bankSession->sendP(csock,packet, "ack"))
                    {
                        printf("Unexpected error!\n");
                        fatalError = true;
                        break;
                    }
                }
                break;
            case 5:
                bool returnBalance = false;
                bankSession->state = 4;
                if(bankSession->error)
                {
                    returnBalance = false;
                } 
                else if(tokens.size() == 3 && tokens[0] == "balance")
                {
                    returnBalance = true;
                }
                else if(tokens.size() == 4 && tokens[0] == "withdraw" && isDouble(tokens[1]))
                {
                    double amount = atof(tokens[1].c_str());
                    if(!bankSession->account->Withdraw(amount))
                    {
                        printf("[error] Failed withdraw\n");
                        returnBalance = false;
                        bankSession->error = true;
                    }
                    returnBalance = true;
                }
                else if(tokens.size() == 5 && tokens[0] == "transfer" && !isDouble(tokens[1])
                    && isDouble(tokens[2]))
                {
                    Account* accountTo = bank->getAccountByName(tokens[1]);
                    double amount = atof(tokens[2].c_str());
                    if(!bankSession->account->Transfer(amount, accountTo))
                    {
                        printf("[error] Failed transfer\n");
                        returnBalance = false;
                        bankSession->error = true;
                    }
                    returnBalance = true;
                }

                if(bankSession->error)
                {
                    bankSession->sendP(csock, packet, "denied");
                }
                else if(returnBalance)
                {
                    char moneyStr[256];
                    sprintf(moneyStr,"%.2Lf",bankSession->account->getBalance());
                    bankSession->sendP(csock, packet, std::string(moneyStr));
                }

                //Reset back to initial state
                bankSession->endSession();
                break;
        }
        
        if(fatalError)
        {
            bankSession->endSession();
            break;
        }
    }

    bankSession->endSession();

    printf("[bank] client ID #%ld disconnected\n", csock);

    close(csock);
    delete bankSession;
    return NULL;
}
void* console_thread(void* arg)
{
    BankSocketThread* bankSocketThread = (BankSocketThread*) arg;
    Bank* bank = bankSocketThread->bank;

    //Let's generate our keys
    for(unsigned int i = 1; i <= 50; ++i)
    {
        byte* key = new byte[CryptoPP::AES::DEFAULT_KEYLENGTH];
        generateRandomKey(to_string((int)i),key, CryptoPP::AES::DEFAULT_KEYLENGTH);
        bank->keys.push_back(key);
        bank->keysInUse.push_back(false);
    }

    //Create Accounts
    Account* new_account = new Account();

    //Alice
    new_account->createAccount(std::string("alice"), 1, std::string("123456"), bank->appSalt);
    new_account->Deposit(100);
    bank->addAccount(new_account);

    //Bob
    new_account = new Account();
    new_account->createAccount(std::string("bob"), 2, std::string("234567"), bank->appSalt);
    new_account->Deposit(50);
    bank->addAccount(new_account);

    //Eve
    new_account = new Account();
    new_account->createAccount(std::string("eve"), 3, std::string("345678"), bank->appSalt);
    new_account->Deposit(0);
    bank->addAccount(new_account);

    char buf[80];
    while(1)
    {
        printf("bank> ");
        fgets(buf, 79, stdin);
        buf[strlen(buf)-1] = '\0';  //trim off trailing newline

        std::vector<std::string> tokens;
        split(buf,' ',tokens);

        if(tokens.size() <= 0)
        {
            printf("Invalid input\n");
            continue;
        }
        if(tokens[0] == "balance")
        {
            if(tokens.size() != 2)
            {
                printf("Invalid input\n");
                continue;
            }

            Account* current_account = bank->getAccountByName(tokens[1]);
            if(!current_account)
            {
                printf("Invalid account\n");
                continue;
            }
            printf("Balance: %.2Lf\n", current_account->getBalance());
            continue;
        }

        if(tokens[0] == "deposit")
        {
            if(tokens.size() != 3)
            {
                printf("Invalid input\n");
                continue;
            }

            long double amount = atof(tokens[2].c_str());

            if(amount <= 0)
            {
                printf("Invalid amount\n");
                continue;
            }

            Account* current_account = bank->getAccountByName(tokens[1]);
            if(!current_account)
            {
                printf("Invalid account\n");
                continue;
            }

            if(current_account->Deposit(amount))
            {
                long double cur_balance = current_account->getBalance();
                printf("Money deposited!\nNew balance: %.2Lf\n", cur_balance);
            } else {
                printf("Error depositing money!\n");
            }
            continue;
        }
        
    }
}