package wrapper

import (
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
	"github.com/golang/protobuf/proto"
)

type BlkWrapper struct {
	blk  *nebulav1.Block
	hash common.Hash
}

func NewBlkWrapper(blk *nebulav1.Block) *BlkWrapper {
	return &BlkWrapper{blk: blk}
}

func (b *BlkWrapper) Hash() common.Hash {
	if b.hash == (common.Hash{}) {
		b.hash = b.calcHash()
	}
	return b.hash
}

func (b *BlkWrapper) Clone() *nebulav1.Block {
	return proto.Clone(b.blk).(*nebulav1.Block)
}

func (b *BlkWrapper) Block() *nebulav1.Block {
	return b.blk
}

func (b *BlkWrapper) Height() uint64 {
	return b.blk.Header.Height
}

func (b *BlkWrapper) Transactions() []*nebulav1.Transaction {
	if b.blk.Transactions != nil {
		return b.blk.Transactions.Txs
	}
	return []*nebulav1.Transaction{}
}

func TxTypeFilter(txType nebulav1.TxType) func(*nebulav1.Transaction) bool {
	return func(tx *nebulav1.Transaction) bool {
		return tx.TxType == txType
	}
}

func (b *BlkWrapper) FilterTransactions(filter func(*nebulav1.Transaction) bool) []*nebulav1.Transaction {
	var filtered []*nebulav1.Transaction
	for _, tx := range b.Transactions() {
		if filter(tx) {
			filtered = append(filtered, tx)
		}
	}
	return filtered
}

func (b *BlkWrapper) ParentHash() common.Hash {
	return common.BytesToHash(b.blk.Header.ParentHash)
}

func (b *BlkWrapper) Time() uint64 {
	return b.blk.Header.Timestamp
}

func (b *BlkWrapper) NumberU64() uint64 {
	return b.Height()
}

func (b *BlkWrapper) Header() *nebulav1.BlockHeader {
	return b.blk.Header
}

func (b *BlkWrapper) calcHash() common.Hash {
	nblk := b.Clone()
	nblk.Header.Signature = nil

	data, _ := proto.Marshal(nblk)
	return crypto.Keccak256Hash(data)
}
