/*
File Name:  Blockchain User.go
Copyright:  2021 Peernet s.r.o.
Author:     Peter Kleissner
*/

package core

import (
	"os"

	"github.com/PeernetOfficial/core/blockchain"
	"github.com/PeernetOfficial/core/btcec"
	"github.com/google/uuid"
)

// initUserBlockchain initializes the users blockchain. It creates the blockchain file if it does not exist already.
// If it is corrupted, it will log the error and exit the process.
func (backend *Backend) initUserBlockchain() {
	var err error
	backend.UserBlockchain, err = blockchain.Init(backend.peerPrivateKey, backend.Config.BlockchainMain)

	if err != nil {
		backend.Filters.LogError("initUserBlockchain", "error: %s\n", err.Error())
		os.Exit(ExitBlockchainCorrupt)
	}
}

// Index the user's blockchain each time there is an update.
func (backend *Backend) userBlockchainUpdateSearchIndex() {
	backend.UserBlockchain.BlockchainUpdate = func(blockchainU *blockchain.Blockchain, oldHeight, oldVersion, newHeight, newVersion uint64) {

		if newVersion != oldVersion || newHeight < oldHeight {
			// invalidate search index data for the user's blockchain
			backend.SearchIndex.UnindexBlockchain(backend.peerPublicKey)

			// reindex everything
			for blockN := uint64(0); blockN < newHeight; blockN++ {
				raw, status, err := blockchainU.GetBlockRaw(blockN)
				if err != nil || status != blockchain.StatusOK {
					continue
				}

				backend.SearchIndex.IndexNewBlock(backend.peerPublicKey, newVersion, blockN, raw)
			}

			return
		}

		if newVersion == oldVersion && newHeight > oldHeight {
			// index the new blocks
			for blockN := oldHeight; blockN < newHeight; blockN++ {
				raw, status, err := blockchainU.GetBlockRaw(blockN)
				if err != nil || status != blockchain.StatusOK {
					continue
				}

				backend.SearchIndex.IndexNewBlock(backend.peerPublicKey, newVersion, blockN, raw)
			}
		}
	}
}

// ReadBlock reads a block and decodes the records. This may be a block of the user's blockchain, or any other that is cached in the global blockchain cache.
func (backend *Backend) ReadBlock(PublicKey *btcec.PublicKey, Version, BlockNumber uint64) (decoded *blockchain.BlockDecoded, raw []byte, found bool, err error) {
	// requesting a block from the user's blockchain?
	if PublicKey.IsEqual(backend.peerPublicKey) {
		_, _, version := backend.UserBlockchain.Header()
		if Version != version {
			return nil, nil, false, nil
		}

		var status int
		raw, status, err = backend.UserBlockchain.GetBlockRaw(BlockNumber)
		if err != nil || status != blockchain.StatusOK {
			return nil, raw, false, err
		}
	} else if backend.GlobalBlockchainCache != nil {
		// read from the cache
		if raw, found = backend.GlobalBlockchainCache.Store.ReadBlock(PublicKey, Version, BlockNumber); !found {
			return nil, nil, false, nil
		}
	} else {
		return nil, nil, false, nil
	}

	// decode the entire block
	blockDecoded, status, err := blockchain.DecodeBlockRaw(raw)
	if err != nil || status != blockchain.StatusOK {
		return nil, raw, false, err
	}

	return blockDecoded, raw, true, nil
}

// ReadFile decodes a file from the given blockchain (the user or any other cached). The block number must be provided.
func (backend *Backend) ReadFile(PublicKey *btcec.PublicKey, Version, BlockNumber uint64, FileID uuid.UUID) (file blockchain.BlockRecordFile, raw []byte, found bool, err error) {
	blockDecoded, raw, found, err := backend.ReadBlock(PublicKey, Version, BlockNumber)
	if !found {
		return file, raw, found, err
	}

	for _, decodedR := range blockDecoded.RecordsDecoded {
		if file, ok := decodedR.(blockchain.BlockRecordFile); ok && file.ID == FileID {
			return file, raw, true, nil
		}
	}

	return file, raw, false, nil
}
