// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package ldb

import (
	"bytes"
	"encoding/binary"
	"fmt"
	//"github.com/conformal/btcdb"
	"github.com/conformal/btcwire"
)

type txUpdateObj struct {
	txSha     *btcwire.ShaHash
	blkHeight int64
	txoff     int
	txlen     int
	spentData []byte
	delete    bool
}

// InsertTx inserts a tx hash and its associated data into the database.
func (db *LevelDb) InsertTx(txsha *btcwire.ShaHash, height int64, txoff int, txlen int, spentbuf []byte) (err error) {
	db.dbLock.Lock()
	defer db.dbLock.Unlock()

	return db.insertTx(txsha, height, txoff, txlen, spentbuf)
}

// insertTx inserts a tx hash and its associated data into the database.
// Must be called with db lock held.
func (db *LevelDb) insertTx(txSha *btcwire.ShaHash, height int64, txoff int, txlen int, spentbuf []byte) (err error) {
	var txU txUpdateObj

	txU.txSha = txSha
	txU.blkHeight = height
	txU.txoff = txoff
	txU.txlen = txlen
	txU.spentData = spentbuf

	db.txUpdateMap[*txSha] = &txU

	return nil
}

// formatTx generates the value buffer for the Tx db.
func (db *LevelDb) formatTx(txu *txUpdateObj) ([]byte, error) {

	blkHeight := txu.blkHeight
	txoff := txu.txoff
	txlen := txu.txlen
	spentbuf := txu.spentData

	txOff := int32(txoff)
	txLen := int32(txlen)

	var txW bytes.Buffer

	err := binary.Write(&txW, binary.LittleEndian, blkHeight)
	if err != nil {
		err = fmt.Errorf("Write fail")
		return nil, err
	}

	err = binary.Write(&txW, binary.LittleEndian, txOff)
	if err != nil {
		err = fmt.Errorf("Write fail")
		return nil, err
	}

	err = binary.Write(&txW, binary.LittleEndian, txLen)
	if err != nil {
		err = fmt.Errorf("Write fail")
		return nil, err
	}

	err = binary.Write(&txW, binary.LittleEndian, spentbuf)
	if err != nil {
		err = fmt.Errorf("Write fail")
		return nil, err
	}

	return txW.Bytes(), nil
}

func (db *LevelDb) getTxData(txsha *btcwire.ShaHash) (rblkHeight int64,
	rtxOff int, rtxLen int, rspentBuf []byte, err error) {
	var buf []byte

	key := shaTxToKey(txsha)
	buf, err = db.lDb.Get(key, db.ro)
	if err != nil {
		return
	}

	var blkHeight int64
	var txOff, txLen int32
	dr := bytes.NewBuffer(buf)
	err = binary.Read(dr, binary.LittleEndian, &blkHeight)
	if err != nil {
		err = fmt.Errorf("Db Corrupt 1")
		return
	}
	err = binary.Read(dr, binary.LittleEndian, &txOff)
	if err != nil {
		err = fmt.Errorf("Db Corrupt 2")
		return
	}
	err = binary.Read(dr, binary.LittleEndian, &txLen)
	if err != nil {
		err = fmt.Errorf("Db Corrupt 3")
		return
	}
	// remainder of buffer is spentbuf
	spentBuf := make([]byte, dr.Len())
	err = binary.Read(dr, binary.LittleEndian, spentBuf)
	if err != nil {
		err = fmt.Errorf("Db Corrupt 4")
		return
	}
	return blkHeight, int(txOff), int(txLen), spentBuf, nil
}

// ExistsTxSha returns if the given tx sha exists in the database
func (db *LevelDb) ExistsTxSha(txsha *btcwire.ShaHash) (exists bool) {
	db.dbLock.Lock()
	defer db.dbLock.Unlock()

	return db.existsTxSha(txsha)
}

// existsTxSha returns if the given tx sha exists in the database.o
// Must be called with the db lock held.
func (db *LevelDb) existsTxSha(txSha *btcwire.ShaHash) (exists bool) {
	_, _, _, _, err := db.getTxData(txSha)
	if err == nil {
		return true
	}

	// BUG(drahn) If there was an error beside non-existant deal with it.

	return false
}

// FetchLocationBySha looks up the Tx sha information by name.
func (db *LevelDb) FetchLocationBySha(txsha *btcwire.ShaHash) (blockidx int64, txoff int, txlen int, err error) {
	db.dbLock.Lock()
	defer db.dbLock.Unlock()
	err = fmt.Errorf("obsolete function")
	return
}

// FetchTxUsedBySha returns the used/spent buffer for a given transaction.
func (db *LevelDb) FetchTxUsedBySha(txSha *btcwire.ShaHash) (spentbuf []byte, err error) {
	db.dbLock.Lock()
	defer db.dbLock.Unlock()

	_, _, _, spentbuf, err = db.getTxData(txSha)
	if err != nil {
		return
	}
	return // spentbuf has the value already
}

func (db *LevelDb) fetchLocationUsedBySha(txsha *btcwire.ShaHash) error {
	// delete me
	return fmt.Errorf("Deleted function")
}