package mshal

import (
	"encoding/hex"
	"errors"
	"time"
)

func (h *HAL) patchExchangeReport(out [9]byte) ([9]byte, error) {
	var in [9]byte

	if h.config.LogFunc != nil {
		h.config.LogFunc(3, "PatchOut: %s", hex.EncodeToString(out[:]))
	}

	if _, err := h.dev.SendFeatureReport(out[:]); err != nil {
		return in, err
	}

	timeout := time.Now().Add(3 * time.Second)

	for time.Now().Before(timeout) {
		_, err := h.dev.GetFeatureReport(in[:])
		if err != nil {
			return in, err
		}

		if in[1]&0xFE == 0xFE {
			if h.config.LogFunc != nil {
				h.config.LogFunc(3, "PatchIn:  %s", hex.EncodeToString(in[:]))
			}

			return in, nil
		}
	}

	return in, ErrorTimeout
}

type PatchExecFuncResponse struct {
	A  byte
	R2 byte
	R3 byte
	R4 byte
	R5 byte
	R6 byte
	R7 byte
	C  bool
}

type PatchExecFuncRequest struct {
	DPTR uint16
	R3   byte
	R4   byte
	R5   byte
	R6   byte
	R7_A byte
}

func (h *HAL) PatchExecFunc(inIRQ bool, addr int, req PatchExecFuncRequest) (PatchExecFuncResponse, error) {
	var response PatchExecFuncResponse

	if !h.patchInstalled && !h.patchCanCall {
		return response, ErrorMissingFunction
	}

	if req.DPTR != 0 && (req.R4 != 0 || req.R3 != 0) {
		return response, errors.New("can't set both DPTR and R3/R4")
	}

	var out [9]byte
	out[1] = 0xef
	if inIRQ {
		out[1] = 0xee
	}
	out[2] = byte(addr >> 8)
	out[3] = byte(addr)
	if req.DPTR != 0 {
		out[4] = byte(req.DPTR >> 8)
		out[5] = byte(req.DPTR)
	} else {
		out[4] = req.R3
		out[5] = req.R4
	}
	out[6] = req.R5
	out[7] = req.R6
	out[8] = req.R7_A

	in, err := h.patchExchangeReport(out)
	if err != nil {
		return response, err
	}

	response.A = in[2]
	response.R2 = in[3]
	response.R3 = in[4]
	response.R4 = in[5]
	response.R5 = in[6]
	response.R6 = in[7]
	response.R7 = in[8]
	response.C = in[1]&1 > 0
	return response, nil
}

func (h *HAL) PatchCodeBlobGetAddress(index int) int {
	if index < 0 {
		return 0
	}

	index += h.patchCallAddrsExternalStart
	if index >= len(h.patchCallAddrs) {
		return 0
	}

	return h.patchCallAddrs[index]
}
