// dgb.go
package dgb

import (
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"sync/atomic"

	//"pool/internal/cache"
	"pool/internal/db"
	"pool/internal/gbt/coin"
	"pool/internal/gbt/dbif"
	"pool/internal/msg"
	"pool/internal/utility"
	"time"

	"database/sql"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcutil/base58"

	//"github.com/btcsuite/btcd/rpcclient"
	_ "github.com/mattn/go-sqlite3"

	"go.uber.org/zap"
)

const GBT_DGB_VERSION string = "dgb v3.0l"

type DgbAddrConfig struct {
	Addr string `json:"addr"`
}

type DgbConfig struct {
	Dgb DgbAddrConfig `json:"dgb"`
}

type GbtDgbContext struct {
	Config DgbConfig
	GbtCtx *coin.GbtContext

	last_time time.Time
	last_gbt  msg.GbtMsg

	Submits float64

	addressIndex int

	new_block_chan  chan int
	new_block_index int

	last_height uint32
}

var logg *zap.Logger
var GbtDgbCtx GbtDgbContext

type BlockCheckData struct {
	Height  int
	Nonce   string
	User    string
	Miner   string
	MinerId string
	Hash    string
	SubIdx  int
}

type PushBlkNewMsg struct {
	Coin   string `json:"coin"`
	Height int    `json:"height"`
	Nonce  string `json:"nonce"`
}

func update_block_confirm(gbt *GbtDgbContext) {
	db, err := sql.Open("sqlite3", "./blocks.db")
	if err != nil {
		//log.Printf("Error opening database: %v", err)
		logg.Error("[gbt]", zap.String("Error opening database", err.Error()))
		return
	}
	defer db.Close()

	query := "SELECT user,miner,minerid,height,nonce,hash,subidx FROM blocks WHERE checked=0 AND created_at >= datetime('now', '-30 minutes') order by id desc limit 2"
	rows, err := db.Query(query)
	if err != nil {
		//log.Printf("Error executing query from blocks: %v", err)
		logg.Error("[gbt]", zap.String("Error executing query from blocks:", err.Error()))
		return
	}
	defer rows.Close()

	var blocks []BlockCheckData
	for rows.Next() {
		var height int
		var nonce string
		var user string
		var miner string
		var minerid string
		var hash string
		var subidx int
		if err := rows.Scan(&user, &miner, &minerid, &height, &nonce, &hash, &subidx); err != nil {
			//log.Printf("Error scanning row in blocks: %v", err)
			logg.Error("[gbt]", zap.String("Error scanning row in blocks:", err.Error()))
			return
		}
		var blockdata BlockCheckData
		blockdata.Height = height
		blockdata.Nonce = nonce
		blockdata.User = user
		blockdata.Miner = miner
		blockdata.MinerId = minerid
		blockdata.Hash = hash
		blockdata.SubIdx = subidx

		blocks = append(blocks, blockdata)
		//fmt.Printf("blocks - Height: %d, Nonce: %d\n", height, nonce)
		//log.Printf("update block height %d nonce %s, subidx %d, user %s", height, nonce, subidx, user+"."+miner+"_"+minerid)
	}
	for _, block := range blocks {
		block_hash, err := gbt.GbtCtx.Client.GetBlockHash(int64(block.Height))
		if err != nil {
			logg.Info("[gbt]", zap.String("GetBlockHash ", err.Error()))
			continue
		}
		rawmsgs := make([]json.RawMessage, 1)
		param_str := `"` + block_hash.String() + `"`
		rawmsgs[0] = json.RawMessage(param_str)
		result, err := gbt.GbtCtx.Client.RawRequest("getblockheader", rawmsgs)
		if err != nil {
			//log.Printf("getblockheader %s", err.Error())
			logg.Error("[gbt]", zap.String("getblockheader", err.Error()))
			continue
		}
		//log.Printf("getblockheader %d %s:%v", block.Height, block_hash, result)
		var blockHeader msg.GetBlockHeaderMsgInt
		err = json.Unmarshal(result, &blockHeader)
		if err != nil {
			//log.Printf("getblockheader Unmarshal %s", err.Error())
			logg.Error("[gbt]", zap.String("getblockheader Unmarshal ", fmt.Sprint(block.Height)+" "+err.Error()))
			continue
		}

		rawmsgs_stats := make([]json.RawMessage, 1)
		rawmsgs_stats[0] = json.RawMessage(param_str)
		result_stats, err := gbt.GbtCtx.Client.RawRequest("getblockstats", rawmsgs_stats)
		if err != nil {
			//log.Printf("getblockstats %s", err.Error())
			logg.Error("[gbt]", zap.String("getblockstats", err.Error()))
			continue
		}
		//log.Printf("getblockheader %d %s:%v", block.Height, block_hash, result)
		var blockStats msg.GetBlockStatsMsg
		err = json.Unmarshal(result_stats, &blockStats)
		if err != nil {
			//log.Printf("getblockstats Unmarshal %s", err.Error())
			logg.Error("[gbt]", zap.String("getblockstats Unmarshal ", fmt.Sprint(block.Height)+" "+err.Error()))
			continue
		}

		if blockHeader.Confirmations > 3 {
			//log.Printf("cmp block: %d %s vs %s %s", block.Height, block.Nonce, string(result), blockHeader.Nonce)
			//log.Printf("cmp block: %d %s %s_%s_%s vs %s\n", block.Height, block.Nonce, block.User, block.Miner, block.MinerId, blockHeader.Nonce)
			nonceb := utility.Uint32ToByteBig(blockHeader.Nonce)
			nonces := hex.EncodeToString(nonceb)
			if nonces == block.Nonce {

				block_height := int64(block.Height)
				/*dbif.NotifyMinerSuccess(gbt.GbtCtx, block.User, block.Miner, block.MinerId, block_height, "", block.Nonce, int64(block.SubIdx), blockStats.Subsidy, blockStats.Totalfee)
				dbif.NotifyUsersBlkStatsSuccess(gbt.GbtCtx, block.User, block_height, "", block.Nonce, int64(block.SubIdx), blockStats.Subsidy, blockStats.Totalfee)*/
				dbif.NotifyPoolBlkStatsSuccess(gbt.GbtCtx, block_height, "", block.Nonce, int64(block.SubIdx), blockStats.Subsidy, blockStats.Totalfee)
				dbif.NotifyBlkDetailSuccess(gbt.GbtCtx, block_height, "", block.Nonce, int64(block.SubIdx))
				/*dbif.NotifyBlockStat(gbt.GbtCtx, block.User, block.Miner, block.MinerId, blockStats.Subsidy, blockStats.Totalfee)*/
				//dbif.NotifyBlkNewSuccess(gbt.GbtCtx, block_height, "", block.Nonce, -1)
				dbif.NotifyBlkNewDb(gbt.GbtCtx, block_height, block.Hash, true, block.Nonce, int64(block.SubIdx))

				//gbt.GbtCtx.Blocks += 1
				//cache.StorePoolCache(gbt.GbtCtx.RedisClient, gbt.GbtCtx.Coin, "blocks", gbt.GbtCtx.Blocks)

				//gbt.GbtCtx.Reward += blockStats.Subsidy
				//cache.StorePoolCache(gbt.GbtCtx.RedisClient, gbt.GbtCtx.Coin, "reward", gbt.GbtCtx.Reward)

				//gbt.GbtCtx.Fee += blockStats.Totalfee
				//cache.StorePoolCache(gbt.GbtCtx.RedisClient, gbt.GbtCtx.Coin, "fee", gbt.GbtCtx.Fee)

				/*var pushmsg PushBlkNewMsg
				pushmsg.Coin = gbt.GbtCtx.Coin
				pushmsg.Height = block.Height
				pushmsg.Nonce = block.Nonce

				jsonData, err := json.Marshal(pushmsg)
				if err != nil {
					//log.Printf("Failed to marshal JSON: %v", err)
					logg.Error("[gbt]", zap.String("Failed to marshal JSON:", err.Error()))
					continue
				}
				if gbt.GbtCtx.PushCh == nil {
					gbt.GbtCtx.PushCh = utility.InitZmqPush(gbt.GbtCtx.Config.Profit.Push)
				}
				if gbt.GbtCtx.PushCh != nil {
					err = gbt.GbtCtx.PushCh.SendMessage([][]byte{[]byte("blk_new"), jsonData})
					if err != nil {
						//log.Printf("Failed to send data: %v", err)
						logg.Error("[gbt]", zap.String("Failed to SendMessage:", err.Error()))
						continue
					}
				}*/

				updateSQL := `UPDATE blocks SET checked = 1 WHERE height = ? AND nonce = ? AND checked = 0`
				_, err = db.Exec(updateSQL, block.Height, block.Nonce)
				if err != nil {
					//log.Printf("Error updating blk_new: %v", err)
					logg.Error("[gbt]", zap.String("Error updating blk_new:", err.Error()))
					continue
				}
				//log.Printf("update block success: %d %s", block.Height, block.Nonce)
				logg.Warn("[gbt]", zap.String("update block success:", fmt.Sprint(block.Height)+" "+block.Nonce))
			}
		}
	}
}

func get_gbt_msg(gbt *GbtDgbContext) []byte {
	rawmsgs := make([]json.RawMessage, 2)
	//rawmsgs[0] = json.RawMessage(`{"capabilities": ["coinbasetxn", "workid", "coinbase/append"],"rules":["segwit"]}`)
	rawmsgs[0] = json.RawMessage(`{"capabilities": ["coinbasetxn", "workid", "coinbase/append"]}`)
	param_str2 := `"` + "skein" + `"`
	if gbt.GbtCtx.Coin == "dgbq" {
		param_str2 = `"` + "qubit" + `"`
	} else if gbt.GbtCtx.Coin == "dgbo" {
		param_str2 = `"` + "odo" + `"`
	} else {
	}
	rawmsgs[1] = json.RawMessage(param_str2)
	result, err := gbt.GbtCtx.Client.RawRequest("getblocktemplate", rawmsgs)
	if err != nil {
		logg.Error("[gbt]", zap.String("getblocktemplate", err.Error()))
		return nil
	}

	var rxmsg msg.GbtMsg
	err = json.Unmarshal(result, &rxmsg)
	if err != nil {
		logg.Error("[gbt]", zap.String("getblocktemplate", err.Error()))
		return nil
	}

	for i := 0; i < len(*rxmsg.Transactions); i++ {
		var ts msg.GbtTransaction
		err = json.Unmarshal((*rxmsg.Transactions)[i], &ts)
		if err != nil {
			logg.Error("[gbt]", zap.String("Unmarshal Transactions", err.Error()))
			continue
		}
		if i >= 500 {
			rxmsg.Coinbasevalue = rxmsg.Coinbasevalue - uint64(ts.Fee)
		}
		//logg.Debug("[gbt]", zap.String("ts.Hash", ts.Hash))
	}
	if len(*rxmsg.Transactions) >= 500 {
		tss := (*rxmsg.Transactions)[:500]
		rxmsg.Transactions = &tss
	}

	if rxmsg.Previousblockhash == gbt.last_gbt.Previousblockhash {
		if time.Now().Sub(gbt.last_time) < time.Duration(gbt.GbtCtx.Config.Rpc.Timeout)*time.Millisecond {
			return nil
		}
	}

	gbtstr := fmt.Sprintf("version %x, prev %s, coinbase %x, longpoolid %s, target %s time %x, bits %s, height %d", rxmsg.Version, rxmsg.Previousblockhash, rxmsg.Coinbasevalue, rxmsg.Longpollid, rxmsg.Target, rxmsg.Curtime, rxmsg.Bits, rxmsg.Height)
	logg.Debug("[gbt]", zap.String(" ", gbtstr))

	body := new_job_from_gbt(gbt, &rxmsg)

	gbt.last_gbt = rxmsg

	gbt.last_time = time.Now()

	return body
}

func gbt_notify_running(gbt *GbtDgbContext) {
	for {
		if !gbt.GbtCtx.Started {
			break
		}
		if gbt.GbtCtx.NodeSubCh == nil {
			gbt.GbtCtx.NodeSubCh = utility.InitZmqSub(gbt.GbtCtx.Config.Rpc.ZmqSub, utility.BITCOIND_ZMQ_HASHBLOCK)
		}
		if gbt.GbtCtx.NodeSubCh != nil {
			cmsg_sub, err := gbt.GbtCtx.NodeSubCh.RecvMessage()
			if err != nil {
				if !gbt.GbtCtx.Started {
					break
				}
				gbt.GbtCtx.NodeSubCh.SetSubscribe(utility.BITCOIND_ZMQ_HASHBLOCK)
				gbt.GbtCtx.NodeSubCh.Connect(gbt.GbtCtx.Config.Rpc.ZmqSub)
				continue
			}
			if len(cmsg_sub) >= 2 {
				if string(cmsg_sub[0]) == "hashblock" {
					GbtDgbCtx.new_block_index = GbtDgbCtx.new_block_index + 1
					//log.Println("gbt_notify_running", hex.EncodeToString(cmsg_sub[1]), GbtDgbCtx.new_block_index)
					gbt.new_block_chan <- GbtDgbCtx.new_block_index

				}
			}
		} else {
			logg.Error("[gbt]", zap.String("notify", "NodeSubCh fail!"))
			time.Sleep(time.Duration(1) * time.Second)
		}
	}
}

func gbt_running(gbt *GbtDgbContext) {
	jobtopic := "job" + gbt.GbtCtx.Coin
	gbtmsg := get_gbt_msg(gbt)
	if gbtmsg != nil {
		if gbt.GbtCtx.PubCh == nil {
			gbt.GbtCtx.PubCh = utility.InitZmqPub(gbt.GbtCtx.Config.Zmq.Pub)
		}
		if gbt.GbtCtx.PubCh != nil {
			for trycnt := 0; trycnt < 3; trycnt++ {
				err := gbt.GbtCtx.PubCh.SendMessage([][]byte{[]byte(jobtopic), gbtmsg})
				if err != nil {
					if !gbt.GbtCtx.Started {
						return
					}
					//gbt.GbtCtx.PubCh.Bind(gbt.GbtCtx.Config.Zmq.Pub)
					logg.Warn("[gbt]", zap.String("job ", err.Error()))
				} else {
					//gbt.GbtCtx.PubCh.SendChan <- [][]byte{[]byte("jobdgb"), gbtmsg}
					logg.Warn("[gbt]", zap.String("job ", "sent"))
					break
				}
			}
			//gbt.GbtCtx.AlivingChan <- true
			atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
		} else {
			logg.Warn("[gbt]", zap.String("job ", "sent failed! PubCh nil"))
		}
	} else {
		atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
	}

	timer := time.NewTimer(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond)
	defer timer.Stop()

	for {
		if !gbt.GbtCtx.Started {
			break
		}

		new_block_notify := false

		select {
		case blk_idx := <-gbt.new_block_chan:
			log.Println("new block chan", blk_idx)
			new_block_notify = true
			if !timer.Stop() {
				<-timer.C
			}
			timer.Reset(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond)
		case <-gbt.GbtCtx.ExitGbtChan:
			logg.Error("[gbt]", zap.String("gbt", "exit"))
			return
		case <-time.After(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond):
			log.Println("poll gbt timeout")
			timer.Reset(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond)
		}
		/*if check_bestblockhash(gbt) {*/
		gbtmsg := get_gbt_msg(gbt)
		if gbtmsg != nil {
			//check_preblock(gbt, DbCtx)
			if gbt.GbtCtx.PubCh == nil {
				gbt.GbtCtx.PubCh = utility.InitZmqPub(gbt.GbtCtx.Config.Zmq.Pub)
			}
			if gbt.GbtCtx.PubCh != nil {
				for trycnt := 0; trycnt < 3; trycnt++ {
					err := gbt.GbtCtx.PubCh.SendMessage([][]byte{[]byte(jobtopic), gbtmsg})
					if err != nil {
						logg.Warn("[gbt]", zap.String("job ", err.Error()))
						continue
					} else {
						//gbt.GbtCtx.PubCh.SendChan <- [][]byte{[]byte("jobdgb"), gbtmsg}
						logg.Warn("[gbt]", zap.String("job ", "sent"))
						break
					}
				}
				//gbt.GbtCtx.AlivingChan <- true
				atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
			} else {
				logg.Warn("[gbt]", zap.String("job ", "sent failed! PubCh nil"))
			}
		} else {
			atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
		}

		if new_block_notify {
			update_block_confirm(gbt)
		}
	}

}

func dgbInit(config *DgbConfig) {
	data, err := ioutil.ReadFile("gbt.conf")
	if err != nil {
		panic(err.Error())
	}
	if err = json.Unmarshal(data, &config); err != nil {
		panic(err.Error())
	}
}

func Init(GbtCtx *coin.GbtContext, DbCtx *db.DbContext) {
	GbtDgbCtx.GbtCtx = GbtCtx
	GbtDgbCtx.last_height = 0

	dgbInit(&GbtDgbCtx.Config)

	GbtDgbCtx.last_time = time.Now()
	logg = GbtCtx.Log
	GbtDgbCtx.new_block_chan = make(chan int, 256)
	GbtDgbCtx.new_block_index = 0
	logg.Info("[gbt]", zap.String("gbt_dgb_version", GBT_DGB_VERSION))
}

func Start() {
	go gbt_running(&GbtDgbCtx)
	go gbt_notify_running(&GbtDgbCtx)
	go submit_block_running(&GbtDgbCtx)
}

func Stop() {
	defer close(GbtDgbCtx.new_block_chan)
}

func new_block_into_db(block *GbtDgbContext, user string, miner string, minerid string, height int64, nonce string, hash string, subidx int64) bool {
	db, err := sql.Open("sqlite3", "./blocks.db")
	if err != nil {
		log.Printf("Error opening database: %v", err)
		return false
	}
	defer db.Close()

	createTableSQL := `
					  CREATE TABLE IF NOT EXISTS blocks (
					  	id INTEGER PRIMARY KEY AUTOINCREMENT,
						user TEXT NOT NULL,
						miner TEXT NOT NULL,
						minerid TEXT NOT NULL,
					  	height INTEGER,
					  	nonce  TEXT NOT NULL,
						hash TEXT NOT NULL,
						subidx INTEGER,
					  checked INTEGER,
						created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
					  );`
	_, err = db.Exec(createTableSQL)
	if err != nil {
		log.Printf("Error creating table: %v", err)
		return false
	}

	insertSQL := `INSERT INTO blocks (user, miner, minerid, height, nonce, checked, hash, subidx) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
	_, err = db.Exec(insertSQL, user, miner, minerid, height, nonce, 0, hash, subidx)
	if err != nil {
		log.Printf("Error inserting data from blocks %s: %v", fmt.Sprint(height), err)
		return false
	}
	return true
}

func submit_block_running(block *GbtDgbContext) {
	logg.Info("[block]", zap.String("submit_block_running", "Start."))
	blktopic := "blk" + block.GbtCtx.Coin
	for {
		if !block.GbtCtx.Started {
			break
		}
		if block.GbtCtx.SubCh == nil {
			block.GbtCtx.SubCh = utility.InitZmqSub(block.GbtCtx.Config.Zmq.Sub, blktopic)
		}
		if block.GbtCtx.SubCh != nil {
			cmsg_sub, err := block.GbtCtx.SubCh.RecvMessage()
			if err != nil {
				if !block.GbtCtx.Started {
					break
				}
				/*block.GbtCtx.SubCh.Destroy()
				block.GbtCtx.SubCh = nil*/
				time.Sleep(time.Duration(1) * time.Second)
				block.GbtCtx.SubCh.SetSubscribe("blk" + block.GbtCtx.Coin)
				block.GbtCtx.SubCh.Connect(block.GbtCtx.Config.Zmq.Sub)
				//block.GbtCtx.SubCh.SetMaxmsgsize(1024 * 1024 * 8)
				continue
			}

			//log.Println(cmsg_sub, len(cmsg_sub), block.GbtCtx.SubCh)
			if len(cmsg_sub) >= 2 {
				if string(cmsg_sub[0]) == blktopic {
					cmsg := cmsg_sub[1]
					//block data
					msgb := make([]byte, len(cmsg)-16)
					copy(msgb, cmsg)

					//height
					//heightb := make([]byte, 4)
					heightb, err := hex.DecodeString(string(cmsg[len(msgb) : len(msgb)+8]))
					if err != nil {
						//block.Consumer.MarkOffset(cmsg, "")
						logg.Error("[block]", zap.String("failed to decode height", err.Error()))
						continue
					}
					var height uint32 = utility.ByteToUint32(heightb)
					logg.Warn("[block]", zap.Uint32("height", height))

					if height <= block.last_height {
						continue
					}
					block.last_height = height

					//index
					//indexb := make([]byte, 4)
					indexb, err1 := hex.DecodeString(string(cmsg[len(msgb)+8:]))
					if err1 != nil {
						//block.Consumer.MarkOffset(cmsg, "")
						logg.Error("[block]", zap.String("failed to decode index", err1.Error()))
						continue
					}
					//copy(indexb, cmsg.Value[len(msgb)+4:])
					var index uint32 = utility.ByteToUint32(indexb)
					logg.Warn("[block]", zap.Uint32("index", index))

					logg.Debug("[block]", zap.String("msg", string(cmsg)), zap.String("blk", string(msgb)))
					var dgbblock msg.BlockGrsMsg
					if err := json.Unmarshal(msgb, &dgbblock); err != nil {
						//block.Consumer.MarkOffset(cmsg, "")
						logg.Error("[block]", zap.String("failed to Unmarshal job", err.Error()))
						continue
					}

					blk := hex.EncodeToString(dgbblock.Data)
					rawmsgs := make([]json.RawMessage, 1)
					logg.Info("[block]", zap.String("blk", blk))
					rawmsgs[0] = json.RawMessage(`"` + blk + `"`)
					//var last_result json.RawMessage

					result, err := block.GbtCtx.Client.RawRequest("submitblock", rawmsgs)
					if err != nil {
						logg.Error("[block]", zap.String("submitblock", err.Error()))
					} else {
						//last_result = result
					}
					logg.Info("[block]", zap.String("result", string(result)))

					/*dbif.NotifyMinerSubmitResult(block.GbtCtx, dgbblock.User, dgbblock.Miner, dgbblock.Index, int64(height), dgbblock.Pow, string(result), dgbblock.Nonce, dgbblock.SubIdx)
					dbif.NotifyUsersBlkStatsSubmitResult(block.GbtCtx, dgbblock.User, int64(height), dgbblock.Pow, string(result), dgbblock.Nonce, dgbblock.SubIdx)*/
					dbif.NotifyPoolBlkStatsSubmitResult(block.GbtCtx, int64(height), dgbblock.Pow, string(result), dgbblock.Nonce, dgbblock.SubIdx)

					block.Submits += 1
					//log.Printf("[block] height %d subidx %d nonce %s\n", height, dgbblock.SubIdx, dgbblock.Nonce)
					logg.Warn("[block]", zap.Float64("total submits", block.Submits), zap.Int64("SubIdx", dgbblock.SubIdx))
					if string(result) == "null" {
						new_block_into_db(block, dgbblock.User, dgbblock.Miner, dgbblock.Index, int64(height), dgbblock.Nonce, dgbblock.Pow, dgbblock.SubIdx)
					}
				}
			}
		} else {
			logg.Error("[block]", zap.String("block", "SubCh failed! retry"))
			time.Sleep(time.Duration(1) * time.Second)
		}
	}
}

func new_job_from_gbt(gbt *GbtDgbContext, rxmsg *msg.GbtMsg) []byte {
	var sjob msg.StratumJob

	sjob.Job_id = ""
	sjob.IsClean = false
	if rxmsg.Height != gbt.last_gbt.Height {
		sjob.IsClean = true
	}
	vb := make([]byte, 4)
	binary.LittleEndian.PutUint32(vb, uint32(rxmsg.Version))
	sjob.Version = int32(binary.BigEndian.Uint32(vb))
	sjob.PrevblockS = rxmsg.Previousblockhash
	pb, err := hex.DecodeString(rxmsg.Previousblockhash)
	sjob.PrevblockBig = hex.EncodeToString(utility.Reverse(utility.Convert_big_endian(pb)))
	bb, _ := hex.DecodeString(rxmsg.Bits)
	sjob.Bits = binary.LittleEndian.Uint32(bb)
	sjob.BitsS = rxmsg.Bits
	tb := make([]byte, 4)
	binary.BigEndian.PutUint32(tb, rxmsg.Curtime)
	t := binary.LittleEndian.Uint32(tb)
	sjob.Timestamp = time.Unix(int64(t), 0)
	sjob.Mintime = rxmsg.Mintime
	sjob.Extranonce2_size = 4
	sjob.Target = rxmsg.Target
	sjob.Height = rxmsg.Height
	sjob.Segwit = rxmsg.Segwit
	cbvalue := rxmsg.Coinbasevalue
	var nowit bool = false
	tslen_nowit := len(*rxmsg.Transactions)
	for i := 0; i < tslen_nowit; i++ {
		var ts msg.GbtTransaction
		err = json.Unmarshal((*rxmsg.Transactions)[i], &ts)
		if err != nil {
			logg.Error("[job]", zap.String("getblocktemplate", err.Error()))
			continue
		}
		//logg.Info("[job]", zap.String("transaction", ts.Hash))
		if ts.Hash == ts.Txid {
		} else {
			nowit = true
		}
		cbvalue -= uint64(ts.Fee)
	}
	if nowit {
		rxmsg.Coinbasevalue = cbvalue
	}

	payaddr := gbt.Config.Dgb.Addr
	if len(gbt.GbtCtx.MinerAddrs) > 0 {
		payaddr = gbt.GbtCtx.MinerAddrs[gbt.GbtCtx.MinerAddrIndex]
		gbt.GbtCtx.MinerAddrIndex = gbt.GbtCtx.MinerAddrIndex + 1
		if gbt.GbtCtx.MinerAddrIndex >= len(gbt.GbtCtx.MinerAddrs) {
			gbt.GbtCtx.MinerAddrIndex = 0
		}
	}

	c1, c2, err := build_coinbase(rxmsg, payaddr)

	if err != nil {
		return nil
	}

	sjob.Coinbase1 = c1
	sjob.Coinbase2 = c2
	//log.Println("coinbase1", c1, "coinbase2", c2)
	tslen := len(*rxmsg.Transactions)
	if nowit {
		tslen = 0
	}

	trans := make([]string, tslen)
	transdata := make([]string, tslen)
	for i := 0; i < tslen; i++ {
		var ts msg.GbtTransaction
		err = json.Unmarshal((*rxmsg.Transactions)[i], &ts)
		if err != nil {
			logg.Error("[job]", zap.String("getblocktemplate", err.Error()))
			continue
		}
		//logg.Info("[job]", zap.String("transaction", ts.Hash))
		if ts.Hash == ts.Txid {
			trans[i] = ts.Hash
		} else {
			trans[i] = ts.Txid
		}
		transdata[i] = ts.Data
	}
	if tslen > 1 {
		sjob.Transactions = BuildMerkleTreeStore(&trans)
	} else {
		if tslen == 1 {
			trans[0], err = utility.ReverseS(trans[0])
		}
		sjob.Transactions = &trans
	}
	sjob.TransData = &transdata

	body, err := json.Marshal(sjob)
	if err != nil {
		logg.Error("[job]", zap.String("Marshal", err.Error()))
		return nil
	}
	return body

}

func address_to_pubkeytxn3(in string) string {
	//log.Println("[job]payee", in)
	decoded, _, err := base58.CheckDecode(in)
	if err != nil {
		logg.Info("[job]", zap.String("CheckDecode", err.Error()))
	}
	//log.Println("[job]base58", encoded)
	pubkeytxn := "76a914"
	pubkeytxn += hex.EncodeToString(decoded)
	pubkeytxn += "88ac"
	return pubkeytxn
}

func build_coinbase(gbt *msg.GbtMsg, poolpayoutaddr string) (string, string, error) {

	var coinbase1 string
	coinbase1 = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff"

	signheader := "/m2pool.com/"
	var coinbase2 string = "0c" + hex.EncodeToString([]byte(signheader))

	coinbase2 += "0000000001"

	coinbase1 += "20"
	heightb := make([]byte, 4)
	binary.LittleEndian.PutUint32(heightb, gbt.Height)
	/*if gbt.Height < 0x100 {
		coinbase1 += "01"
	} else {
		if gbt.Height < 0x10000 {
			coinbase1 += "02"
		} else {
			if gbt.Height < 0x1000000 {
				coinbase1 += "03"
			} else {
				coinbase1 += "04"
			}
		}
	}*/
	coinbase1 += "04"

	coinbase1 += hex.EncodeToString(heightb)[:8]
	timeb := make([]byte, 4)
	binary.LittleEndian.PutUint32(timeb, uint32(time.Now().Unix()))
	coinbase1 += "04"
	coinbase1 += hex.EncodeToString(timeb)
	coinbase1 += "08"

	logg.Info("[job]", zap.String("coinbase1", coinbase1))

	coinbasevalueb := make([]byte, 8)
	binary.LittleEndian.PutUint64(coinbasevalueb, gbt.Coinbasevalue)
	coinbase2 += hex.EncodeToString(coinbasevalueb)
	coinbase2 += "19"
	//coinbase2 += address_to_pubkeytxn22(poolpayoutaddr)
	//coinbase2 += address_to_pubkeytxn2(poolpayoutaddr)
	coinbase2 += address_to_pubkeytxn3(poolpayoutaddr)
	//coinbase2 += "0000000000000000"
	//witlen := fmt.Sprintf("%02x", len(gbt.Segwit)/2)
	//coinbase2 += witlen
	//coinbase2 += gbt.Segwit

	coinbase2 += "00000000"
	logg.Info("[job]", zap.String("coinbase2", coinbase2))
	return coinbase1, coinbase2, nil
}

func nextPowerOfTwo(n int) int {
	// Return the number if it's already a power of 2.
	if n&(n-1) == 0 {
		return int(math.Log2(float64(n)))
	}

	// Figure out and return the next power of two.
	exponent := int(math.Log2(float64(n))) + 1
	return exponent // 2^exponent
}

func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash {
	// Check for nil parameters
	if left == nil || right == nil {
		return nil // or handle error as appropriate
	}
	// Concatenate the left and right nodes.
	var hash [chainhash.HashSize * 2]byte
	copy(hash[:chainhash.HashSize], left[:])
	copy(hash[chainhash.HashSize:], right[:])

	newHash := chainhash.DoubleHashH(hash[:])
	return &newHash
}

func BuildMerkleTreeStore(transactions *[]string) *[]string {
	// Calculate how many entries are required to hold the binary merkle
	// tree as a linear array and create an array of that size.
	ts_size := len(*transactions)
	if ts_size == 1 {
		ts_string := make([]string, 1)
		ts_string[0], _ = utility.ReverseS((*transactions)[0])
		return &ts_string
	}
	exp := nextPowerOfTwo(ts_size + 1)

	var all_size int = 0
	var temp_size = ts_size - 1
	var size int = 0
	for i := 0; i < exp-1; i++ {
		size = 1 + i + temp_size/2 + temp_size%2

		if size < exp {
			size = exp
		}

		all_size += size
		temp_size = size - i - 2

	}
	arraySize := ts_size + all_size

	merkles := make([]*chainhash.Hash, arraySize)

	// Create the base transaction hashes and populate the array with them.

	for i := range *transactions {
		// If we're computing a witness merkle root, instead of the
		// regular txid, we use the modified wtxid which includes a
		// transaction's witness data within the digest. Additionally,
		// the coinbase's wtxid is all zeroes.

		merkles[i], _ = chainhash.NewHashFromStr((*transactions)[i])
	}

	// Start the array offset after the last transaction and adjusted to the
	// next power of two.
	offset := ts_size

	all_size = ts_size
	size = ts_size
	var start int = 0

	temp_size = ts_size - 1
	for i := 0; i < exp-1; i++ {
		for j := 0; j <= i; j++ {
			merkles[offset] = merkles[start]
			offset++
			start++
		}
		for ; start < all_size; start += 2 {
			if start+1 >= all_size {
				newHash := HashMerkleBranches(merkles[start], merkles[start])
				merkles[offset] = newHash
				offset++
				start++
				break
			} else {
				newHash := HashMerkleBranches(merkles[start], merkles[start+1])
				merkles[offset] = newHash
				offset++
			}
		}
		size = 1 + i + temp_size/2 + temp_size%2

		if size <= exp {
			size = exp
		}
		all_size += size
		temp_size = size - i - 2

	}

	merkles_string := make([]string, exp)
	var j int = 0
	for i := offset - int(exp); i < offset; i++ {
		merkles_string[j], _ = utility.ReverseS(merkles[i].String())
		j++
	}

	logg.Info("[job]", zap.Any("merkles bytes", merkles_string))
	return &merkles_string
}
