/*
 * @project: TERA
 * @version: Development (beta)
 * @license: MIT (not for evil)
 * @copyright: Yuriy Ivanov 2017-2019 [progr76@gmail.com]
 * Web: https://terafoundation.org
 * Twitter: https://twitter.com/terafoundation
 * Telegram: https://web.telegram.org/#/im?p=@terafoundation
*/

"use strict";
const fs = require('fs');
const crypto = require('crypto');
require("./library");
require("./crypto-library");
const WalletPath = "WALLET";
const DBRow = require("./db/db-row");
const CONFIG_NAME = GetDataPath(WalletPath + "/config.lst");
class CApp
{
    constructor()
    {
        CheckCreateDir(GetDataPath(WalletPath))
        var bReadOnly = (global.PROCESS_NAME !== "TX");
        this.DBHistory = new DBRow("../" + WalletPath + "/wallet-act", 4 * 6 + 1 + 200 + 10 + 6, "{BlockNum:uint, FromID:uint, FromOperationID:uint, ToID:uint, Direct:str1, Description:str200, SumCOIN:uint,SumCENT:uint32, Currency:uint}",
        bReadOnly)
        this.Password = ""
        this.WalletOpen = undefined
        var Params = LoadParams(CONFIG_NAME, undefined);
        if(!Params)
        {
            Params = {}
            if(global.TEST_NETWORK)
            {
                Params.Key = global.ARR_PUB_KEY[0]
            }
            else
            {
                Params.Key = GetHexFromArr(crypto.randomBytes(32))
            }
            Params.AccountMap = {}
            Params.MiningAccount = 0
        }
        if(Params.MiningAccount)
            global.GENERATE_BLOCK_ACCOUNT = Params.MiningAccount
        this.AccountMap = Params.AccountMap
        this.KeyPair = crypto.createECDH('secp256k1')
        if(Params.Protect)
        {
            ToLogClient("Wallet protect by password")
            this.KeyXOR = GetArrFromHex(Params.KeyXOR)
            this.WalletOpen = false
            this.SetPrivateKey(Params.PubKey)
        }
        else
        {
            this.SetPrivateKey(Params.Key)
        }
    }
    SetMiningAccount(Account)
    {
        global.GENERATE_BLOCK_ACCOUNT = Account
        this.SaveWallet()
    }
    AddTransaction(Tr)
    {
        if(!global.TX_PROCESS.Worker)
            return 0;
        var StrHex = GetHexFromArr(sha3(Tr.body));
        global.TX_PROCESS.Worker.send({cmd:"FindTX", TX:StrHex})
        return SERVER.AddTransaction(Tr, 1);
    }
    SetPrivateKey(KeyStr, bSetNew)
    {
        var bGo = 1;
        if(this.WalletOpen === false)
        {
            bGo = 0
        }
        if(KeyStr && KeyStr.length === 64 && bGo)
        {
            this.KeyPair.setPrivateKey(GetArr32FromHex(KeyStr))
            this.KeyPair.PubKeyArr = this.KeyPair.getPublicKey('', 'compressed')
            this.KeyPair.PubKeyStr = GetHexFromArr(this.KeyPair.PubKeyArr)
            this.KeyPair.PrivKeyStr = KeyStr.toUpperCase()
            this.KeyPair.addrArr = this.KeyPair.PubKeyArr.slice(1)
            this.KeyPair.addrStr = GetHexAddresFromPublicKey(this.KeyPair.addrArr)
            this.KeyPair.addr = this.KeyPair.addrArr
            this.KeyPair.WasInit = 1
            this.PubKeyArr = this.KeyPair.PubKeyArr
        }
        else
        {
            this.KeyPair.WasInit = 0
            if(KeyStr)
            {
                this.PubKeyArr = GetArrFromHex(KeyStr)
                this.KeyPair.PubKeyStr = GetHexFromArr(this.PubKeyArr)
            }
            else
            {
                this.PubKeyArr = []
                this.KeyPair.PubKeyStr = ""
            }
            this.KeyPair.PrivKeyStr = ""
        }
        if(bSetNew)
        {
            this.AccountMap = {}
        }
        this.FindMyAccounts(0)
        if(bGo)
            this.SaveWallet()
    }
    CloseWallet()
    {
        this.Password = ""
        this.WalletOpen = false
        this.KeyPair = crypto.createECDH('secp256k1')
        this.SetPrivateKey(GetHexFromArr(this.PubKeyArr), false)
        ToLogClient("Wallet close")
        return 1;
    }
    OpenWallet(StrPassword)
    {
        if(this.WalletOpen !== false)
        {
            ToLogClient("Wallet was open")
        }
        var Hash = this.HashProtect(StrPassword);
        var TestPrivKey = this.XORHash(this.KeyXOR, Hash, 32);
        if(!IsZeroArr(TestPrivKey))
        {
            this.KeyPair.setPrivateKey(Buffer.from(TestPrivKey))
            var TestPubKey = this.KeyPair.getPublicKey('', 'compressed');
            if(CompareArr(TestPubKey, this.PubKeyArr) !== 0)
            {
                ToLogClient("Wrong password")
                return 0;
            }
            this.Password = StrPassword
            this.WalletOpen = true
            this.SetPrivateKey(GetHexFromArr(TestPrivKey), false)
        }
        else
        {
            this.Password = StrPassword
            this.WalletOpen = true
            this.SetPrivateKey(GetHexFromArr(this.PubKeyArr), false)
        }
        ToLogClient("Wallet open")
        return 1;
    }
    SetPasswordNew(StrPassword)
    {
        if(this.WalletOpen === false)
        {
            ToLogClient("Wallet is close by password")
            return ;
        }
        this.Password = StrPassword
        if(StrPassword)
            this.WalletOpen = true
        else
            this.WalletOpen = undefined
        this.SaveWallet()
    }
    HashProtect(Str)
    {
        var arr = shaarr(Str);
        for(var i = 0; i < 10000; i++)
        {
            arr = shaarr(arr)
        }
        return arr;
    }
    XORHash(arr1, arr2, length)
    {
        var arr3 = [];
        for(var i = 0; i < length; i++)
        {
            arr3[i] = arr1[i] ^ arr2[i]
        }
        return arr3;
    }
    SaveWallet()
    {
        if(this.WalletOpen === false)
        {
            return ;
        }
        var Params = {};
        if(this.Password)
        {
            Params.Protect = true
            var Hash = this.HashProtect(this.Password);
            if(this.KeyPair.WasInit)
            {
                Params.KeyXOR = GetHexFromArr(this.XORHash(this.KeyPair.getPrivateKey(), Hash, 32))
            }
            else
            {
                var Key2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
                Params.KeyXOR = GetHexFromArr(this.XORHash(Key2, Hash, 32))
            }
            Params.PubKey = GetHexFromArr(this.PubKeyArr)
            this.KeyXOR = GetArrFromHex(Params.KeyXOR)
        }
        else
        {
            if(this.KeyPair.WasInit)
                Params.Key = this.KeyPair.PrivKeyStr
            else
                Params.Key = GetHexFromArr(this.PubKeyArr)
        }
        Params.AccountMap = this.AccountMap
        Params.MiningAccount = global.GENERATE_BLOCK_ACCOUNT
        SaveParams(CONFIG_NAME, Params)
    }
    OnDoHistoryAct(Item, BlockNum)
    {
        Item.BlockNum = BlockNum
        this.DBHistory.Write(Item)
    }
    OnDeleteBlock(BlockNumFrom)
    {
        this.DBHistory.DeleteHistory(BlockNumFrom)
    }
    OnCreateAccount(Data)
    {
        this.AccountMap[Data.Num] = 0
    }
    FindMyAccounts(bClean)
    {
        if(IsZeroArr(this.PubKeyArr))
            return ;
        if(bClean)
            this.AccountMap = {}
        DApps.Accounts.FindAccounts([this.PubKeyArr], this.AccountMap, 0)
    }
    GetAccountKey(Num)
    {
        if(this.KeyPair.WasInit && global.TestTestWaletMode)
        {
        }
        return this.KeyPair;
    }
    GetPrivateKey(Num)
    {
        if(!this.KeyPair.WasInit)
            return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        var KeyPair;
        if(Num)
        {
            KeyPair = this.GetAccountKey(Num)
        }
        else
        {
            KeyPair = this.KeyPair
        }
        return KeyPair.getPrivateKey();
    }
    GetSignFromArr(Arr, Num)
    {
        if(!this.KeyPair.WasInit)
            return "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
        var PrivKey = this.GetPrivateKey(Num);
        var hash = shabuf(Arr);
        var sigObj = secp256k1.sign(hash, Buffer.from(PrivKey));
        return GetHexFromArr(sigObj.signature);
    }
    GetSignTransaction(TR)
    {
        if(!this.KeyPair.WasInit)
            return "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
        var PrivKey = this.GetPrivateKey(this.AccountMap[TR.FromID]);
        var Arr = DApps.Accounts.GetSignTransferTx(TR, PrivKey);
        return GetHexFromArr(Arr);
    }
    GetHistoryMaxNum()
    {
        return this.DBHistory.GetMaxNum();
    }
    GetHistory(start, count, Filter, Direct00)
    {
        if(Filter)
        {
            Filter = Filter.trim()
            Filter = Filter.toUpperCase()
        }
        var arr = [];
        for(var num = start; true; num++)
        {
            var Item = this.DBHistory.Read(num);
            if(!Item)
                break;
            Item.ToName = ""
            if(Item.ToID !== 1000000000000)
            {
                var Account = DApps.Accounts.ReadState(Item.ToID);
                if(Account)
                {
                    Item.ToName = Account.Name
                }
            }
            if(Filter)
            {
                var Date = DateFromBlock(Item.BlockNum);
                var Str = "" + Date + " " + Item.Description.toUpperCase() + " " + Item.FromID + " " + Item.ToID + " " + Item.ToName.toUpperCase() + " " + Item.FromOperationID + " " + Item.BlockNum;
                if(Str.indexOf(Filter) < 0)
                    continue;
            }
            Item.Value = {SumCOIN:Item.SumCOIN, SumCENT:Item.SumCENT}
            arr.push(Item)
            count--
            if(count < 1)
                break;
        }
        return arr;
    }
};
global.WALLET = new CApp;
