package pieces

import (
	// "fmt"

	"github.com/minio/sha256-simd"

	"fil_integrate/build/cid"
	"fil_integrate/build/state-types/abi"
)

const (
	LEFT  byte = 0
	RIGHT byte = 1
)

type MerkleProof struct {
	Data []cid.Commit
	Path []byte
}

func (proof *MerkleProof) Add(hash cid.Commit, path byte) {
	proof.Data = append(proof.Data, hash)
	proof.Path = append(proof.Path, path)
}

func (proof *MerkleProof) Verify(data []byte, finalRoot cid.Commit, dataOffset abi.PaddedPieceSize) (bool, error) {
	ok := proof.verifyMerklePath(dataOffset, abi.UnpaddedPieceSize(len(data)).Padded())
	if !ok {
		return false, nil
	}

	root, err := GeneratePieceCommitmentFast(data, abi.UnpaddedPieceSize(len(data)))
	if err != nil {
		return false, err
	}
	for index, hash := range proof.Data {
		h := sha256.New()

		if proof.Path[index] == LEFT {
			h.Write(hash[:])
			h.Write(root[:])
		} else {
			h.Write(root[:])
			h.Write(hash[:])
		}
		res := h.Sum(nil)
		trim_to_fr32(res[:32])
		copy(root[:], res)
	}
	return root == finalRoot, nil
}

func (proof *MerkleProof) verifyMerklePath(offset abi.PaddedPieceSize, size abi.PaddedPieceSize) bool {
	for size > 1 {
		size >>= 1
		offset >>= 1
	}

	for _, path := range proof.Path {
		index := (offset >> 1) << 1
		if index != offset {
			if path != LEFT {
				return false
			}
		} else {
			if path != RIGHT {
				return false
			}
		}
		offset >>= 1
	}

	return true
}