package seal

import(
	"bufio"
	"context"
	"io"
	"os"
	"runtime"
	"sync"
	"path/filepath"
	"bytes"

	"golang.org/x/xerrors"

	logging "github.com/ipfs/go-log/v2"
	commcid "github.com/filecoin-project/go-fil-commcid"
	ffi "github.com/filecoin-project/filecoin-ffi"
	"github.com/filecoin-project/go-state-types/abi"
	"github.com/filecoin-project/go-state-types/network"
	"github.com/ipfs/go-cid"

	"fil_integrate/build/fr32"
	"fil_integrate/build/storiface"
	"fil_integrate/build/storage"
	spproof "fil_integrate/build/proof"
)

var log = logging.Logger("sealing")

//32字节，总共256位
//[has_pre][MetaLen1..MetaLen4][PieceLen1..PieceLen4]
const TagLen int = 8

const NewestNetworkVersion = network.Version13

type Sealer struct{
	sectors  SectorProvider
}

var _ SectorSealer = &Sealer{}

func New(sectors SectorProvider) (*Sealer, error) {
	sb := &Sealer{
		sectors: sectors,
	}

	return sb, nil
}

func (sb *Sealer)GetCommRFromDir(sectorID abi.SectorID) (cid.Cid, error) {
	commr := make([]byte, 32)
	path := filepath.Join(sb.sectors.GetRoot(), "cache", storiface.SectorName(sectorID), "commr")
	out, err := os.OpenFile(path, os.O_RDONLY, 0644)
	if err != nil{
		return cid.Cid{}, err
	}
	defer out.Close()

	_, err = out.Read(commr[:])
	if err != nil{
		return cid.Cid{}, err
	}
	return commcid.ReplicaCommitmentV1ToCID(commr[:])
}

func (sb *Sealer)PutCommRIntoDir(sectorID abi.SectorID, sealedCID cid.Cid) error {
	commr, err:= commcid.CIDToReplicaCommitmentV1(sealedCID)
	if err != nil{
		return err
	}

	path := filepath.Join(sb.sectors.GetRoot(), "cache", storiface.SectorName(sectorID), "commr")
	out, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0644)
	if err != nil{
		return err
	}
	defer out.Close()


	_, err = out.Write(commr[:])
	if err != nil{
		return err
	}
	return nil
}

func (sb *Sealer)AddPiece( 
	ctx context.Context, 
	sector storage.SectorRef, 
	existingPieceSizes []abi.UnpaddedPieceSize, 
	pieceSize abi.UnpaddedPieceSize, 
	file storage.Data, 
	numbers ...int32,
) (abi.PieceInfo, error) {
	chunk := abi.PaddedPieceSize(4 << 20)
	parallel := runtime.NumCPU()

	var offset abi.UnpaddedPieceSize
	for _, size := range existingPieceSizes {
		offset += size
	}

	ssize, err := sector.ProofType.SectorSize()
	if err != nil {
		return abi.PieceInfo{}, err
	}

	maxPieceSize := abi.PaddedPieceSize(ssize)

	if offset.Padded()+pieceSize.Padded() > maxPieceSize {
		return abi.PieceInfo{}, xerrors.Errorf("can't add %d byte piece to sector %v with %d bytes of existing pieces", pieceSize, sector, offset)
	}

	var done func()
	var stagedFile *partialFile

	defer func() {
		if done != nil {
			done()
		}

		if stagedFile != nil {
			if err := stagedFile.Close(); err != nil {
				log.Errorf("closing staged file: %+v", err)
			}
		}
	}()

	var stagedPath storiface.SectorPaths
	if len(existingPieceSizes) == 0 {
		stagedPath, done, err = sb.sectors.AcquireSector(ctx, sector, 0, storiface.FTUnsealed, storiface.PathSealing, numbers...)
		if err != nil {
			return abi.PieceInfo{}, xerrors.Errorf("acquire unsealed sector: %w", err)
		}

		stagedFile, err = createPartialFile(maxPieceSize, stagedPath.Unsealed)
		if err != nil {
			return abi.PieceInfo{}, xerrors.Errorf("creating unsealed sector file: %w", err)
		}
	} else {
		stagedPath, done, err = sb.sectors.AcquireSector(ctx, sector, storiface.FTUnsealed, 0, storiface.PathSealing, numbers...)
		if err != nil {
			return abi.PieceInfo{}, xerrors.Errorf("acquire unsealed sector: %w", err)
		}

		stagedFile, err = openPartialFile(maxPieceSize, stagedPath.Unsealed)
		if err != nil {
			return abi.PieceInfo{}, xerrors.Errorf("opening unsealed sector file: %w", err)
		}
	}

	w, err := stagedFile.Writer(storiface.UnpaddedByteIndex(offset).Padded(), pieceSize.Padded())
	if err != nil {
		return abi.PieceInfo{}, xerrors.Errorf("getting partial file writer: %w", err)
	}

	pw := fr32.NewPadWriter(w)

	pr := io.TeeReader(io.LimitReader(file, int64(pieceSize)), pw)

	throttle := make(chan []byte, parallel)
	piecePromises := make([]func() (abi.PieceInfo, error), 0)

	buf := make([]byte, chunk.Unpadded())
	for i := 0; i < parallel; i++ {
		if abi.UnpaddedPieceSize(i)*chunk.Unpadded() >= pieceSize {
			break // won't use this many buffers
		}
		throttle <- make([]byte, chunk.Unpadded())
	}

	for {
		var read int
		for rbuf := buf; len(rbuf) > 0; {
			n, err := pr.Read(rbuf)
			if err != nil && err != io.EOF {
				return abi.PieceInfo{}, xerrors.Errorf("pr read error: %w", err)
			}

			rbuf = rbuf[n:]
			read += n

			if err == io.EOF {
				break
			}
		}
		if read == 0 {
			break
		}

		done := make(chan struct {
			cid.Cid
			error
		}, 1)
		pbuf := <-throttle
		copy(pbuf, buf[:read])

		go func(read int) {
			defer func() {
				throttle <- pbuf
			}()

			c, err := sb.pieceCid(sector.ProofType, pbuf[:read])
			done <- struct {
				cid.Cid
				error
			}{c, err}
		}(read)

		piecePromises = append(piecePromises, func() (abi.PieceInfo, error) {
			select {
			case e := <-done:
				if e.error != nil {
					return abi.PieceInfo{}, e.error
				}

				return abi.PieceInfo{
					Size:     abi.UnpaddedPieceSize(len(buf[:read])).Padded(),
					PieceCID: e.Cid,
				}, nil
			case <-ctx.Done():
				return abi.PieceInfo{}, ctx.Err()
			}
		})
	}

	if err := pw.Close(); err != nil {
		return abi.PieceInfo{}, xerrors.Errorf("closing padded writer: %w", err)
	}

	if err := stagedFile.MarkAllocated(storiface.UnpaddedByteIndex(offset).Padded(), pieceSize.Padded()); err != nil {
		return abi.PieceInfo{}, xerrors.Errorf("marking data range as allocated: %w", err)
	}

	if err := stagedFile.Close(); err != nil {
		return abi.PieceInfo{}, err
	}
	stagedFile = nil

	if len(piecePromises) == 1 {
		return piecePromises[0]()
	}

	pieceCids := make([]abi.PieceInfo, len(piecePromises))
	for i, promise := range piecePromises {
		pieceCids[i], err = promise()
		if err != nil {
			return abi.PieceInfo{}, err
		}
	}

	pieceCID, err := ffi.GenerateUnsealedCID(sector.ProofType, pieceCids)
	if err != nil {
		return abi.PieceInfo{}, xerrors.Errorf("generate unsealed CID: %w", err)
	}

	// validate that the pieceCID was properly formed
	if _, err := commcid.CIDToPieceCommitmentV1(pieceCID); err != nil {
		return abi.PieceInfo{}, err
	}

	return abi.PieceInfo{
		Size:     pieceSize.Padded(),
		PieceCID: pieceCID,
	}, nil

}

func (sb *Sealer) pieceCid(spt abi.RegisteredSealProof, in []byte) (cid.Cid, error) {
	prf, werr, err := ToReadableFile(bytes.NewReader(in), int64(len(in)))
	if err != nil {
		return cid.Undef, xerrors.Errorf("getting tee reader pipe: %w", err)
	}

	pieceCID, err := ffi.GeneratePieceCIDFromFile(spt, prf, abi.UnpaddedPieceSize(len(in)))
	if err != nil {
		return cid.Undef, xerrors.Errorf("generating piece commitment: %w", err)
	}

	_ = prf.Close()

	return pieceCID, werr()
}

func ToReadableFile(r io.Reader, n int64) (*os.File, func() error, error) {
	f, ok := r.(*os.File)
	if ok {
		return f, func() error { return nil }, nil
	}

	var w *os.File

	f, w, err := os.Pipe()
	if err != nil {
		return nil, nil, err
	}

	var wait sync.Mutex
	var werr error

	wait.Lock()
	go func() {
		defer wait.Unlock()

		var copied int64
		copied, werr = io.CopyN(w, r, n)
		if werr != nil {
			log.Warnf("toReadableFile: copy error: %+v", werr)
		}

		err := w.Close()
		if werr == nil && err != nil {
			werr = err
			log.Warnf("toReadableFile: close error: %+v", err)
			return
		}
		if copied != n {
			log.Warnf("copied different amount than expected: %d != %d", copied, n)
			werr = xerrors.Errorf("copied different amount than expected: %d != %d", copied, n)
		}
	}()

	return f, func() error {
		wait.Lock()
		return werr
	}, nil
}


func (sb *Sealer)UnsealedRange(
	ctx context.Context, 
	sid storage.SectorRef, 
	sectorSize abi.SectorSize, 
	ticket abi.SealRandomness, 
	commd cid.Cid, 
	out io.Writer, 
	offset storiface.UnpaddedByteIndex, 
	size abi.UnpaddedPieceSize,
) error {
	log.Infof("[%d] Unsealing sector", sid.ID.Number)
	{
		p, done, err := sb.sectors.AcquireSector(ctx, sid, storiface.FTUnsealed, storiface.FTNone, storiface.PathSealing)
		if err != nil {
			return xerrors.Errorf("acquire unsealed sector for removing: %w", err)
		}
		done()

		if err := os.Remove(p.Unsealed); err != nil {
			return xerrors.Errorf("removing unsealed sector: %w", err)
		}
	}

	err := sb.UnsealPiece(ctx, sid, 0, abi.PaddedPieceSize(sectorSize).Unpadded(), ticket, commd)
	if err != nil {
		return err
	}

	ok, err := sb.ReadPiece(ctx, out, sid, offset, size)
	if err != nil{
		return err
	}

	if !ok {
		return xerrors.Errorf("Read pieces error!")
	}

	return nil

}

func (sb *Sealer) UnsealPiece(ctx context.Context, sector storage.SectorRef, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, randomness abi.SealRandomness, commd cid.Cid) error {
	ssize, err := sector.ProofType.SectorSize()
	if err != nil {
		return err
	}
	maxPieceSize := abi.PaddedPieceSize(ssize)

	// try finding existing
	unsealedPath, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTUnsealed, storiface.FTNone, storiface.PathStorage)
	var pf *partialFile

	switch {
	case xerrors.Is(err, storiface.ErrSectorNotFound):
		unsealedPath, done, err = sb.sectors.AcquireSector(ctx, sector, storiface.FTNone, storiface.FTUnsealed, storiface.PathStorage)
		if err != nil {
			return xerrors.Errorf("acquire unsealed sector path (allocate): %w", err)
		}
		defer done()

		pf, err = createPartialFile(maxPieceSize, unsealedPath.Unsealed)
		if err != nil {
			return xerrors.Errorf("create unsealed file: %w", err)
		}

	case err == nil:
		defer done()

		// return nil

		pf, err = openPartialFile(maxPieceSize, unsealedPath.Unsealed)
		if err != nil {
			return xerrors.Errorf("opening partial file: %w", err)
		}
	default:
		return xerrors.Errorf("acquire unsealed sector path (existing): %w", err)
	}
	defer pf.Close() // nolint

	allocated, err := pf.Allocated()
	if err != nil {
		return xerrors.Errorf("getting bitruns of allocated data: %w", err)
	}

	toUnseal, err := computeUnsealRanges(allocated, offset, size)
	if err != nil {
		return xerrors.Errorf("computing unseal ranges: %w", err)
	}

	if !toUnseal.HasNext() {
		return nil
	}

	srcPaths, srcDone, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTCache|storiface.FTSealed, storiface.FTNone, storiface.PathStorage)
	if err != nil {
		return xerrors.Errorf("acquire sealed sector paths: %w", err)
	}
	defer srcDone()

	sealed, err := os.OpenFile(srcPaths.Sealed, os.O_RDONLY, 0644) // nolint:gosec
	if err != nil {
		return xerrors.Errorf("opening sealed file: %w", err)
	}
	defer sealed.Close() // nolint

	var at, nextat abi.PaddedPieceSize
	first := true
	for first || toUnseal.HasNext() {
		first = false

		piece, err := toUnseal.NextRun()
		if err != nil {
			return xerrors.Errorf("getting next range to unseal: %w", err)
		}

		at = nextat
		nextat += abi.PaddedPieceSize(piece.Len)

		if !piece.Val {
			continue
		}

		out, err := pf.Writer(offset.Padded(), size.Padded())
		if err != nil {
			return xerrors.Errorf("getting partial file writer: %w", err)
		}

		// <eww>
		opr, opw, err := os.Pipe()
		if err != nil {
			return xerrors.Errorf("creating out pipe: %w", err)
		}

		var perr error
		outWait := make(chan struct{})

		{
			go func() {
				defer close(outWait)
				defer opr.Close() // nolint

				padwriter := fr32.NewPadWriter(out)

				bsize := uint64(size.Padded())
				if bsize > uint64(runtime.NumCPU())*fr32.MTTresh {
					bsize = uint64(runtime.NumCPU()) * fr32.MTTresh
				}

				bw := bufio.NewWriterSize(padwriter, int(abi.PaddedPieceSize(bsize).Unpadded()))

				_, err := io.CopyN(bw, opr, int64(size))
				if err != nil {
					perr = xerrors.Errorf("copying data: %w", err)
					return
				}

				if err := bw.Flush(); err != nil {
					perr = xerrors.Errorf("flushing unpadded data: %w", err)
					return
				}

				if err := padwriter.Close(); err != nil {
					perr = xerrors.Errorf("closing padwriter: %w", err)
					return
				}
			}()
		}
		// </eww>

		// TODO: This may be possible to do in parallel
		err = ffi.UnsealRange(sector.ProofType,
			srcPaths.Cache,
			sealed,
			opw,
			sector.ID.Number,
			sector.ID.Miner,
			randomness,
			commd,
			uint64(at.Unpadded()),
			uint64(abi.PaddedPieceSize(piece.Len).Unpadded()))

		_ = opw.Close()

		if err != nil {
			return xerrors.Errorf("unseal range: %w", err)
		}

		select {
		case <-outWait:
		case <-ctx.Done():
			return ctx.Err()
		}

		if perr != nil {
			return xerrors.Errorf("piping output to unsealed file: %w", perr)
		}

		if err := pf.MarkAllocated(storiface.PaddedByteIndex(at), abi.PaddedPieceSize(piece.Len)); err != nil {
			return xerrors.Errorf("marking unsealed range as allocated: %w", err)
		}

		if !toUnseal.HasNext() {
			break
		}
	}

	return nil
}

func (sb *Sealer) ReadPiece(ctx context.Context, writer io.Writer, sector storage.SectorRef, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize) (bool, error) {
	path, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTUnsealed, storiface.FTNone, storiface.PathStorage)
	if err != nil {
		return false, xerrors.Errorf("acquire unsealed sector path: %w", err)
	}
	defer done()

	ssize, err := sector.ProofType.SectorSize()
	if err != nil {
		return false, err
	}
	maxPieceSize := abi.PaddedPieceSize(ssize)

	pf, err := openPartialFile(maxPieceSize, path.Unsealed)
	if err != nil {
		if xerrors.Is(err, os.ErrNotExist) {
			return false, nil
		}

		return false, xerrors.Errorf("opening partial file: %w", err)
	}

	ok, err := pf.HasAllocated(offset, size)
	if err != nil {
		_ = pf.Close()
		return false, err
	}

	if !ok {
		_ = pf.Close()
		return false, nil
	}

	f, err := pf.Reader(offset.Padded(), size.Padded())
	if err != nil {
		_ = pf.Close()
		return false, xerrors.Errorf("getting partial file reader: %w", err)
	}

	upr, err := fr32.NewUnpadReader(f, size.Padded())
	if err != nil {
		return false, xerrors.Errorf("creating unpadded reader: %w", err)
	}

	if _, err := io.CopyN(writer, upr, int64(size)); err != nil {
		_ = pf.Close()
		return false, xerrors.Errorf("reading unsealed file: %w", err)
	}

	if err := pf.Close(); err != nil {
		return false, xerrors.Errorf("closing partial file: %w", err)
	}

	return true, nil
}

// 没有测试
// func EncodeDataToPieces(sb *ffiwrapper.Sealer, ctx context.Context, sector SectorRef, sectorSize abi.SectorSize, file Data) (abi.PieceInfo, error) {
// 	var piecesID []byte
// 	var FinalPiece abi.PieceInfo
// 	var pieceNumber int32 = 1

// 	UnpaddedSectorSize := abi.PaddedPieceSize(sectorSize).Unpadded()
// 	buf := make([]byte, UnpaddedSectorSize)

// 	DataLen := UnpaddedSectorSize-TagLen

// 	for{
// 		copy(buf[:TagLen], 0)
// 		var MetaLen int = 0
// 		var rerr error
// 		for rbuf := buf[TagLen:]; len(rbuf) > 0; {
// 			n, rerr := file.read(buf[TagLen:])

// 			if rerr != nil && rerr != io.EOF{
// 				return nil, rerr
// 			}
// 			rbuf = rbuf[n:]
// 			MetaLen += n

// 			if rerr == io.EOF{
// 				break
// 			}
// 		}
// 		if rerr == io.EOF{
// 			//encode first sector
// 			pieceNumber, FinalPiece, err = EncodeData(buf, MetaLen, DataLen, piecesID, pieceNumber)
// 			if err != nil{
// 				return nil, err
// 			}
// 			break
// 		}
// 		copy(buf[:4], []byte(MetaLen))

// 		piece, err := sb.AddPiece(ctx, sector, nil, UnpaddedSectorSize, bytes.NewReader(buf), pieceNumber)
// 		if err != nil{
// 			return nil, err
// 		}
// 		piecesID = append(pieces, commcid.CIDToPieceCommitmentV1(piece.PieceCID)...)
// 		pieceNumber += 1
// 	}
// 	return pieceNumber, FinalPiece, nil
// }

// func EncodeData(buf []byte, MetaLen int, DataLen int, piecesID []byte, pieceNumber int32) (int32, abi.PieceInfo, error) {
// 	//encode first sector
// 	remain := len(piecesID)
// 	CommLen := min(remain, ((DataLen-MetaLen)/32) * 32)
// 	rbuf := buf[MetaLen+TagLen:]
	
//     copy(buf[:4], []byte(MetaLen))
// 	copy(buf[4:8], []byte(CommLen))
// 	copy(rbuf, piecesID[:CommLen])
// 	copy(rbuf[CommLen:], 0)

// 	piecesID = piecesID[CommLen:]
// 	MetaLen = 0
// 	remain -= CommLen
// 	rbuf = buf[TagLen+32:]

// 	prePiece, err := sb.AddPiece(ctx, sector, nil, UnpaddedSectorSize, bytes.NewReader(buf), pieceNumber)
// 	pieceNumber += 1
// 	if err != nil{
// 		return nil, err
// 	}

// 	for ;remain > 0; {
// 		//encode next n sector
// 		CommLen := min(remain, ((DataLen-32)/32) * 32)

// 		copy(buf[:4], []byte(MetaLen | 0x80000000))
// 		copy(buf[4:8], []byte(CommLen))
// 		copy(buf[8:40], commcid.CIDToPieceCommitmentV1(prePiece.PieceCID)...)
// 		copy(rbuf, piecesID[:CommLen])
// 		copy(rbuf[CommLen:], 0)

// 		piecesID = piecesID[CommLen:]
// 		MetaLen = 0
// 		remain -= CommLen
// 		rbuf = buf[TagLen:]

// 		prePiece, err = sb.AddPiece(ctx, sector, nil, UnpaddedSectorSize, bytes.NewReader(buf), pieceNumber)
// 		pieceNumber += 1
// 		if err != nil{
// 			return nil, err
// 		}
// 	}

// 	return pieceNumber, prePiece, nil
// }

// func DecodePieceToData(sb *ffiwrapper.Sealer, ctx context.Context, out io.Writer, finalPiece abi.PieceInfo, pieceNum int32, sectorSize abi.SectorSize) ([]abi.PieceInfo, error){
// 	var piecesID []abi.PieceInfo
// 	var commds []byte

// 	unpaddedSectorSize := abi.PaddedPieceSize(sectorSize).Unpadded()
// 	buf := make([]byte, unpaddedSectorSize)
// 	DataLen := unpaddedSectorSize-TagLen

// 	for ;pieceNum > 0; PieceNum-- {
// 		ok, err := sb.ReadPiece(ctx, sid, bytes.NewWriter(buf), 0, unpaddedSectorSize, pieceNum)
// 		if err != nil {
// 			return nil
// 		}
// 		if !ok {
// 			return xerrors.Errorf("Read pieces error!")
// 		}

// 		MetaLen := buf[0:4] & 0x7fffffff
// 		HasPre := buf[0:4] >> 31
// 		CommLen := buf[4:8]
// 		rbuf := buf[8:]

// 		if HasPre {
// 			prePiece := buf[8:40]
// 			CommLen -= 32
// 			rbuf = rbuf[32:]
// 			commd, _ := commcid.DataCommitmentV1ToCID(prePiece)
// 			piecesID = append(piecesID, abi.PieceInfo{abi.PaddedPieceSize(sectorSize), commd})
// 		}
// 		data := rbuf[:MetaLen]
// 		commds = append(commds, rbuf[MetaLen:MetaLen+CommLen]...)
// 		//**data顺序错了，需要
// 		n, werr := out.write(data[:])
// 		if werr != nil {
// 			return nil, werr
// 		}
// 	}

// 	for cbuf := commds; len(cbuf) > 0; {
// 		commd, _ := cbuf.DataCommitmentV1ToCID(commds[32])
// 		piecesID = append(piecesID, abi.PieceInfo{abi.PaddedPieceSize(sectorSize), commd})
// 		cbuf = cbuf[32:]
// 	}
// 	return piecesID, nil
// }

func (sb *Sealer)CheckPieceAndDataRoot(
	sid storage.SectorRef, 
	commd cid.Cid, 
	pieces []abi.PieceInfo,
) (bool, error) {
	UnsealedCID, err := ffi.GenerateUnsealedCID(sid.ProofType, pieces)
	if err != nil{
		return false, err
	}

	return commd == UnsealedCID, nil
}

func (sb *Sealer) SealPreCommit1(ctx context.Context, sector storage.SectorRef, ticket abi.SealRandomness, pieces []abi.PieceInfo) (out storage.PreCommit1Out, err error) {
	// ffi.say_hello()
	paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTUnsealed, storiface.FTSealed|storiface.FTCache, storiface.PathSealing)
	if err != nil {
		return nil, xerrors.Errorf("acquiring sector paths: %w", err)
	}
	defer done()

	e, err := os.OpenFile(paths.Sealed, os.O_RDWR|os.O_CREATE, 0644) // nolint:gosec
	if err != nil {
		return nil, xerrors.Errorf("ensuring sealed file exists: %w", err)
	}
	if err := e.Close(); err != nil {
		return nil, err
	}

	if err := os.Mkdir(paths.Cache, 0755); err != nil { // nolint
		if os.IsExist(err) {
			log.Warnf("existing cache in %s; removing", paths.Cache)

			if err := os.RemoveAll(paths.Cache); err != nil {
				return nil, xerrors.Errorf("remove existing sector cache from %s (sector %d): %w", paths.Cache, sector, err)
			}

			if err := os.Mkdir(paths.Cache, 0755); err != nil { // nolint:gosec
				return nil, xerrors.Errorf("mkdir cache path after cleanup: %w", err)
			}
		} else {
			return nil, err
		}
	}

	var sum abi.UnpaddedPieceSize
	for _, piece := range pieces {
		sum += piece.Size.Unpadded()
	}
	ssize, err := sector.ProofType.SectorSize()
	if err != nil {
		return nil, err
	}
	ussize := abi.PaddedPieceSize(ssize).Unpadded()
	if sum != ussize {
		return nil, xerrors.Errorf("aggregated piece sizes don't match sector size: %d != %d (%d)", sum, ussize, int64(ussize-sum))
	}

	// TODO: context cancellation respect
	p1o, err := ffi.SealPreCommitPhase1(
		sector.ProofType,
		paths.Cache,
		paths.Unsealed,
		paths.Sealed,
		sector.ID.Number,
		sector.ID.Miner,
		ticket,
		pieces,
	)
	if err != nil {
		return nil, xerrors.Errorf("presealing sector %d (%s): %w", sector.ID.Number, paths.Unsealed, err)
	}
	return p1o, nil
}

func (sb *Sealer) SealPreCommit2(ctx context.Context, sector storage.SectorRef, phase1Out storage.PreCommit1Out) (storage.SectorCids, error) {
	paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTSealed|storiface.FTCache, 0, storiface.PathSealing)
	if err != nil {
		return storage.SectorCids{}, xerrors.Errorf("acquiring sector paths: %w", err)
	}
	defer done()

	sealedCID, unsealedCID, err := ffi.SealPreCommitPhase2(phase1Out, paths.Cache, paths.Sealed)
	if err != nil {
		return storage.SectorCids{}, xerrors.Errorf("presealing sector %d (%s): %w", sector.ID.Number, paths.Unsealed, err)
	}

	return storage.SectorCids{
		Unsealed: unsealedCID,
		Sealed:   sealedCID,
	}, nil
}

func (sb *Sealer) SealCommit1(ctx context.Context, sector storage.SectorRef, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) {
	paths, done, err := sb.sectors.AcquireSector(ctx, sector, storiface.FTSealed|storiface.FTCache, 0, storiface.PathSealing)
	if err != nil {
		return nil, xerrors.Errorf("acquire sector paths: %w", err)
	}
	defer done()
	output, err := ffi.SealCommitPhase1(
		sector.ProofType,
		cids.Sealed,
		cids.Unsealed,
		paths.Cache,
		paths.Sealed,
		sector.ID.Number,
		sector.ID.Miner,
		ticket,
		seed,
		pieces,
	)
	if err != nil {
		log.Warn("StandaloneSealCommit error: ", err)
		log.Warnf("num:%d tkt:%v seed:%v, pi:%v sealedCID:%v, unsealedCID:%v", sector.ID.Number, ticket, seed, pieces, cids.Sealed, cids.Unsealed)

		return nil, xerrors.Errorf("StandaloneSealCommit: %w", err)
	}
	return output, nil
}

func (sb *Sealer) SealCommit2(ctx context.Context, sector storage.SectorRef, phase1Out storage.Commit1Out) (storage.Proof, error) {
	return ffi.SealCommitPhase2(phase1Out, sector.ID.Number, sector.ID.Miner)
}

func (sb *Sealer)Sealed( 
	ctx context.Context,
	sid storage.SectorRef, 
	seed abi.InteractiveSealRandomness, 
	ticket abi.SealRandomness, 
	pieces []abi.PieceInfo,
) (storage.SectorCids, storage.Proof, error) {
	// var sealedSectors spproof.SectorInfo

	log.Infof("[%d] Running replication(1)...", sid.ID.Number)

	pc1out, err := sb.SealPreCommit1(ctx, sid, ticket, pieces)
	if err != nil {
		return storage.SectorCids{}, nil, xerrors.Errorf("commit: %w", err)
	}

	log.Infof("[%d] Running replication(2)...", sid.ID.Number)
	cids, err := sb.SealPreCommit2(ctx, sid, pc1out)
	if err != nil {
		return storage.SectorCids{}, nil, xerrors.Errorf("commit: %w", err)
	}

	log.Infof("[%d] Generating PoRep for sector (1)", sid.ID.Number)
	c1o, err := sb.SealCommit1(ctx, sid, ticket, seed, pieces, cids)
	if err != nil {
		return storage.SectorCids{}, nil, err
	}

	log.Infof("[%d] Generating PoRep for sector (2)", sid.ID.Number)

	var proof storage.Proof
	proof, err = sb.SealCommit2(ctx, sid, c1o)
	if err != nil {
		return storage.SectorCids{}, nil, err
	}

	return cids, proof, nil
}

func (sb *Sealer)AggregateSealProofs(aggregateInfo spproof.AggregateSealVerifyProofAndInfos, proofs []storage.Proof) ([]byte, error){
	return ffi.AggregateSealProofs(aggregateInfo, proofs)
}

func (sb *Sealer)GenerateCommit2Proof( 
	ctx context.Context, 
	sid storage.SectorRef, 
	seed abi.InteractiveSealRandomness, 
	ticket abi.SealRandomness, 
	pieces []abi.PieceInfo, 
	cids storage.SectorCids,
) (storage.Proof, error) {
	c1out, err := sb.SealCommit1(ctx, sid, ticket, seed, pieces, cids)
	if err != nil {
		return nil, err
	}

	return sb.SealCommit2(ctx, sid, c1out)
}

func (sb *Sealer)GenerateWindowPoStProofs( 
	ctx context.Context, 
	minerID abi.ActorID, 
	sectorInfo []spproof.SectorInfo, 
	randomness abi.PoStRandomness,
) ([]spproof.PoStProof, []abi.SectorID, error) {
	randomness[31] &= 0x3f
	privsectors, skipped, done, err := sb.pubSectorToPriv(ctx, minerID, sectorInfo, nil, abi.RegisteredSealProof.RegisteredWindowPoStProof)
	if err != nil {
		return nil, nil, xerrors.Errorf("gathering sector info: %w", err)
	}
	defer done()

	if len(skipped) > 0 {
		return nil, skipped, xerrors.Errorf("pubSectorToPriv skipped some sectors")
	}

	proof, faulty, err := ffi.GenerateWindowPoSt(minerID, privsectors, randomness)

	var faultyIDs []abi.SectorID
	for _, f := range faulty {
		faultyIDs = append(faultyIDs, abi.SectorID{
			Miner:  minerID,
			Number: f,
		})
	}

	return proof, faultyIDs, err
}

func (sb *Sealer)AggregateWindowPoStProofs(aggregateInfo spproof.AggregateWindowPostInfos, proofs []spproof.PoStProof) ([]byte, error) {
	if len(proofs) != len(aggregateInfo.SectorCount) {
		return nil, xerrors.Errorf("the lenth of windowPoStProofs and sectorCount is not match")
	}

	sectorCount := aggregateInfo.SectorCount[0]
	for _, count := range(aggregateInfo.SectorCount) {
		if sectorCount != count {
			return nil, xerrors.Errorf("Window PoSt challenge count must be equal")
		}
	}

	for i, random := range(aggregateInfo.Randomnesses) {
		aggregateInfo.Randomnesses[i][31] = random[31] & 0x3f
	}
	return ffi.AggregateWindowPoStProofs(aggregateInfo, proofs)
}

func (sb *Sealer) pubSectorToPriv(ctx context.Context, mid abi.ActorID, sectorInfo []spproof.SectorInfo, faults []abi.SectorNumber, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error)) (ffi.SortedPrivateSectorInfo, []abi.SectorID, func(), error) {
	fmap := map[abi.SectorNumber]struct{}{}
	for _, fault := range faults {
		fmap[fault] = struct{}{}
	}

	var doneFuncs []func()
	done := func() {
		for _, df := range doneFuncs {
			df()
		}
	}

	var skipped []abi.SectorID
	var out []ffi.PrivateSectorInfo
	for _, s := range sectorInfo {
		if _, faulty := fmap[s.SectorNumber]; faulty {
			continue
		}

		sid := storage.SectorRef{
			ID:        abi.SectorID{Miner: mid, Number: s.SectorNumber},
			ProofType: s.SealProof,
		}

		paths, d, err := sb.sectors.AcquireSector(ctx, sid, storiface.FTCache|storiface.FTSealed, 0, storiface.PathStorage)
		if err != nil {
			log.Warnw("failed to acquire sector, skipping", "sector", sid.ID, "error", err)
			skipped = append(skipped, sid.ID)
			continue
		}
		doneFuncs = append(doneFuncs, d)

		postProofType, err := rpt(s.SealProof)
		if err != nil {
			done()
			return ffi.SortedPrivateSectorInfo{}, nil, nil, xerrors.Errorf("acquiring registered PoSt proof from sector info %+v: %w", s, err)
		}

		out = append(out, ffi.PrivateSectorInfo{
			CacheDirPath:     paths.Cache,
			PoStProofType:    postProofType,
			SealedSectorPath: paths.Sealed,
			SectorInfo:       s,
		})
	}

	return ffi.NewSortedPrivateSectorInfo(out...), skipped, done, nil
}

type Verifier struct {}

var ProofVerifier = Verifier{}

var _ SectorVerifier = Verifier{}

func (Verifier) VerifySeal(info spproof.SealVerifyInfo) (bool, error) {
	return ffi.VerifySeal(info)
}

func (Verifier) VerifyAggregateSeals(aggregate spproof.AggregateSealVerifyProofAndInfos) (bool, error) {
	return ffi.VerifyAggregateSeals(aggregate)
}

func (Verifier) VerifyWindowPoSt(info spproof.WindowPoStVerifyInfo) (bool, error) {
	info.Randomness[31] &= 0x3f

	return ffi.VerifyWindowPoSt(info)
}

func (Verifier)VerifyAggregateWindowPostProofs(aggregateInfo spproof.AggregateWindowPostInfos, sealedSectors [][]spproof.SectorInfo) (bool, error) {
	var sectorInfos []spproof.SectorInfo
	sectorCount := make([]uint, len(sealedSectors))
	for i, sectors := range(sealedSectors) {
		sectorCount[i] = uint(len(sectors))
		sectorInfos = append(sectorInfos, sectors...)
		// for _, sector := range(sectors) {
		// 	sectorInfos = append(sectorInfos, sector)
		// }
	}
	aggregateInfo.ChallengedSectors = sectorInfos
	aggregateInfo.SectorCount = sectorCount

	for i, random := range(aggregateInfo.Randomnesses) {
		aggregateInfo.Randomnesses[i][31] = random[31] & 0x3f
	}

	return ffi.VerifyAggregateWindowPostProofs(aggregateInfo)
}

func DefaultAggregationType() abi.RegisteredAggregationProof {
	return abi.RegisteredAggregationProof_SnarkPackV1;
}