package dcrcash

import (
	"dcrcash/dcrcashRpc"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"strconv"
	"time"
)

type DcrCashApi struct {
}

func (m *DcrCashApi) InitRpcConf(rpcuser string, rpcpassword string, rpchost string, rpcport int) bool {
	dcrcashRpc.InitRpcConf(rpcuser, rpcpassword, rpchost, rpcport)
	return true
}

//get a new taddr
func (m *DcrCashApi) WalletNewAddr() (string, error) {
	var newaddr RpcResult
	r := rand.NewSource(time.Now().UnixNano())
	label := strconv.FormatInt(r.Int63(), 10)
	fmt.Println(label)
	dcrcashRpc.RunCall("walletpassphrase", Config.Walletpass.Passphrase, 3)
	dcrcashRpc.RunCall("createnewaccount", label)
	//time.Sleep(time.Second * 1)
	result, err := dcrcashRpc.RunCall("getnewaddress", label)
	if err != nil {
		fmt.Println("Call getnewaddress fail:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &newaddr); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}

	// success
	if len(newaddr.Error.Message) == 0 {
		return newaddr.Result, nil
	}

	err = errors.New(newaddr.Error.Message)
	return "", err
}

//get PrivKey by taddr
func (m *DcrCashApi) WalletDumpPrivKey(address string) (string, error) {

	var key RpcResult

	if len(address) == 0 {
		fmt.Println("input address err:")
		err := errors.New("input address error:")

		return "", err
	}

	//
	result, err := dcrcashRpc.RunCall("dumpprivkey", address)
	if err != nil {
		fmt.Println("Call dumpprivkey fail:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &key); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}

	// success
	if len(key.Error.Message) == 0 {
		return key.Result, nil
	}
	err = errors.New(key.Error.Message)
	return "", err
}

func (m *DcrCashApi) WalletInfo() ([]byte, error) {
	var walletinfoResult WalletinfoResult
	result, err := dcrcashRpc.RunCall("walletinfo")

	if err != nil {
		fmt.Println("Call walletinfo fail:", err.Error())
		return nil, err
	}
	fmt.Println("walletinfo:", string(result))
	if err := json.Unmarshal([]byte(result), &walletinfoResult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return nil, err
	}

	if len(walletinfoResult.Error.Message) == 0 {
		var jbuf []byte
		WalleTxfee = walletinfoResult.Result.TxFee
		fmt.Println(WalleTxfee)
		if jbuf, err = json.Marshal(walletinfoResult.Result); err != nil {
			return nil, err
		}
		return jbuf, nil
	}
	err = errors.New(walletinfoResult.Error.Message)
	return nil, err
}

func (m *DcrCashApi) WalletSetTxFee(amount float64) (bool, error) {
	var settxfee Setfeeresult

	result, err := dcrcashRpc.RunCall("settxfee", amount)
	if err != nil {
		fmt.Println("Call settxfee fail:", err.Error())
		return false, err
	}

	if err := json.Unmarshal([]byte(result), &settxfee); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return false, err
	}

	if len(settxfee.Error.Message) == 0 {
		return settxfee.Result, nil
	}

	err = errors.New(settxfee.Error.Message)
	return false, err
}

func (m DcrCashApi) WalleGetBlockCount() (uint64, error) {

	var blockcount Blockcountresult

	//
	result, err := dcrcashRpc.RunCall("getblockcount")

	if err != nil {
		fmt.Println("Call getblockcount fail:", err.Error())
		return 0, err
	}

	if err := json.Unmarshal([]byte(result), &blockcount); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return 0, err
	}

	// success
	if len(blockcount.Error.Message) == 0 {
		return uint64(blockcount.Result), nil
	}
	err = errors.New(blockcount.Error.Message)
	return 0, err
}

//get the server's total available balance
func (m DcrCashApi) WalleGetBalance(account string) ([]byte, error) {

	var liteBalance BalanceResult

	// getbalance all
	result, err := dcrcashRpc.RunCall("getbalance", account)
	if err != nil {
		fmt.Println("Call getbalance fail:", err.Error())
		return nil, err
	}

	if err := json.Unmarshal([]byte(result), &liteBalance); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return nil, err
	}
	fmt.Println("WalleGetBalance:", len(liteBalance.Result.Balances))
	// success
	if len(liteBalance.Error.Message) == 0 {
		var jbuf []byte
		if jbuf, err = json.Marshal(liteBalance.Result.Balances); err != nil {
			return nil, err
		}
		return jbuf, nil
	}
	err = errors.New(liteBalance.Error.Message)
	return nil, err
}

//get block hash by index
func (m DcrCashApi) WalletGetBlockHash(index uint64) (string, error) {

	var Blockhash RpcResult

	//get block hash by index
	result, err := dcrcashRpc.RunCall("getblockhash", index)
	if err != nil {
		fmt.Println("Call getblockhash fail:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &Blockhash); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}

	// success
	if len(Blockhash.Error.Message) == 0 {
		return Blockhash.Result, nil
	}
	err = errors.New(Blockhash.Error.Message)
	return "", err
}

//get block time by hash;return:blocktime
func (m DcrCashApi) WalletGetBlockTime(blockhash string) (uint64, error) {

	var blockresult BlockResult

	if len(blockhash) == 0 {
		fmt.Println("input para err:")
		err := errors.New("input para err")
		return 0, err
	}
	//get block info by blockhash
	result, err := dcrcashRpc.RunCall("getblock", blockhash)
	if err != nil {
		fmt.Println("Call getblock fail:", err.Error())
		return 0, err
	}

	if err := json.Unmarshal([]byte(result), &blockresult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return 0, err
	}

	// success
	if len(blockresult.Error.Message) == 0 {

		return uint64(blockresult.Result.Time), nil
	}
	err = errors.New(blockresult.Error.Message)
	return 0, err
}

func (m *DcrCashApi) WalleListSinceBlock(blockhash string) ([]byte, error) {

	var listtransactionsresult Listtransactionsresult

	if len(blockhash) == 0 {
		fmt.Println("input blockhash err:")
		err := errors.New("input blockhash err")
		return nil, err
	}

	//
	result, err := dcrcashRpc.RunCall("listsinceblock", blockhash)
	fmt.Println("WalleListSinceBlock Result:", string(result))
	if err != nil {
		fmt.Println("Call listsinceblock fail:", err.Error())
		return nil, err
	}

	if err := json.Unmarshal([]byte(result), &listtransactionsresult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return nil, err
	}

	// success
	if len(listtransactionsresult.Error.Message) == 0 {

		var jbuf []byte
		if jbuf, err = json.Marshal(listtransactionsresult.Result.Transactions); err != nil {
			return nil, err
		}
		return jbuf, nil
	}
	err = errors.New(listtransactionsresult.Error.Message)
	return nil, err
}

//get block Height by hash;return:blocktime
func (m DcrCashApi) WalletGetBlockHeight(blockhash string) (uint64, error) {

	var blockresult BlockResult

	if len(blockhash) == 0 {
		fmt.Println("input para err:")
		err := errors.New("input para err")
		return 0, err
	}
	//get block info by blockhash
	result, err := dcrcashRpc.RunCall("getblock", blockhash)
	if err != nil {
		fmt.Println("Call getblock fail:", err.Error())
		return 0, err
	}

	if err := json.Unmarshal([]byte(result), &blockresult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return 0, err
	}

	// success
	if len(blockresult.Error.Message) == 0 {

		return uint64(blockresult.Result.Height), nil
	}
	err = errors.New(blockresult.Error.Message)
	return 0, err
}

//Sent an amount from an account to a zcash address.
//The amount is a real and is rounded to the nearest 0.00000001.
//Result:"transactionid" (string) The transaction id.
func (m *DcrCashApi) WalleSendTo(toaddress string, amount float64, comment, commentto string) (string, error) {

	var SendResult RpcResult

	if len(toaddress) == 0 {
		fmt.Println("input toaddress err:")
		err := errors.New("input toaddress err")
		return "", err
	}
	//fmt.Println("config.WalletInfo.Pripassphrase:", Config.Walletpass.Passphrase)
	dcrcashRpc.RunCall("walletpassphrase", Config.Walletpass.Passphrase, 3)
	result, err := dcrcashRpc.RunCall("sendtoaddress", toaddress, amount, "", "")
	fmt.Println("WalleSendTo>>>>>>>>", string(result))
	if err != nil {
		fmt.Println("Call sendfrom fail:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &SendResult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}

	// success
	if len(SendResult.Error.Message) == 0 {
		return SendResult.Result, nil
	}
	err = errors.New(SendResult.Error.Message)
	fmt.Println("WalleSendTo>>>>>>>>End....")
	return strconv.Itoa(SendResult.Error.Code), err
}

func (m *DcrCashApi) WalletSendFrom(fromaccount string, toaddress string, amount float64, minconf int, comment, commentto string) (string, error) {

	var SendResult RpcResult

	if len(toaddress) == 0 {
		fmt.Println("input toaddress err:")
		err := errors.New("input toaddress err")
		return "", err
	}

	if toaddress == DEFAULTACCOUNT {
		var err error
		toaddress, err = m.WalletAddressofaccount(toaddress)
		if err != nil {
			fmt.Println("Call getaccountaddress fail:", err.Error())
			return "", err
		}
		//toaddress = "DscoFukRjv6y9C62tsiJG8qPVsA9AbTe9AB"
	}
	dcrcashRpc.RunCall("walletpassphrase", Config.Walletpass.Passphrase, 3)
	result, err := dcrcashRpc.RunCall("sendfrom", fromaccount, toaddress, amount)
	fmt.Println("WalleSendfrom>>>>>>>>", string(result))
	if err != nil {
		fmt.Println("Call sendfrom fail:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &SendResult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}

	// success
	if len(SendResult.Error.Message) == 0 {
		return SendResult.Result, nil
	}
	err = errors.New(SendResult.Error.Message)
	return strconv.Itoa(SendResult.Error.Code), err
}

//list unspent transaction outputs
func (m DcrCashApi) Listunspent(address []string) ([]map[string]interface{}, error) {

	var unspentResult RpcUnspentResult

	result, err := dcrcashRpc.RunCall("listunspent", 1, 9999999, address)
	fmt.Println("Listunspent Result:", string(result))
	if err != nil {
		fmt.Println("Call listunspent fail:", err.Error())
		return nil, err
	}

	if err := json.Unmarshal([]byte(result), &unspentResult); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return nil, err
	}

	// success
	if len(unspentResult.Error.Message) != 0 {
		err = errors.New(unspentResult.Error.Message)
		return nil, err
	}

	var unspentValue map[string]float64
	unspentValue = make(map[string]float64)
	for _, unspentInfo := range unspentResult.Result {
		addr := unspentInfo.Address
		if unspentValue[addr] == 0 {
			unspentValue[addr] = unspentInfo.Amount
		} else {
			unspentValue[addr] += unspentInfo.Amount
		}
	}

	outputsArr := make([]map[string]interface{}, 0)
	for k, v := range unspentValue {
		requestData := make(map[string]interface{})
		requestData["address"] = k
		valuestr := fmt.Sprintf("%.8f", v)
		value, _ := strconv.ParseFloat(valuestr, 64)
		requestData["Amount"] = value
		outputsArr = append(outputsArr, requestData)
	}
	return outputsArr, nil

}

//Send Raw Transaction
func (m DcrCashApi) SendRawTransaction(hexstring string) (string, error) {

	var rawtransactioninfo RpcResult

	if len(hexstring) == 0 {
		fmt.Println("input para err:")
		err := errors.New("input para err")
		return "", err
	}

	result, err := dcrcashRpc.RunCall("sendrawtransaction", hexstring)
	if err != nil {
		fmt.Println("Call sendrawtransaction fail:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &rawtransactioninfo); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}

	if len(rawtransactioninfo.Error.Message) == 0 {

		return rawtransactioninfo.Result, nil
	}
	err = errors.New(rawtransactioninfo.Error.Message)
	return "", err
}

func (m DcrCashApi) ListAccounts() ([]byte, error) {
	var accountsofValues AccountsOfvaluesResult

	result, err := dcrcashRpc.RunCall("listaccounts")
	if err != nil {
		fmt.Println("Call listaccounts fail:", err.Error())
		return nil, err
	}

	if err := json.Unmarshal(result, &accountsofValues); err != nil {
		fmt.Println("unmarshal err:", err.Error())
		return nil, err

	}
	if len(accountsofValues.Error.Message) == 0 {
		var jbuf []byte
		if jbuf, err = json.Marshal(accountsofValues.Result); err != nil {
			return nil, err
		}
		return jbuf, nil
	}
	err = errors.New(accountsofValues.Error.Message)
	return nil, err
}

func (m DcrCashApi) WalletAddressofaccount(account string) (string, error) {
	var oneaddress OneaddressOfaccount
	if len(account) == 0 {
		fmt.Println("input para err:")
		err := errors.New("input para err")
		return "", err
	}
	result, err := dcrcashRpc.RunCall("getaccountaddress", account)
	if err != nil {
		fmt.Println("Call getaccountaddress fial:", err.Error())
		return "", err
	}

	if err := json.Unmarshal([]byte(result), &oneaddress); err != nil {
		fmt.Println("Unmarshal err :", err.Error())
		return "", err
	}
	if len(oneaddress.Error.Message) == 0 {
		return oneaddress.Result, nil
	}

	err = errors.New(oneaddress.Error.Message)
	return "", err

}
