package process

import (
	"go-ethereum-advance/common"
	"go-ethereum-advance/consensus"
	"go-ethereum-advance/core"
	"go-ethereum-advance/core/executionpool"
	"go-ethereum-advance/core/state"
	"go-ethereum-advance/core/transcut"
	"go-ethereum-advance/core/types"
	"go-ethereum-advance/core/vm"
	"go-ethereum-advance/log"
	"go-ethereum-advance/params"
)

type Result struct {
	Receipt types.Receipt //
	Ed      ErrorDetail
}

type Process interface {
	//执行批次交易并返回结果以及读写集
	ExecBenchTxs(block *types.Block,statedb *state.StateDB,txs []*types.Transaction, gp *core.GasPool,cfg vm.Config) []*Result
	//执行单一交易并返回结果以及读写集
	ExecSingleTx(block *types.Block,tx *types.Transaction,chainConf *params.ChainConfig,author *common.Address, gp *core.GasPool,usedGass *uint64) *Result
	//检查传递进来的读写集是否有冲突
	RWSetCanBeWritten(c *vm.CacheSet) ([]common.Hash,bool)
}

type Processor struct {
	config *params.ChainConfig // Chain configuration options
	bc     *core.BlockChain         // Canonical block chain
	engine consensus.Engine    // Consensus engine used for block rewards
}

type ErrorDetail struct {
	err  error
	tx   *types.Transaction
}

func NewProcessor(config *params.ChainConfig, bc *core.BlockChain, engine consensus.Engine) *Processor {
	return &Processor{
		config: config,
		bc:     bc,
		engine: engine,
	}
}

func (p *Processor) ExecBenchTxs(block *types.Block,statedb *state.StateDB,txs []*types.Transaction,authors []*common.Address ,gp *core.GasPool,cfg vm.Config) []*Result  {
	var (
		results     []*Result
		header      = block.Header()
		usedGas     = new(uint64)
		blockHash   = block.Hash()
		blockNumber = block.Number()
		errs        []*ErrorDetail
	)

	blockContext := core.NewEVMBlockContext(header, p.bc, nil)
	vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
	cache := transcut.NewFromMarkCache()
	for i, tx := range txs {
		msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)
		if err != nil {
			ed := &ErrorDetail{
				err: err,
				tx: tx,
			}
			errs = append(errs,ed)
			log.Error("tx asMessage failed","err",err.Error(),"tx info",tx.Hash().String())
			continue
		}
		item := transcut.NewItem(tx,msg,authors[i])
		from := msg.From()
		cache.SetMarkCache(from,item)
	}

	//TODO modify by echo
	level := cache.GetMaxLevel()
	for i:=0;i < int(level);i++ {
		items := cache.GetLevelItemWithMark(uint32(i))
		pool := executionpool.NewPool(len(items))
		for _,item := range items{
			task := executionpool.NewTask(p.config,p.bc,nil,gp,statedb,blockNumber,blockHash,item,usedGas,vmenv,i)
			pool.AddTask(*task)
		}

		//需要等待第一批次的交易都执行完自开启下一批次
		for i:=0;i< len(items);i++ {
			select {
			case v := <- pool.ResultChan:
				ed := ErrorDetail{
					err: v.Err,
					tx: v.Tx,
				}
				result := &Result{
					Receipt: *v.Receipt,
					Ed: ed,
				}
				results = append(results,result)
			}
		}
	}
	return results
}

func (p *Processor) ExecSingleTx(config *params.ChainConfig, bc core.ChainContext, author *common.Address, gp *core.GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) *Result  {
	var (
		usedGas     = new(uint64)
	)
	receipt,err := core.ApplyTransaction(config,bc,author,gp,statedb,header,tx,usedGas,cfg)
	if err != nil {
		log.Error("ExecSingleTx err","err",err.Error(),"tx info",tx.Hash().String())
		return &Result{
			Receipt: nil,
			Ed: ErrorDetail{
				err: err,
				tx: tx,
			},
		}
	}
	return &Result{
		Receipt: *receipt,
		Ed: ErrorDetail{
			err: nil,
			tx: tx,
		},
	}
}

type WrittenBefore struct {
	written  map[common.Hash]struct{}
}
/**
对同一批次级别的读写集进行检测
有读写冲突的交易需要拿出来单独执行、
所谓读写冲突是，在当前的读集中在之前的写集给予修改
**/
func (p *Processor) RWSetCanBeWritten(c *vm.CacheSet) ([]common.Hash,bool)  {
	maxLevel := c.GetMaxLevel()
	res := make([]common.Hash,0)
	flag := true
	for i:=0;i<maxLevel;i++ {
		tmpCache := c.GetRecordsWithLevel(i)
		wb := make(map[common.Hash]struct{})
		for _,key := range tmpCache.Order {
			record,ok := tmpCache.GetRecordWithTxHash(key)
			if !ok {
				log.Info("没有找到对应的读写集","level",i,"txInfo",key.String())
				continue
			}

			for k,_ := range record.ReadRecord {
				_,ok := wb[k]
				if ok {
					//在读集中发现了之前有写集修改过的记录
					res = append(res,key)
				}
			}

			for key,_ := range record.WriteRecord {
				wb[key] = struct{}{}
			}
		}
	}
	if len(res) > 0 {
		flag = false
	}
	return res,flag
}

