package bitcoin

import (
	"bitcoin/bitcoinRpc"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
)

type BitCoinApi struct {
}

//get a new taddr
func (m *BitCoinApi) WalletNewAddr() (string, error) {

	var newaddr RpcResult

	result, err := bitcoinRpc.RunCall("getnewaddress")

	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 *BitCoinApi) 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 := bitcoinRpc.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
}

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

	var Blockhash RpcResult

	//get block hash by index
	result, err := bitcoinRpc.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 BitCoinApi) 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 := bitcoinRpc.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
}

//get block Height by hash;return:blocktime
func (m BitCoinApi) 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 := bitcoinRpc.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
}

func (m *BitCoinApi) 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 := bitcoinRpc.RunCall("listsinceblock", blockhash,1,true)

	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 the server's total available balance
func (m BitCoinApi) WalleGetBalance(account string) (float64, error) {

	var liteBalance BalanceResult

	// getbalance all
	result, err := bitcoinRpc.RunCall("getbalance", "*")

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

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

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

//get the server's total unconfirmed balance
func (m BitCoinApi) WalleGetUnconfirmedBalance(account string) (float64, error) {

	var UnconfirmedBalance BalanceResult

	//
	result, err := bitcoinRpc.RunCall("getunconfirmedbalance")

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

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

	// success
	if len(UnconfirmedBalance.Error.Message) == 0 {
		return float64(UnconfirmedBalance.Result), nil
	}
	err = errors.New(UnconfirmedBalance.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 *BitCoinApi) WalleSendFrom(fromaccount string, tozcashaddress string, amount float64, minconf int, comment string, commentto string) (string, error) {

	var SendResult RpcResult

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

	//
	result, err := bitcoinRpc.RunCall("sendfrom", fromaccount, tozcashaddress, amount, minconf, "", "")

	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 "", err
}

//get the number of blocks in the longest block chain
func (m BitCoinApi) WalleGetBlockCount() (uint64, error) {

	var blockcount Blockcountresult

	//
	result, err := bitcoinRpc.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
}

//Set the transaction fee per kB. Overwrites the paytxfee parameter.

func (m BitCoinApi) WalletSetTxFee(amount float64) (bool, error) {

	var settxfee Setfeeresult
	//
	result, err := bitcoinRpc.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
	}

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

//Result:
//  	"walletversion": xxxxx,     钱包的额版本
//  	"balance": xxxxxxx,        钱包中已经确认的LTC余额
//  	"unconfirmed_balance": xxx, 钱包中未经确认的LTC余额
//  	"immature_balance": xxxxxx, 钱包中不成熟的余额
//  	"txcount": xxxxxxx,         钱包中交易的总数
//  	"keypoololdest": xxxxxx,    密钥池中最早的预生成密钥的时间戳
// 	    "keypoolsize": xxxx,        预先生成了多少个新密匙
// 	    "unlocked_until": ttt,      (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT)
//                                   that the wallet is unlocked for transfers, or 0 if the wallet is locked
//  	"paytxfee": x.xxxx,         交易费用的配置 set in  LTC/KB
//		"hdmasterkeyid": "<hash160>", (string) the Hash160 of the HD master pubkey
func (m BitCoinApi) WalletInfo() ([]byte, error) {

	var walletinfoResult WalletinfoResult

	//
	result, err := bitcoinRpc.RunCall("getwalletinfo")

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

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

	// success
	if len(walletinfoResult.Error.Message) == 0 {
		var jbuf []byte
		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 BitCoinApi) InitRpcConf(rpcuser string, rpcpassword string, rpchost string, rpcport int) bool {

	bitcoinRpc.InitRpcConfig(rpcuser, rpcpassword, rpchost, rpcport)
	return true
}


//Adds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.
func (m BitCoinApi) WalletImportAddress(address string, rescan bool) (bool, error) {

	var rpcResult RpcResult
	var err error
	var result []byte
	if len(address) == 0 {
		fmt.Println("Call WalletImportAddress address is null:")
		err = errors.New("address is null")
		return false, err
	}

	// import address in Wallet
	result, err = bitcoinRpc.RunCall("importaddress", address, "", rescan)
	if err != nil {
		fmt.Println("WalletImportAddress  call bitcoinRpc.RunCall fail:", err.Error(), address)
		return false, err
	}

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

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

	err = errors.New(rpcResult.Error.Message)
	return false, err
}
/*
//list unspent transaction outputs
func (m BitCoinApi) Listunspent(address []string) ([]UnspentInfo, error) {

	var unspentResult RpcUnspentResult
	result, err := bitcoinRpc.RunCall("listunspent",1,9999999,address)

	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 {
		return unspentResult.Result, nil
	}

	err = errors.New(unspentResult.Error.Message)
	return nil, err
}
*/
//list unspent transaction outputs
func (m BitCoinApi) Listunspent(address []string) ([]map[string]interface{}, error) {

        var unspentResult RpcUnspentResult
        result, err := bitcoinRpc.RunCall("listunspent",1,9999999,address)

        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 BitCoinApi) 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 := bitcoinRpc.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
}

