// Package tiger implements the Tiger hash algorithm
// https://github.com/cxmcc/tiger
package tiger

import "hash"

// The size of a Tiger hash value in bytes
const Size = 24

// The blocksize of Tiger hash function in bytes
const BlockSize = 64

const (
	chunk = 64
	initA = 0x0123456789abcdef
	initB = 0xfedcba9876543210
	initC = 0xf096a5b4c3b2e187
)

type digest struct {
	a      uint64
	b      uint64
	c      uint64
	x      [chunk]byte
	nx     int
	length uint64
	ver    int
}

func (d *digest) Reset() {
	d.a = initA
	d.b = initB
	d.c = initC
	d.nx = 0
	d.length = 0
}

// New returns a new hash.Hash computing the Tiger hash value
func New() hash.Hash {
	d := new(digest)
	d.Reset()
	d.ver = 1
	return d
}

// New returns a new hash.Hash computing the Tiger2 hash value
func New2() hash.Hash {
	d := new(digest)
	d.Reset()
	d.ver = 2
	return d
}

func (d *digest) BlockSize() int {
	return BlockSize
}

func (d *digest) Size() int {
	return Size
}

func (d *digest) Write(p []byte) (length int, err error) {
	length = len(p)
	d.length += uint64(length)
	if d.nx > 0 {
		n := len(p)
		if n > chunk-d.nx {
			n = chunk - d.nx
		}
		copy(d.x[d.nx:d.nx+n], p[:n])
		d.nx += n
		if d.nx == chunk {
			d.compress(d.x[:chunk])
			d.nx = 0
		}
		p = p[n:]
	}
	for len(p) >= chunk {
		d.compress(p[:chunk])
		p = p[chunk:]
	}
	if len(p) > 0 {
		d.nx = copy(d.x[:], p)
	}
	return
}

func (d digest) Sum(in []byte) []byte {
	length := d.length
	var tmp [64]byte
	if d.ver == 1 {
		tmp[0] = 0x01
	} else {
		tmp[0] = 0x80
	}

	size := length & 0x3f
	if size < 56 {
		d.Write(tmp[:56-size])
	} else {
		d.Write(tmp[:64+56-size])
	}

	length <<= 3
	for i := uint(0); i < 8; i++ {
		tmp[i] = byte(length >> (8 * i))
	}
	d.Write(tmp[:8])

	for i := uint(0); i < 8; i++ {
		tmp[i] = byte(d.a >> (8 * i))
		tmp[i+8] = byte(d.b >> (8 * i))
		tmp[i+16] = byte(d.c >> (8 * i))
	}

	return append(in, tmp[:24]...)
}
