// test-vector-manual -- KEKS test structure with manually encoded fields
// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"math/big"
	"net"
	"time"

	"github.com/google/uuid"
	"go.cypherpunks.su/tai64n/v4"

	"go.cypherpunks.su/keks"
)

func mustHexDec(s string) []byte {
	b, err := hex.DecodeString(s)
	if err != nil {
		panic(err)
	}
	return b
}

var Size int64

func mustEncode(n int64, err error) {
	Size += n
	if err != nil {
		panic(err)
	}
}

func main() {
	var buf bytes.Buffer
	mustEncode(keks.MagicEncode(&buf, keks.Magic("test-vector")))
	{
		mustEncode(keks.ByteEncode(&buf, byte(keks.AtomMap)))
		{
			mustEncode(keks.StrEncode(&buf, "ip"))
			ip := net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348")
			h := keks.Hexlet(ip[:])
			mustEncode(keks.HexletEncode(&buf, &h))
		}
		{
			mustEncode(keks.StrEncode(&buf, "nil"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomNIL)))
		}
		{
			mustEncode(keks.StrEncode(&buf, "str"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomMap)))
			{
				mustEncode(keks.StrEncode(&buf, "bin"))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
				{
					mustEncode(keks.BinEncode(&buf, []byte("")))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'0'}, 60)))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'1'}, 61)))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'2'}, 255)))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'A'}, 61+255)))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'B'}, 62+255)))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'3'}, 1024)))
					mustEncode(keks.BinEncode(&buf, bytes.Repeat([]byte{'4'}, 63+255+65535+1)))
				}
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
				{
					mustEncode(keks.StrEncode(&buf, "utf8"))
					mustEncode(keks.StrEncode(&buf, "привет мир"))
				}
			}
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}
		{
			mustEncode(keks.StrEncode(&buf, "blob"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
			mustEncode(keks.BlobEncode(&buf, 12, bytes.NewReader([]byte{'5'})))
			mustEncode(keks.BlobEncode(&buf, 12, bytes.NewReader(bytes.Repeat([]byte{'6'}, 12))))
			mustEncode(keks.BlobEncode(&buf, 12, bytes.NewReader(bytes.Repeat([]byte{'7'}, 13))))
			mustEncode(keks.BlobEncode(&buf, 5, bytes.NewReader([]byte("1234567890-"))))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}
		{
			mustEncode(keks.StrEncode(&buf, "bool"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
			mustEncode(keks.BoolEncode(&buf, true))
			mustEncode(keks.BoolEncode(&buf, false))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}
		{
			mustEncode(keks.StrEncode(&buf, "ints"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomMap)))
			{
				mustEncode(keks.StrEncode(&buf, "neg"))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
				mustEncode(keks.IntEncode(&buf, -1))
				mustEncode(keks.IntEncode(&buf, -2))
				mustEncode(keks.IntEncode(&buf, -32))
				mustEncode(keks.IntEncode(&buf, -33))
				mustEncode(keks.IntEncode(&buf, -123))
				mustEncode(keks.IntEncode(&buf, -1234))
				mustEncode(keks.IntEncode(&buf, -12345678))

				b := new(big.Int).SetBytes(mustHexDec("0100000000000000000000"))
				b = b.Neg(b)
				mustEncode(keks.BigIntEncode(&buf, b))
				b.SetBytes(mustHexDec("0100000000000000000000000000000001"))
				b = b.Neg(b)
				mustEncode(keks.BigIntEncode(&buf, b))
				b.SetBytes(mustHexDec("e5a461280341856d4ad908a69ea5f3ccc10c7882142bb7d801cc380f26b6b4d69632024ee521f8cfafb443d49a2a3d0cc73bb4757e882f5396ed302b418210d0d49d71be86ca699cf5ee3bd6d57ed658e69316229644ba650c92d7f0d4db29c3ad1dfa9979166f4c6e79561a58f8e2c63d08df4e2246ed1f64d2d613a19d8c9a6870e6188e2f3ad40c038fda30452f8ddfcd212a6a974bc25ec6a0564c66a7d28750ff9db458b74441e49ee5e82dbf4974d645678e0ad031f97aaba855451eef17a89b42821e530816dd5793a83b7a82e8ede81e7f3395691f761784f8bc627961cd40845ee908a40b9d1f01927b38eb1a7d4efd60db0944f7ec1b832b7e6eb1833f9a351576ad5de571fae8865da7514f06b0fbf38c1f2a8538f5d38b4e18001ccbb9ddcb488530f6086d14744d8b5672166e48e9ef93772575db66b6f257c6ffad6e2c291510c5ed02e1a8b24b44ec1e2a91686238e8defd18c01998634a5076a6b7f85fc81a1d61a15b2c528dfa082ce3e3e2ca649ac04817ec5c123e0b761ab103f780c014f021bbeb7ea3b86e0ca1c833e38ef5c897a6d7e1f4a2398c490b3d65e2f45c7fae402d1df1698b6fddb185481664871c2664bfd1686b2b3372783f1856f6247a3f8437a2818f68b7c4ea13a5f57b73c72870b684045f15"))
				b = b.Neg(b)
				mustEncode(keks.BigIntEncode(&buf, b))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
			}
			{
				mustEncode(keks.StrEncode(&buf, "pos"))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
				mustEncode(keks.UIntEncode(&buf, 0))
				mustEncode(keks.UIntEncode(&buf, 1))
				mustEncode(keks.UIntEncode(&buf, 31))
				mustEncode(keks.UIntEncode(&buf, 32))
				mustEncode(keks.UIntEncode(&buf, 123))
				mustEncode(keks.UIntEncode(&buf, 1234))
				mustEncode(keks.UIntEncode(&buf, 12345678))
				b := new(big.Int).SetBytes(mustHexDec("0100000000000000000000"))
				mustEncode(keks.BigIntEncode(&buf, b))
				b.SetBytes(mustHexDec("0100000000000000000000000000000000"))
				mustEncode(keks.BigIntEncode(&buf, b))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
			}
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}
		{
			u := uuid.MustParse("0e875e3f-d385-49eb-87b4-be42d641c367")
			mustEncode(keks.StrEncode(&buf, "uuid"))
			h := keks.Hexlet(u[:])
			mustEncode(keks.HexletEncode(&buf, &h))
		}
		{
			mustEncode(keks.StrEncode(&buf, "dates"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
			{
				var tai tai64n.TAI64
				t := time.Unix(1234567890, 0)
				t = tai64n.Leapsecs.Add(t)
				tai.FromTime(t)
				mustEncode(keks.TAI64Encode(&buf, &tai))
			}
			{
				var tai tai64n.TAI64N
				t := time.Unix(1234567890, 456*1000)
				t = tai64n.Leapsecs.Add(t)
				tai.FromTime(t)
				mustEncode(keks.TAI64NEncode(&buf, &tai))
			}
			{
				var tai tai64n.TAI64N
				t := time.Unix(1234567890, 456789)
				t = tai64n.Leapsecs.Add(t)
				tai.FromTime(t)
				mustEncode(keks.TAI64NEncode(&buf, &tai))
			}
			mustEncode(io.Copy(&buf, bytes.NewReader(append(
				[]byte{byte(keks.AtomTAI64NA)},
				[]byte("\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15")...,
			))))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}
		{
			mustEncode(keks.StrEncode(&buf, "floats"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{NaN: true}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{Pinf: true}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{Ninf: true}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{M: big.NewInt(0), E: big.NewInt(0)}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{M: big.NewInt(-181), E: big.NewInt(-2)}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{M: big.NewInt(5), E: big.NewInt(-5)}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{M: big.NewInt(-8687443681197687), E: big.NewInt(-46)}))
			mustEncode(keks.FloatEncode(&buf, &keks.Float{M: big.NewInt(0).SetBytes([]byte{0x27, 0xE4, 0x1B, 0x32, 0x46, 0xBE, 0xC9, 0xB1, 0x6E, 0x39, 0x81, 0x15}), E: big.NewInt(-12345)}))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}
		{
			mustEncode(keks.StrEncode(&buf, "empties"))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
			{
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomList)))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
			}
			{
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomMap)))
				mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
			}
			mustEncode(keks.BlobEncode(&buf, 123, bytes.NewReader([]byte{})))
			{
				h := keks.Hexlet(uuid.Nil[:])
				mustEncode(keks.HexletEncode(&buf, &h))
			}
			mustEncode(io.Copy(&buf, bytes.NewReader(append(
				[]byte{byte(keks.AtomTAI64)},
				[]byte("\x00\x00\x00\x00\x00\x00\x00\x00")...,
			))))
			mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
		}

		mustEncode(keks.ByteEncode(&buf, byte(keks.AtomEOC)))
	}
	fmt.Println(hex.EncodeToString(buf.Bytes()))
}
