package equity

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"errors"

	"github.com/vapor/protocol/vm"

	"github.com/vapor/equity/compiler"
)

const (
	firstClauseShift string = "00000000"
	endingClauseName string = "ending"
)

// Shift statistics contract clause's offset
func Shift(contract *compiler.Contract) (map[string]string, error) {
	clauseMap := make(map[string]string)
	if len(contract.Clauses) == 1 {
		clauseMap[contract.Clauses[0].Name] = firstClauseShift
		return clauseMap, nil
	}

	instructions, err := vm.ParseProgram(contract.Body)
	if err != nil {
		return nil, err
	}

	var jumpifData [][]byte
	for i, inst := range instructions {
		if inst.Op.String() == "JUMPIF" {
			if i > 0 && instructions[i-1].Op.String() == "NOP" {
				continue
			}
			jumpifData = append([][]byte{inst.Data}, jumpifData...)
		}
	}

	// Check the number of contract's clause
	if len(contract.Clauses) != len(jumpifData)+1 {
		return nil, errors.New("the number of contract's clause is not equal to the number of jumpif instruction")
	}

	for i, clause := range contract.Clauses {
		if i == 0 {
			clauseMap[clause.Name] = firstClauseShift
			continue
		}
		clauseMap[clause.Name] = hex.EncodeToString(jumpifData[i-1])
	}

	var buffer bytes.Buffer
	if err := binary.Write(&buffer, binary.LittleEndian, uint32(len(contract.Body))); err != nil {
		return nil, err
	}
	clauseMap[endingClauseName] = hex.EncodeToString(buffer.Bytes())

	return clauseMap, nil
}
