package datastructures

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"time"
)

type SignerRequest struct {
	Version       uint8
	Action        Action
	System        uint8
	Root          uint8
	Configuration uint8
	Parameter1    uint8
	Parameter2    uint16
	Parameter3    uint8
	Content1      string
	Content2      string
	Content3      string
}

const protocolVersion = 1

func SignerRequestFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerRequest, error) {
	headerLength := Decode24BitLength(blockData[0:3])
	headerBytes := blockData[3 : 3+headerLength]

	contentBytes := blockData[3+headerLength:]
	content1Length := Decode24BitLength(contentBytes[0:3])
	content1 := string(contentBytes[3 : 3+content1Length])

	content2Offset := 3 + content1Length
	content2Length := Decode24BitLength(contentBytes[content2Offset : content2Offset+3])
	content2 := string(contentBytes[3+content2Offset : 3+content2Offset+content2Length])

	content3Offset := 3 + content2Offset + content2Length
	content3Length := Decode24BitLength(contentBytes[content3Offset : content3Offset+3])
	content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length])

	calculated := CalculateXorCheckSum([][]byte{lengthBytes, blockData})
	if checkSum != calculated {
		return nil, errors.New(fmt.Sprintf("invalid checksum expected 0x%x got 0x%x", calculated, checkSum))
	}
	return &SignerRequest{
		Version:       headerBytes[0],
		Action:        Action(headerBytes[1]),
		System:        headerBytes[2],
		Root:          headerBytes[3],
		Configuration: headerBytes[4],
		Parameter1:    headerBytes[5],
		Parameter2:    binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}),
		Parameter3:    headerBytes[8],
		Content1:      content1,
		Content2:      content2,
		Content3:      content3,
	}, nil
}

func (r SignerRequest) Serialize() []byte {
	parameter2Bytes := make([]byte, 2)
	binary.BigEndian.PutUint16(parameter2Bytes, r.Parameter2)
	headerBytes := bytes.Join([][]byte{
		{r.Version, byte(r.Action), r.System, r.Root, r.Configuration, r.Parameter1},
		parameter2Bytes, {r.Parameter3}}, []byte{})
	content1Bytes := []byte(r.Content1)
	content2Bytes := []byte(r.Content2)
	content3Bytes := []byte(r.Content3)
	blockBytes := bytes.Join([][]byte{
		encode24BitLength(headerBytes), headerBytes,
		encode24BitLength(content1Bytes), content1Bytes,
		encode24BitLength(content2Bytes), content2Bytes,
		encode24BitLength(content3Bytes), content3Bytes,
	}, []byte{})
	return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{})
}

func NewNulRequest() *SignerRequest {
	return &SignerRequest{
		Version:  protocolVersion,
		Action:   ActionNul,
		Content1: time.Now().UTC().Format("010203042006.05"),
	}
}