package lite

import (
	"encoding/json"
	"errors"
	"fmt"
	"lite/liteRpc"
)

type LiteApi struct {
}

// lite rpc  error struct
type LiteError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// lite rpc  common result struct
type LiteResult struct {
	Result string    `json:"result"`
	Error  LiteError `json:"error"`
	Id     string    `json:"id"`
}

// get block info result struct
type Liteblock struct {
	Hash              string
	Confirmations     uint64
	Size              uint64
	Strippedsize      uint64
	Weight            uint64
	Height            uint64
	Version           uint64
	VersionHex        string
	Merkleroot        string
	Tx                []string
	Time              uint64
	Mediantime        uint64
	Nonce             uint64
	Bits              string
	Difficulty        float64
	Chainwork         string
	Previousblockhash string
	Nextblockhash     string
}

type LiteblockResult struct {
	Result Liteblock `json:"result"`
	Error  LiteError `json:"error"`
	Id     string    `json:"id"`
}

//lite list  transactions since blockhash result struct
type Listtransactions struct {
	Account       string  `json:"account"`
	Address       string  `json:"address"`
	Category      string  `json:"category"`
	Amount        float64 `json:"amount"`
	Vout          uint64  `json:"vout"`
	Fee           float64 `json:"fee":`
	Confirmations uint64  `json:"confirmations"`
	Blockhash     string  `json:"blockhash"`
	Blockindex    uint64  `json:"blockindex"`
	Blocktime     uint64  `json:"blocktime"`
	Txid          string  `json:"txid"`
	Time          uint64  `json:"time"`
	Timereceived  uint64  `json:"timereceived"`
	Label         string  `json:"label"`
	Comment       string  `json:"comment"`
	To            string  `json:"to"`
}

type listtransactions struct {
	Transactions []Listtransactions `json: "transactions"`
	Lastblock    string             `json: "lastblock"`
}

type Listtransactionsresult struct {
	Result listtransactions `json:"result"`
	Error  LiteError        `json:"error"`
	Id     string           `json:"id"`
}

//get Balance result struct
type BalanceResult struct {
	Result float64   `json:"result"`
	Error  LiteError `json:"error"`
	Id     string    `json:"id"`
}

//get block count 
type Blockcountresult struct {
	Result uint64
	Error  LiteError
	Id     string
}

//set fee 
type Setfeeresult struct {
	Result bool
	Error  LiteError
	Id     string
}

//list all address of a account
type ListAddressesByAccount struct {
	Result []string
	Error  LiteError
	Id     string
}


//get wallet info result struct
type Litewalletinfo struct {
	Walletversion       uint64
	Balance             float64
	Unconfirmed_balance float64
	Immature_balance    float64
	Txcount             uint64
	Keypoololdest       uint64
	Keypoolsize         uint64
	Unlocked_until      uint64
	Paytxfee            float64
	Hdmasterkeyid       string
}

type LitewalletinfoResult  struct {
	Result Litewalletinfo  `json:"result"`
	Error  LiteError      `json:"error"`
	Id     string          `json:"id"`
}


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

	var liteaddr LiteResult

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

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

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

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

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

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

	var litekey LiteResult

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

		return "", err
	}

	//
	result, err := liteRpc.RunCall("dumpprivkey", address)

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

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

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

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

	var Blockhash LiteResult

	//get block hash by index
	result, err := liteRpc.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 LiteApi) WalletGetBlockTime(blockhash string) (uint64, error) {

	var liteBlockresult LiteblockResult

	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 := liteRpc.RunCall("getblock", blockhash)
	if err != nil {
		fmt.Println("Call getblock fail:", err.Error())
		return 0, err
	}

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

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

		return uint64(liteBlockresult.Result.Time), nil
	}
	err = errors.New(liteBlockresult.Error.Message)
	return 0, err
}
//get block Height by hash;return:blocktime
func (m LiteApi) WalletGetBlockHeight(blockhash string) (uint64, error) {

	var liteBlockresult LiteblockResult

	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 := liteRpc.RunCall("getblock", blockhash)
	if err != nil {
		fmt.Println("Call getblock fail:", err.Error())
		return 0, err
	}

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

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

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

func (m *LiteApi) 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 := liteRpc.RunCall("listsinceblock", blockhash)

	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 LiteApi) WalleGetBalance(account string) (float64, error) {

	var liteBalance BalanceResult

	// getbalance all
	result, err := liteRpc.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 LiteApi) WalleGetUnconfirmedBalance(account string) (float64, error) {

	var liteUnconfirmedBalance BalanceResult

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

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

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

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

	var LiteSendResult LiteResult

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

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

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

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

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

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

	var blockcount Blockcountresult

	//
	result, err := liteRpc.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 LiteApi) WalletSetTxFee(amount float64) (bool, error) {

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

//get the current litecoin address for receiving payments to this account
func (m *LiteApi) WalletGetAccountAddress(Account string) (string, error) {

	var listAddressesByAccount  LiteResult

	//
	result, err := liteRpc.RunCall("getaccountaddress", "")

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

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

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

//the list of addresses for the given account
func (m *LiteApi) WalletAddrList(Account string) ([]string, error) {

	var addresslist ListAddressesByAccount

	//
	result, err := liteRpc.RunCall("getaddressesbyaccount", Account)

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

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

	// success
	if len(addresslist.Error.Message) == 0 {
		return addresslist.Result, nil
	}
	err = errors.New(addresslist.Error.Message)
	return nil, 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 LiteApi) WalletInfo() ([]byte, error) {

	var litewalletinfoResult LitewalletinfoResult

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

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

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

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

	err = errors.New(litewalletinfoResult.Error.Message)
	return nil, err
}


//export all address of the wallet to filename
func (m LiteApi) WalleDumpWalletAddress(filename string) (error) {

	var literesult LiteResult

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

	//
	result, err := liteRpc.RunCall("dumpwallet", filename)

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

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

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


//Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.
func (m LiteApi) WalleGetReceivedByAccount(account string, minconf uint64) (float64, error) {

	var receivedByAccount BalanceResult

	//
	result, err := liteRpc.RunCall("getreceivedbyaccount", account, minconf)

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

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

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

//get the total amount received by the given zcashaddress in transactions with at least minconf confirmations.
func (m LiteApi) WalleGetReceivedByAddress(address string, minconf uint64) (float64, error) {

	var receivedByAddress BalanceResult

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

	//
	result, err := liteRpc.RunCall("getreceivedbyaddress", address, minconf)

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

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

	// success
	if len(receivedByAddress.Error.Message) == 0 {
		return float64(receivedByAddress.Result), nil
	}
	err = errors.New(receivedByAddress.Error.Message)
	return 0, err
}
//Send Raw Transaction
func (m LiteApi) SendRawTransaction(hexstring string) (string, error) {

	var rawtransactioninfo LiteResult

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

	result, err := liteRpc.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 LiteApi) InitRpcConf(rpcuser string, rpcpassword string, rpchost string, rpcport int) bool {

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