package tiger

import (
	"encoding/binary"
	"unsafe"
)

var littleEndian bool

func init() {
	x := uint32(0x04030201)
	y := [4]byte{0x1, 0x2, 0x3, 0x4}
	littleEndian = *(*[4]byte)(unsafe.Pointer(&x)) == y
}

func pass(a, b, c uint64, x []uint64, mul uint64) (uint64, uint64, uint64) {
	a, b, c = round(a, b, c, x[0], mul)
	b, c, a = round(b, c, a, x[1], mul)
	c, a, b = round(c, a, b, x[2], mul)
	a, b, c = round(a, b, c, x[3], mul)
	b, c, a = round(b, c, a, x[4], mul)
	c, a, b = round(c, a, b, x[5], mul)
	a, b, c = round(a, b, c, x[6], mul)
	b, c, a = round(b, c, a, x[7], mul)
	return a, b, c
}

func round(a, b, c, x, mul uint64) (uint64, uint64, uint64) {
	c ^= x
	a -= t1[c&0xff] ^ t2[(c>>16)&0xff] ^ t3[(c>>32)&0xff] ^ t4[(c>>48)&0xff]
	b += t4[(c>>8)&0xff] ^ t3[(c>>24)&0xff] ^ t2[(c>>40)&0xff] ^ t1[(c>>56)&0xff]
	b *= mul
	return a, b, c
}

func keySchedule(x []uint64) {
	x[0] -= x[7] ^ 0xa5a5a5a5a5a5a5a5
	x[1] ^= x[0]
	x[2] += x[1]
	x[3] -= x[2] ^ ((^x[1]) << 19)
	x[4] ^= x[3]
	x[5] += x[4]
	x[6] -= x[5] ^ ((^x[4]) >> 23)
	x[7] ^= x[6]
	x[0] += x[7]
	x[1] -= x[0] ^ ((^x[7]) << 19)
	x[2] ^= x[1]
	x[3] += x[2]
	x[4] -= x[3] ^ ((^x[2]) >> 23)
	x[5] ^= x[4]
	x[6] += x[5]
	x[7] -= x[6] ^ 0x0123456789abcdef
}

func (d *digest) compress(data []byte) {
	// save_abc
	a := d.a
	b := d.b
	c := d.c

	var x []uint64
	if littleEndian {
		x = []uint64{
			binary.LittleEndian.Uint64(data[0:8]),
			binary.LittleEndian.Uint64(data[8:16]),
			binary.LittleEndian.Uint64(data[16:24]),
			binary.LittleEndian.Uint64(data[24:32]),
			binary.LittleEndian.Uint64(data[32:40]),
			binary.LittleEndian.Uint64(data[40:48]),
			binary.LittleEndian.Uint64(data[48:56]),
			binary.LittleEndian.Uint64(data[56:64]),
		}
	} else {
		x = []uint64{
			binary.BigEndian.Uint64(data[0:8]),
			binary.BigEndian.Uint64(data[8:16]),
			binary.BigEndian.Uint64(data[16:24]),
			binary.BigEndian.Uint64(data[24:32]),
			binary.BigEndian.Uint64(data[32:40]),
			binary.BigEndian.Uint64(data[40:48]),
			binary.BigEndian.Uint64(data[48:56]),
			binary.BigEndian.Uint64(data[56:64]),
		}
	}

	d.a, d.b, d.c = pass(d.a, d.b, d.c, x, 5)
	keySchedule(x)
	d.c, d.a, d.b = pass(d.c, d.a, d.b, x, 7)
	keySchedule(x)
	d.b, d.c, d.a = pass(d.b, d.c, d.a, x, 9)

	// feedforward
	d.a ^= a
	d.b -= b
	d.c += c
}
