// Copyright © 2012 Dmitry Chestnykh, 2015 Drake Wilson.  All rights reserved.  Use of this source code is
// governed by a BSD-style license that can be found in the ../LICENSE.md file.

package skein_test

import (
	"bytes"
	"testing"

	"github.com/blanu/Dust/go/skein"
)

type initialVector struct {
	osize uint64
	chain [8]uint64
}

var initialVectors = []initialVector{
	{
		osize: 128 / 8,
		chain: [8]uint64{
			0xA8BC7BF36FBF9F52, 0x1E9872CEBD1AF0AA, 0x309B1790B32190D3, 0xBCFBB8543F94805C,
			0x0DA61BCD6E31B11B, 0x1A18EBEAD46A32E3, 0xA2CC5B18CE84AA82, 0x6982AB289D46982D,
		},
	},
	{
		osize: 160 / 8,
		chain: [8]uint64{
			0x28B81A2AE013BD91, 0xC2F11668B5BDF78F, 0x1760D8F3F6A56F12, 0x4FB747588239904F,
			0x21EDE07F7EAF5056, 0xD908922E63ED70B8, 0xB8EC76FFECCB52FA, 0x01A47BB8A3F27A6E,
		},
	},
	{
		osize: 224 / 8,
		chain: [8]uint64{
			0xCCD0616248677224, 0xCBA65CF3A92339EF, 0x8CCD69D652FF4B64, 0x398AED7B3AB890B4,
			0x0F59D1B1457D2BD0, 0x6776FE6575D4EB3D, 0x99FBC70E997413E9, 0x9E2CFCCFE1C41EF7,
		},
	},
	{
		osize: 256 / 8,
		chain: [8]uint64{
			0xCCD044A12FDB3E13, 0xE83590301A79A9EB, 0x55AEA0614F816E6F, 0x2A2767A4AE9B94DB,
			0xEC06025E74DD7683, 0xE7A436CDC4746251, 0xC36FBAF9393AD185, 0x3EEDBA1833EDFC13,
		},
	},
	{
		osize: 384 / 8,
		chain: [8]uint64{
			0xA3F6C6BF3A75EF5F, 0xB0FEF9CCFD84FAA4, 0x9D77DD663D770CFE, 0xD798CBF3B468FDDA,
			0x1BC4A6668A0E4465, 0x7ED7D434E5807407, 0x548FC1ACD4EC44D6, 0x266E17546AA18FF8,
		},
	},
	{
		osize: 512 / 8,
		chain: [8]uint64{
			0x4903ADFF749C51CE, 0x0D95DE399746DF03, 0x8FD1934127C79BCE, 0x9A255629FF352CB1,
			0x5DB62599DF6CA7B0, 0xEABE394CA9D5C3F4, 0x991112C71A75B523, 0xAE18A40B660FCC33,
		},
	},
}

func TestInitial(t *testing.T) {
	var ini skein.Initial
	for num, test := range initialVectors {
		ini.Init(test.osize, nil)
		chain := ini.Chain()

		eq := true
		for i := 0; i < 8; i++ {
			if chain[i] != test.chain[i] {
				eq = false
				break
			}
		}

		if !eq {
			t.Errorf("ini %d: expected %x, got %x", num, test.chain, chain)
			continue
		}
	}
}

type basicVector struct {
	in, out []byte
}

func linearRun(a, b byte) (run []byte) {
	switch {
	case a < b:
		run = make([]byte, 1 + int(b - a))
		for i, _ := range run {
			run[i] = a + byte(i)
		}

	case a > b:
		run = make([]byte, 1 + int(a - b))
		for i, _ := range run {
			run[i] = a - byte(i)
		}

	case a == b:
		run = make([]byte, 1)
		run[0] = a
	}

	return
}

var basicVectors = []basicVector{
	{
		in:  linearRun(0xff, 0xff),
		out: []byte{
			0x71, 0xB7, 0xBC, 0xE6, 0xFE, 0x64, 0x52, 0x22, 0x7B, 0x9C, 0xED, 0x60, 0x14, 0x24, 0x9E, 0x5B,
			0xF9, 0xA9, 0x75, 0x4C, 0x3A, 0xD6, 0x18, 0xCC, 0xC4, 0xE0, 0xAA, 0xE1, 0x6B, 0x31, 0x6C, 0xC8,
			0xCA, 0x69, 0x8D, 0x86, 0x43, 0x07, 0xED, 0x3E, 0x80, 0xB6, 0xEF, 0x15, 0x70, 0x81, 0x2A, 0xC5,
			0x27, 0x2D, 0xC4, 0x09, 0xB5, 0xA0, 0x12, 0xDF, 0x2A, 0x57, 0x91, 0x02, 0xF3, 0x40, 0x61, 0x7A,
		},
	},
	{
		in:  linearRun(0xff, 0xc0),
		out: []byte{
			0x45, 0x86, 0x3B, 0xA3, 0xBE, 0x0C, 0x4D, 0xFC, 0x27, 0xE7, 0x5D, 0x35, 0x84, 0x96, 0xF4, 0xAC,
			0x9A, 0x73, 0x6A, 0x50, 0x5D, 0x93, 0x13, 0xB4, 0x2B, 0x2F, 0x5E, 0xAD, 0xA7, 0x9F, 0xC1, 0x7F,
			0x63, 0x86, 0x1E, 0x94, 0x7A, 0xFB, 0x1D, 0x05, 0x6A, 0xA1, 0x99, 0x57, 0x5A, 0xD3, 0xF8, 0xC9,
			0xA3, 0xCC, 0x17, 0x80, 0xB5, 0xE5, 0xFA, 0x4C, 0xAE, 0x05, 0x0E, 0x98, 0x98, 0x76, 0x62, 0x5B,
		},
	},
	{
		in:  linearRun(0xff, 0x80),
		out: []byte{
			0x91, 0xCC, 0xA5, 0x10, 0xC2, 0x63, 0xC4, 0xDD, 0xD0, 0x10, 0x53, 0x0A, 0x33, 0x07, 0x33, 0x09,
			0x62, 0x86, 0x31, 0xF3, 0x08, 0x74, 0x7E, 0x1B, 0xCB, 0xAA, 0x90, 0xE4, 0x51, 0xCA, 0xB9, 0x2E,
			0x51, 0x88, 0x08, 0x7A, 0xF4, 0x18, 0x87, 0x73, 0xA3, 0x32, 0x30, 0x3E, 0x66, 0x67, 0xA7, 0xA2,
			0x10, 0x85, 0x6F, 0x74, 0x21, 0x39, 0x00, 0x00, 0x71, 0xF4, 0x8E, 0x8B, 0xA2, 0xA5, 0xAD, 0xB7,
		},
	},
}

func TestBasic(t *testing.T) {
	var h skein.Hash

	for num, test := range basicVectors {
		h.Init(uint64(len(test.out)), nil)

		wn, err := h.Write(test.in)
		if wn != len(test.in) || err != nil {
			t.Errorf("basic %d: couldn't write %d bytes (%d, %v)", num, len(test.in), wn, err)
			continue
		}

		out := make([]byte, len(test.out))
		rn, err := h.Read(out)
		if rn != len(out) || err != nil {
			t.Errorf("basic %d: couldn't read %d bytes (%d, %v)", num, len(out), rn, err)
			continue
		}

		if !bytes.Equal(out, test.out) {
			t.Errorf("basic %d: expected %x, got %x", num, test.out, out)
			continue
		}
	}
}

// TODO:
//   - test with various args
//   - test stream cipher XOR parts
//   - test different sequences of read/write lengths
