using System;
using System.Diagnostics;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Console = HyperCube.Utils.AdvConsole;
using System.Text.RegularExpressions;
using QRCoder;
using System.IO;
using Microsoft.AspNetCore.Components;

//0xe5D682717955d6C35d465A3485625C64655a04f4 - HCB in rinkeby
//0xb504ba124b74333d8536db534f7fcdc174d6ee3d - system address rinkeby
//0x413D9500A675d9b645034aC5f4325BF12ddeb7c1 bitcoin


namespace HyperCube.Models
{
    public struct TransactionObject
    {
        public string from;
        public string to;
        public string data;
        public string gas;
        public string value;
    }

    public class Blockchain
    {
        [Inject]
        AppData AppData { get; set; }
        public static Blockchain Dev
        {
            get {
                return loaded[0];
            }
            set { }
        }
        public static string tokenName;
        public static string decimals;
        public static string symbol;
        public static string tokenBalance;
        public static string balanceHCB;

        public static Dictionary<int, Blockchain> supported;
        //public static string Connected
        //{
        //    get
        //    {
        //        var bc = AppData.CurrentAccount.GetSelectedBlockChain(); //acc selected
        //        if (bc != null)
        //            return $"{bc.name}";
        //        return "none";
        //    }
        //}
        public static string URLdefault = "127.0.0.1";
        //public static int defaultPort = 8545;
        public static int defaultPort = 8666;
        public static string defaultName = "Ethereum dev network";

        public static bool blockChainsInitialized = false;
        public static Dictionary<int, Blockchain> loaded = new();
        public Dictionary<int, SmartContract> contracts = new();
        public Dictionary<string, SmartContract> contractNames = new();
        public int id;
        public int port;
        public string url;
        public string address = "";
        public string name;
        public string balance;
        static Blockchain instance;

        public async static Task RegisterNetworks()
        {
            if (!blockChainsInitialized)
            {
                Console.WriteLine($"RegisterNetworks");
                var ethDev = new Blockchain(0, "Ethereum Dev Network", "127.0.0.1", 8545);
                var ethRinkeby = new Blockchain(1, "Ethereum Test Network", "127.0.0.1", 8666);
                await ethDev.Initialize();
                await ethRinkeby.Initialize();

                blockChainsInitialized = true;
            }
        }

        public static Blockchain Find(int id)
        {
            if (loaded.ContainsKey(id))
                return loaded[id];
            return null;
        }


        public async Task<string> GetBalanceToken(string contractAddress, string address)
        {
            TransactionObject transObj = new TransactionObject();
            transObj.to = contractAddress;

            //compile data
            //to.data = await compileFunction("decimals");
            //transObj.data = await compileFunction("name");
            //var answer = await RunFunction2("eth_call", transObj, "latest");
            ////Console.WriteLine("ImportERC20 len "+ answer.Length + " answer " + answer);
            //var parsed = ParseStringAnswer(answer);
            //int tokenlen = hex2dec(parsed[1]);
            //tokenName = HextoString(parsed[2]).Substring(0, tokenlen);
            //to.data = await compileFunction("balanceOf(address)");
            transObj.data = await compileFunction("symbol");
            var answer = await RunFunction2("eth_call", transObj, "latest");
            var parsed = ParseStringAnswer(answer);
            if (parsed.Length == 0)
                return null;
            int symlen = hex2dec(parsed[1]);
            symbol = HextoString(parsed[2]).Substring(0, symlen);
            //Console.WriteLine("ImportERC20 len " + answer.Length + " answer " + answer);
            transObj.data = await compileFunction("decimals");
            answer = await RunFunction2("eth_call", transObj, "latest");
            decimals = hex2dec(ParseStringAnswer(answer)[0]).ToString();

            transObj.data = await compileFunction($"function balanceOf(address account)");
            if (address != null)
                transObj.data += zerofill(address, 64, true);
            //answer = await RunFunction2("eth_call", to, AccountModel.Current.GetActualAddress(this));
            answer = await RunFunction2("eth_call", transObj, "latest");
            balanceHCB = ((double)AccountModel.ConvertBalance(ParseStringAnswer(answer)[0]) / 1000000000000000000.0).ToString();
            return null;
        }

        public async Task<string> Initialize()
        {
            if (!loaded.ContainsKey(id))
                loaded.Add(id, this);
            Console.WriteLine($"Initialize: {name},  loaded blockchains " + loaded.Count);
            string addr = await ListAccounts();

            await LoadContracts();
            Console.WriteLine("LoadContracts count " + contracts.Count);
            //Console.WriteLine("connected " + Connected);
            return $"{name} {url}:{port}";
        }

        public async Task ChangeOwner(string ERC20Address, string contractAddress)            
        {
            TransactionObject transObj = new TransactionObject();
            transObj.from = address;
            transObj.to = ERC20Address;// "0xe5D682717955d6C35d465A3485625C64655a04f4";
            transObj.gas = "9000";
            transObj.value = contractAddress;
            transObj.data = await compileFunction($"function transferOwnership(address newOwner)");
            var answer = await RunFunction2("eth_sendTransaction", transObj);
        }


        public async Task<string> Verify(AccountModel verifier, ArticleModel article)
        {
            Console.WriteLine($"Verify starting");
            try
            {
                var bc = await verifier.GetSelectedBlockChain();
                VerifyContract verifyContract = SmartContract.Find("Verify", bc) as VerifyContract;
                if (verifyContract != null)
                {
                    Console.WriteLine($"VerifyContract found");
                    var transactionId = await verifyContract.Run(verifier, article);
                    return transactionId;
                }
                else
                    Console.WriteLine($"VerifyContract null");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message + "stack trace" + e.StackTrace);
            }
            return "Verify failed";
        }

        public static string dec2hex(int decValue, bool prefix = true)
        {
            string hexValue = decValue.ToString("X");
            if (prefix)
                hexValue = "0x" + hexValue;
            return hexValue;
        }

        public static int hex2dec(string hexValue)
        {
            return Convert.ToInt32(hexValue, 16);
        }

        //public static Blockchain GetInstance()
        //{
        //    if (instance == null)
        //        instance = new Blockchain(URLdefault, defaultPort);
        //    return instance;
        //}

        //Blockchain()
        //{
        //}

        public Blockchain(int id, string name, string url, int port)
        {
            this.id = id;
            this.url = url;
            this.port = port;
            this.name = name;
        }

        public async Task<string> CreateBlockchainAccount(AccountModel account, string pass)
        {
            Console.WriteLine($"Run CreateBlockchainAccount {account}");
            var res = await RunFunction("personal_newAccount", $"\"{pass}\"");
            string query;
            if (account != null)
            {
                Console.WriteLine($"CreateBlockchainAccount {account}: {res}");
                //query = $"update aspnetusers set eth_address='{res}' where UserName='{account.Name}'";
                var addr = account.GetActualAddress(this);
                if (addr == null)
                    query = $"insert into account_wallets (uuid, blockchain_id, account_uuid, password_plain, added) values ('{res}',{id},'{account.UUID}','{account.PWDHash}', NOW())";
                else
                    query = $"update account_wallets set uuid='{res}', password_plain='{account.PWDHash}', added = NOW() where account_uuid='{account.UUID}' and blockchain_id={id}";
                account.SetActualAddress(res, this);
                Console.WriteLine($"CreateBlockchainAccount query {query}");
                await MySQLConnector.Instance().SQLInsert(query);
            }
            else
                Console.WriteLine($"CreateBlockchainAccount {id}: {res}");
            address = res;
            if (account == null)
            {
                query = $"update blockchains set address_main='{res}' where id={id}";                
                Console.WriteLine($"CreateBlockchainAccount query {query}");
                await MySQLConnector.Instance().SQLInsert(query);
            }
            return res;
        }

        public static string hex2bin(string hexstring)
        {
            string binarystring = String.Join(String.Empty,  hexstring.Select(    c => Convert.ToString(Convert.ToInt32(c.ToString(), 16), 2).PadLeft(4, '0')  ));
            Console.WriteLine("bin " + binarystring);
            return binarystring;
        }

        public static string bin2hex(string code)
        {
            //code = "0x".bin2hex('getCount()');
            var bytes = Encoding.UTF8.GetBytes(code);
            var hex = BitConverter.ToString(bytes).Replace("-", "");
            Console.WriteLine("hex " + hex);
            return "0x" + hex;
        }

        public static string zerofill(string hex, int charCount, bool hexPrefix0x)
        {
            if (hexPrefix0x)
                hex = hex.Remove(0, 2);
            Console.WriteLine($"zerofill input {hex} count {hex.Length}");
            var res = hex.PadLeft(charCount, '0');
            Console.WriteLine($"zerofill out {hex} count {res.Length}");
            return res;
        }

        public async Task<string> ListAccounts()
        {
            Console.WriteLine($"ListAccounts blockchain {id}");
            string answer = "no";
            //{ "jsonrpc":"2.0","method":"eth_getCode","params":["0x938cae6f6c21ed9d55196e96ef880f562e530553", "latest" ],"id":1}
            //string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x874c6a51e62680d4594cd2555ed8fa47b63ed253\",\"latest\"],\"id\":1}";
            string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_accounts\", \"params\":[],\"id\":1}";

            answer = await Post.PostRequestAsync(this, req);

            //string json = Encoding.UTF8.GetString(bytedata, 1, bytedata.Length - 1);
            //try
            //{
            Console.WriteLine("Json string " + answer);
            dynamic jsonDe = JsonConvert.DeserializeObject(answer);
            var res = (Newtonsoft.Json.Linq.JArray)jsonDe.result;
            if (res.Count > 0 && id == 0)   //Dev
            {
                address = jsonDe.result[0];
            }
            else
            {
                address = await GetAddress();
                if (address == "" || address == null)
                    address = await CreateEthAddress("test_Password_212");
            }
            var query = $"update blockchains set address_main='{address}' where id={id}";
            Console.WriteLine("query " + query);
            await MySQLConnector.Instance().SQLInsert(query);
            Console.WriteLine("Json addr " + jsonDe.result[0]);
            return address;
        }

        public async Task<string> CreateEthAddress(string pass)
        {
            return await CreateBlockchainAccount(null, pass);
        }

        public async Task LoadContracts()
        {
            MySQLConnector dbCon = MySQLConnector.Instance();
            contracts = await dbCon.SQLSelectContracts(id);
            foreach (var contract in contracts.Values)
            {
                await contract.LoadFunction();
            }
        }

        public async Task<string> GetSHA3(string code)
        {
            var hex = bin2hex(code);
            var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"web3_sha3\",\"params\":[\"{hex}\"], \"id\":1}}";

            var answer = await Post.PostRequestAsync(this, req);
            dynamic jsonDe = JsonConvert.DeserializeObject(answer);

            Console.WriteLine("result " + jsonDe.result);
            return jsonDe.result;
        }

        public async Task<string> GetBalance(AccountModel account)
        {
            var addr = await account.GetOrCreateActualAddress(this);
            //var ret = await RunFunction("eth_getBalance", $"\"{addr}\",\"latest\"");
            Console.WriteLine($"GetBalance AccountModel addr {addr}");
            return await GetBalance(addr);
        }

        public async Task<string> GetBalance()
        {
            return await GetBalance(address);
        }
        //eth.getBalance('0xb504ba124b74333D8536DB534F7fcdC174d6Ee3d')

        public async Task<string> GetBalance(string address)
        {
            var ret = await RunFunction("eth_getBalance", $"\"{address}\",\"latest\"");
            var balanceWei = AccountModel.ConvertBalance(ret);
            var balanceToken = (double)balanceWei / 1000000000000000000.0;
            Console.WriteLine($"GetBalance balanceToken {balanceToken}");
            balance = balanceToken.ToString();
            return balance;
        }
        
        public async Task<string> RunFunction(string name, string parms)
        {
            Console.WriteLine($"RunFunction {name} params {parms}");
            var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"{name}\",\"params\":[{parms}], \"id\":1}}";

            var answer = await Post.PostRequestAsync(this, req);
            dynamic jsonDe = JsonConvert.DeserializeObject(answer);

            Console.WriteLine("RunFunction result " + jsonDe.result);
            return jsonDe.result;
            //return "test answer";
        }

        public async Task<string> RunFunction2(string name, TransactionObject to, string defaultBlock = null)
        {
            //транзакция добавления контракта 0x00bdbf1571aa69a2cf8c8c7913a01eaca3a4b7e5e7e49fc787cb81c0df2c2682
            Dictionary<string, string> paramDict = new Dictionary<string, string>();
            string parms = "{";
            if (to.from != null)
                paramDict["from"] = to.from;
            if (to.to != null)
                paramDict["to"] = to.to;
            if (to.data != null)
            {
                paramDict["data"] = "0x"+to.data;
            }
            int count = 0;
            foreach (var p in paramDict)
            {
                parms += $"\"{p.Key}\":\"{p.Value}\"";
                count++;
                if (paramDict.Count > count)
                {
                    parms += ",";
                }
            }
            parms += "}";
            if (defaultBlock != null)
                parms += $", \"{defaultBlock}\"";
            Console.WriteLine($"RunFunction {name} params {parms} active bc: {this.address}");
            var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"{name}\",\"params\":[{parms}], \"id\":1}}";

            var answer = await Post.PostRequestAsync(this, req);
            dynamic jsonDe = JsonConvert.DeserializeObject(answer);

            Console.WriteLine("RunFunction result " + jsonDe.result);
            return jsonDe.result;
            //return "test answer";
        }

        public static string QRtest(string input)
        {
            QRCodeGenerator qrGenerator = new QRCodeGenerator();
            QRCodeData qrCodeData = qrGenerator.CreateQrCode(input, QRCodeGenerator.ECCLevel.Q);
            QRCode qrCode = new QRCode(qrCodeData);
            Bitmap qrCodeImage = qrCode.GetGraphic(7);
            //Image img = qrCodeImage;

            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);
            //var fulltexname = Encoding.UTF8.GetBytes("http://" + myHostName + "/" + filename);

            //bw.Write(loc_id);
            //bw.Write(fulltexname);

            
            //return base64String;            
            qrCodeImage.Save(ms, ImageFormat.Png);
            byte[] imageBytes = ms.ToArray();
            string base64String = Convert.ToBase64String(imageBytes);
            Console.WriteLine($"base64String {base64String}");
            return base64String;
            //Image.FromStream(ms);
        }

        public static List<string> ExecuteCommand(string cmd)
        {
            List<string> output = new List<string>();            
            var processInfo = new ProcessStartInfo(@"e:\www\nodejs\test.bat", cmd );
            processInfo.CreateNoWindow = true;
            processInfo.UseShellExecute = false;
            processInfo.RedirectStandardError = true;
            processInfo.RedirectStandardOutput = true;

            var process = Process.Start(processInfo);

            process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Echo(e.Data, ref output);
            process.BeginOutputReadLine();

            process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
                Console.WriteLine("error>>" + e.Data);
            process.BeginErrorReadLine();

            process.WaitForExit();

            Console.WriteLine($"ExitCode: {process.ExitCode}");
            num = 0;
            process.Close();
            return output;
        }

        static int num = 0;
        
        static void Echo(string msg, ref List<string> output)
        {
            //if (num > 0)
            if (msg != null && msg.Length > 0)
            {
                Console.WriteLine("output >>" + msg);
                output.Add(msg);
            }
            num++;
        }

        async Task<string> mySHA3(string code)
        {
            var res = await GetSHA3(code);
            Console.WriteLine($"GetSHA3 {code} {res}");
            var ret = res.Substring(2, 8);
            return ret;
        }

        public async Task<string> compileFunction(string input, bool getsha = true)
        {
            //\s = пробел
            //\S = любой символ, кроме промельного
            //+ одно или более вхождений
            //. любой сивол, кроме перевода строки
            //* ноль или более вхождений «а»
            //скобки без экранирования = группы
            //function transfer(address recipient, uint256 amount) 
            string pattern = @"function\s+(\S+)\s*\((.*)\).*";
            string replacement_name = "$1"; //номер найденной группы
            string replacement_params = "$2";
            string resultname = Regex.Replace(input, pattern, replacement_name).Trim();
            string param_pattern = @"(uint8|address|uint256)";
            string final = resultname + "(";
            if (input != resultname)
            {
                string resultparams = Regex.Replace(input, pattern, replacement_params).Trim();
                if (input != resultparams)
                {
                    int i = 0;
                    foreach (Match match in Regex.Matches(resultparams, param_pattern))
                    {
                        if (i > 0)
                            final += "," + match.Value;
                        else
                            final += match.Value;
                        i++;
                    }
                }
            }
            string function_processed = final + ")";            
            Console.WriteLine($"function_processed {function_processed}");
            if (getsha)
            {
                function_processed = await mySHA3(function_processed);
            }
            return function_processed;
        }

        //public double UInt256Conv(string input)
        //{
        //    System.Numerics.BigInteger bigint1 = System.Numerics.BigInteger.Parse("329974300448100608374211110737048701521");
        //    System.Numerics.BigInteger bigint2 = new System.Numerics.BigInteger(18);
        //    return bigint1 / 18.0;
        //}

        public static string HextoString(string InputText)
        {

            byte[] bb = Enumerable.Range(0, InputText.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(InputText.Substring(x, 2), 16))
                             .ToArray();
            //return System.Text.Encoding.ASCII.GetString(bb);
            // or System.Text.Encoding.UTF7.GetString
            return System.Text.Encoding.UTF8.GetString(bb);
            // or System.Text.Encoding.Unicode.GetString
            // or etc.
        }

        public string[] ParseStringAnswer(string answer)
        {
            var ret = new string[] { };
            if (answer.Length > 2)
            {
                answer = answer.Substring(2, answer.Length - 2);
                int words = answer.Length / 64;
                ret = new string[words];
                for (int i = 0; i < words; i++)
                {
                    var word = answer.Substring(i * 64, 64);
                    ret[i] = word;
                    Console.WriteLine($"word {i} {word}");
                    Console.WriteLine($"str {i} {HextoString(word)}");
                    var testbalance1 = (double)(AccountModel.ConvertBalance(word)/ 1000000000000000000);
                    var testbalance2 = ((double)AccountModel.ConvertBalance(word) / 1000000000000000000.0);
                    Console.WriteLine($"testbalance1 {testbalance1} testbalance2 {testbalance2} ");
                }
            }
            return ret;
        }

        //верификация запускает запрос на минт/перевод токенов от системы
        //количество вычисляется по конечной ценности, переданной в смарт и курсу к $$$$???
        //комиссия владельцам
        //4.  Рабочий стол. Верификация
        //7.  Календарь событий - тут добавление мероприятия запускает транзакции спонсирования
        //8.  Персональные данные - роль
        //13.  Цифровые активы
        //14.  Блокчейн

        //накапать токенов эксперту и автору материала

        //0. делаем новый контракт с массивом ролей
        //1. делаем в нем управляющий код с подтверждениями на выпуск токенов
        //2. в рамках текущего токена - делаем овнером токена мульти контракт с ролями
        //3. этим же контрактом запускаем verify и другие контракты - функции
        //4. что должен фиксировать Verify?
        //5. как и где определять параметры запускаемых функций во внешних контрактах
        //6. определение курса токенов

        public async Task Mint(string tokenAmount, string addressTo)
        {
            //mint(address to, uint256 amount) 
            TransactionObject transObj = new TransactionObject();
            transObj.from = address;
            transObj.to = "0xe5D682717955d6C35d465A3485625C64655a04f4";
            transObj.gas = "9000";
            transObj.value = tokenAmount;
            transObj.data = await compileFunction($"function mint(address to, uint256 amount)");
            if (addressTo != null && tokenAmount != null)
            {
                transObj.data += zerofill(addressTo, 64, true);
                transObj.data += zerofill(dec2hex(Convert.ToInt32(tokenAmount)), 64, true);
            }
            var answer = await RunFunction2("eth_sendTransaction", transObj);
        }

        public async Task TransferToken(string sender, string tokenContract, string addressTo, string tokenAmount)
        {
            //transfer(address recipient, uint256 amount)
            TransactionObject transObj = new TransactionObject();
            transObj.from = sender;
            transObj.to = tokenContract;
            transObj.gas = "9000";
            transObj.value = tokenAmount;
            transObj.data = await compileFunction($"function transfer(address recipient, uint256 amount)");
            if (addressTo != null && tokenAmount != null)
            {
                transObj.data += zerofill(addressTo, 64, true);
                transObj.data += zerofill(dec2hex(Convert.ToInt32(tokenAmount)), 64, true);
            }
            var answer = await RunFunction2("eth_sendTransaction", transObj);            
        }

        public async Task ImportERC20(string contractAddress, string importAddress)
        {
            //"0xb504ba124b74333D8536DB534F7fcdC174d6Ee3d"
            //var gbh = await GetTByHash("0x00bdbf1571aa69a2cf8c8c7913a01eaca3a4b7e5e7e49fc787cb81c0df2c2682");
            //Console.WriteLine("GetTByHash " + gbh );
            //runfunction balanceOf totalSupply symbol name
            //RunContractRead
            TransactionObject transObj = new TransactionObject();
            transObj.to = contractAddress;

            //compile data
            //to.data = await compileFunction("decimals");
            transObj.data = await compileFunction("name");
            var answer = await RunFunction2("eth_call", transObj, "latest");
            //Console.WriteLine("ImportERC20 len "+ answer.Length + " answer " + answer);
            var parsed = ParseStringAnswer(answer);
            int tokenlen = hex2dec(parsed[1]);
            tokenName = HextoString(parsed[2]).Substring(0,tokenlen);
            //to.data = await compileFunction("balanceOf(address)");
            transObj.data = await compileFunction("symbol");
            answer = await RunFunction2("eth_call", transObj, "latest");
            parsed = ParseStringAnswer(answer);
            int symlen = hex2dec(parsed[1]);
            symbol = HextoString(parsed[2]).Substring(0, symlen);
            //Console.WriteLine("ImportERC20 len " + answer.Length + " answer " + answer);
            transObj.data = await compileFunction("decimals");
            answer = await RunFunction2("eth_call", transObj, "latest");
            decimals = hex2dec(ParseStringAnswer(answer)[0]).ToString();
            
            transObj.data = await compileFunction($"function balanceOf(address account)");
            if (address != null)
                transObj.data += zerofill(importAddress, 64, true);
            //answer = await RunFunction2("eth_call", to, AccountModel.Current.GetActualAddress(this));
            answer = await RunFunction2("eth_call", transObj, "latest");
            tokenBalance = ((double)AccountModel.ConvertBalance(ParseStringAnswer(answer)[0]) / 1000000000000000000.0).ToString();
           
        }

        public async Task<string> BalanceOf(string contractAddress, string holderAddress)
        {
            TransactionObject transObj = new TransactionObject();
            transObj.to = contractAddress;
            transObj.data = await compileFunction($"function balanceOf(address account)");
            if (address != null)
                transObj.data += zerofill(holderAddress, 64, true);
            //answer = await RunFunction2("eth_call", to, AccountModel.Current.GetActualAddress(this));
            var answer = await RunFunction2("eth_call", transObj, "latest");
            tokenBalance = ((double)AccountModel.ConvertBalance(ParseStringAnswer(answer)[0]) / 1000000000000000000.0).ToString();
            return tokenBalance;
        }

        public async void RunContractRead()
        {
            string answer = "no";
            string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{\"to\":\"0x874c6a51e62680d4594cd2555ed8fa47b63ed253\", \"data\":\"0xa87d942c\"},\"latest\"],\"id\":1}";
            //string req = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"0x0000000000000000000000000000000000000000000000000000000000000004\"}";
            //Console.WriteLine($"req " + req);
            await Post.PostRequestAsync(this, req);
            //Console.WriteLine($"answer {answer} len {answer.Length}" );
            address = answer;
        }

        public async Task<string> SendTransaction(string fromAddress, string toAddress, int wei)
        {
            var sum = dec2hex(wei);
            var gas = await GetEstimatedGasTransaction(wei);
            Console.WriteLine($"SendTransaction from {fromAddress} to {toAddress} sum {sum}");
            var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{toAddress}\",\"gas\":\"{gas}\", \"data\":\"\", \"value\":\"{sum}\"}}");
            return answer;
        }

        //{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{ "from":"0xD81eeE6b39d9556c3067A3551A3FB2882b92F327", "to":"0x119b58faddcdbc09cafcd272530aa079cec10004", "gas":"0x31b2ef", "data":"0x11111111"}], "id":1}
        public async Task<string> RunContractWrite(string contractAddress, string data, string gas, string value)
        {
            Console.WriteLine("RunContract contractAddress " + contractAddress);
            var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"{gas}\", \"data\":\"{data}\", \"value\":\"{value}\"}}");
            //$"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"0x31b2ef\", \"data\":\"{data}\"}}], \"id\":1}}";
            //var answer = await Post.PostRequestAsync(req);
            //dynamic jsonDe = JsonConvert.DeserializeObject(answer);

            //Console.WriteLine("result " + answer);
            return answer;
        }

        public async Task<string> GetTByHash(string transactionAddress)
        {
            string req = $"{{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionByHash\",\"params\":[\"{transactionAddress}\"],\"id\":1}}";
            Console.WriteLine("GetTByHash " + req);
            return await Post.PostRequestAsync(this, req);
        }

        public async Task<string> GetReceipt(string transactionAddress, bool returnAddress = false)
        {
            Console.WriteLine("transactionAddress " + transactionAddress);
            var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"{transactionAddress}\"], \"id\":1}}";

            var answer = await Post.PostRequestAsync(this, req);

            dynamic jsonDe = JsonConvert.DeserializeObject(answer);
            if (jsonDe.result != null)
            {
                var blockHash = jsonDe.result.blockHash;
                var blockNumber = jsonDe.result.blockNumber;
                var contractAddress = jsonDe.result.contractAddress;
                await MySQLConnector.Instance().SQLInsert($"insert into transactions (result, name) values ('{Convert.ToString(jsonDe.result)}', 'eth_getTransactionReceipt')");
                Console.WriteLine("result " + answer);
                if (returnAddress)
                    return contractAddress;
                else
                    return Convert.ToString(jsonDe.result);
            }
            else
            {
                Console.WriteLine("eth_getTransactionReceipt reuslt NULL " + transactionAddress);
                return null;
            }
        }

        //TODO add GAS!!!
        public async Task<object[]> AddContract(string name, string code, string bytecode)
        {
            Console.WriteLine("bytecode " + bytecode);
            var gas = await GetEstimatedGasContractAdd(bytecode);
            var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{{\"from\":\"{address}\",\"gas\":\"{gas}\", \"data\":\"{bytecode}\"}}], \"id\":1}}";

            var answer = await Post.PostRequestAsync(this, req);
            dynamic jsonDe = JsonConvert.DeserializeObject(answer);
            var res = jsonDe.result;
            Console.WriteLine("result AddContract transactionAddress: " + res);
            if (res != null)
            {                
                SmartContract newctr = new SmartContract(name, code, bytecode, this.id, gas);
                
                if (gas == null)
                    gas = "invalid";
                long id = await MySQLConnector.Instance().SQLInsert($"insert into smart_contracts (code, bytecode, name, date_add, gas_cost, blockchain_id) values ('{code}','{bytecode}','{name}',NOW(), '{gas}', {this.id})");
                newctr.ID = (int) id;
                contracts.Add((int)id, newctr);
                contractNames.Add(name, newctr);
                return new object[] { res, newctr };
            }
            return null;
        }

        public async Task<string> GetEstimatedGasTransaction(int weiAmount)
        {
            if (weiAmount > 0)
            {
                var ret = await RunFunction("eth_estimateGas", $"{{\"from\":\"{address}\",\"value\":\"{dec2hex(weiAmount)}\"}}");
                Console.WriteLine("GetEstimatedGas " + ret);
                return ret;
            }
            return null;
        }

        public async Task<string> GetEstimatedGasContractAdd(string bytecode)
        {
            var ret = await RunFunction("eth_estimateGas", $"{{\"from\":\"{address}\",\"data\":\"{bytecode}\"}}");
            Console.WriteLine("GetEstimatedGasContractAdd " + ret);
            return ret;
        }

        public async Task<string> GetAddress()
        {
            if (address == "")
            {
                address = await MySQLConnector.Instance().SQLSelectUUID("select address_main from blockchains where id="+id);
            }
            return address;
            //post запрос
        }
    }
}