/*
 * @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";
require("../dapp/dapp");
require("../dapp/accounts");
require("../dapp/smart");
require("../dapp/file");
require("../dapp/messager");
require("../dapp/names");
if(global.PROCESS_NAME === "MAIN" || global.PROCESS_NAME === "TX")
    require("./wallet");
module.exports = class CSmartContract extends require("./block-exchange")
{
    constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
    {
        super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
        this.BufHashTree = new RBTree(CompareArr)
        this.BufHashTree.LastAddNum = 0
    }
    AddBlockToHashTree(Block)
    {
        this.BufHashTree.LastAddNum = Block.BlockNum
        var arr = Block.arrContent;
        if(arr)
        {
            for(var i = 0; i < arr.length; i++)
            {
                var HASH = shaarr(arr[i]);
                this.BufHashTree.insert(HASH)
            }
        }
    }
    DeleteBlockFromHashTree(Block)
    {
        var arr = Block.arrContent;
        if(arr)
        {
            for(var i = 0; i < arr.length; i++)
            {
                var HASH = shaarr(arr[i]);
                this.BufHashTree.remove(HASH)
            }
        }
    }
    OnWriteBlock(Block)
    {
    }
    BlockProcessTX(Block)
    {
        if(Block.BlockNum < 1)
            return ;
        var COUNT_MEM_BLOCKS = 0;
        var NUM1 = 1240000;
        var NUM2 = 1400000;
        if(global.LOCAL_RUN)
        {
            NUM1 = 15
            NUM2 = 100
        }
        if(Block.BlockNum > global.BLOCKNUM_TICKET_ALGO)
        {
            NUM1 = 1000000000000
            NUM2 = 1000000000000
        }
        if(Block.BlockNum > NUM1)
        {
            COUNT_MEM_BLOCKS = 1
            if(Block.BlockNum > NUM2)
                COUNT_MEM_BLOCKS = 60
            if(this.BufHashTree.LastAddNum !== Block.BlockNum - 1)
            {
                this.BufHashTree.clear()
                for(var num = COUNT_MEM_BLOCKS; num >= 1; num--)
                {
                    var Block2 = this.ReadBlockDB(Block.BlockNum - num);
                    if(Block2)
                    {
                        this.AddBlockToHashTree(Block2)
                    }
                }
            }
        }
        for(var key in DApps)
        {
            DApps[key].OnWriteBlockStart(Block)
        }
        var arrContentResult = [];
        var BlockNum = Block.BlockNum;
        var arr = Block.arrContent;
        if(arr)
            for(var i = 0; i < arr.length; i++)
            {
                var HASH = shaarr(arr[i]);
                if(this.BufHashTree.find(HASH))
                {
                    continue;
                }
                var type = arr[i][0];
                var App = DAppByType[type];
                if(App)
                {
                    App.ResultTx = 0
                    DApps.Accounts.BeginTransaction()
                    var StrHex = GetHexFromArr(sha3(arr[i]));
                    var item = global.TreeFindTX.LoadValue(StrHex);
                    if(item)
                        global.CurTrItem = item.TX
                    else
                        global.CurTrItem = undefined
                    var Result = App.OnWriteTransaction(Block, arr[i], BlockNum, i);
                    var SetResult = Result;
                    if(Result === true)
                    {
                        if(App.ResultTx)
                            SetResult = App.ResultTx
                        if(!DApps.Accounts.CommitTransaction(BlockNum, i))
                            SetResult = 0
                    }
                    else
                    {
                        DApps.Accounts.RollBackTransaction()
                        SetResult = 0
                    }
                    if(SetResult === true)
                        SetResult = 1
                    arrContentResult[i] = SetResult
                    if(item)
                    {
                        var ResultStr = Result;
                        if(Result === true || typeof Result === "number")
                        {
                            ResultStr = "Add to blockchain"
                            if(type === global.TYPE_TRANSACTION_FILE)
                                ResultStr += ": file/" + BlockNum + "/" + i
                        }
                        item.cmd = "RetFindTX"
                        item.ResultStr = "" + ResultStr
                        item.bFinal = 1
                        item.Result = SetResult
                        process.send(item)
                    }
                    global.CurTrItem = undefined
                }
            }
        if(COUNT_MEM_BLOCKS)
        {
            var Block2 = this.ReadBlockDB(Block.BlockNum - COUNT_MEM_BLOCKS);
            if(Block2)
                this.DeleteBlockFromHashTree(Block2)
            this.AddBlockToHashTree(Block)
        }
        if(arrContentResult.length)
            process.send({cmd:"WriteBodyResult", BlockNum:Block.BlockNum, arrContentResult:arrContentResult})
        for(var key in DApps)
        {
            DApps[key].OnWriteBlockFinish(Block)
        }
    }
    BlockDeleteTX(Block)
    {
        this.BufHashTree.LastAddNum = 0
        for(var key in DApps)
        {
            DApps[key].OnDeleteBlock(Block)
        }
    }
    OnDelete(Block)
    {
    }
    IsValidTicket(Tr, BlockNum)
    {
        this.CheckCreateTicketObject(Tr, BlockNum)
        if(Tr.power < MIN_POWER_POW_TR)
            return  - 2;
        if(Tr.num !== BlockNum)
            return  - 3;
        return 1;
    }
    IsValidTransaction(Tr, BlockNum)
    {
        if(!Tr.body || Tr.body.length < MIN_TRANSACTION_SIZE || Tr.body.length > MAX_TRANSACTION_SIZE)
            return  - 1;
        this.CheckCreateTransactionObject(Tr)
        if(Tr.power - Math.log2(Tr.body.length / 128) < MIN_POWER_POW_TR)
            return  - 2;
        if(Tr.num !== BlockNum)
            return  - 3;
        if(Tr.body[0] === TYPE_TRANSACTION_ACC_HASH)
            return  - 4;
        return 1;
    }
    ReWriteDAppTransactions(Length)
    {
        if(!TX_PROCESS.Worker)
            return 0;
        if(!Length)
            return 0;
        var StartNum = this.BlockNumDB - Length + 1;
        if(StartNum < 0)
            StartNum = 0
        var EndNum = this.BlockNumDB;
        var MinBlock = DApps.Accounts.GetMinBlockAct();
        if(MinBlock > StartNum)
        {
            ToLog("Cant rewrite transactions. Very long length of the rewriting chain. Max length=" + (this.BlockNumDB - MinBlock))
            return 0;
        }
        TX_PROCESS.Worker.send({cmd:"ReWriteDAppTransactions", StartNum:StartNum, EndNum:EndNum})
        return 1;
    }
    AddDAppTransactions(BlockNum, Arr)
    {
        if(BlockNum % PERIOD_ACCOUNT_HASH !== 0)
            return ;
        var BlockNumHash = BlockNum - DELTA_BLOCK_ACCOUNT_HASH;
        if(BlockNumHash < 0)
            return ;
        var Hash = DApps.Accounts.GetHashOrUndefined(BlockNumHash);
        if(Hash)
        {
            var Body = [TYPE_TRANSACTION_ACC_HASH];
            WriteUintToArr(Body, BlockNumHash)
            WriteArrToArr(Body, Hash, 32)
            var Tr = {body:Body};
            this.CheckCreateTransactionObject(Tr)
            Arr.unshift(Tr)
        }
        else
        {
        }
    }
    AddTransactionOwn(Tr)
    {
        if(!global.TX_PROCESS.Worker)
            return 0;
        var StrHex = GetHexFromArr(sha3(Tr.body));
        global.TX_PROCESS.Worker.send({cmd:"FindTX", TX:StrHex})
        return this.AddTransaction(Tr, 1);
    }
};
