import { FRAME_FLAG_2BYTE, FRAME_FLAG_4BYTE, FRAME_TAIL, FuncCode } from './constants';
import type { SetupConfig } from './types';

const HEADER_MAGIC = new Uint8Array([0x68, 0x68, 0xff, 0xff]);
const HEADER_LEN = 10;
const STANDARD_PAYLOAD_LEN = 54;
const STANDARD_TOTAL_LEN = HEADER_LEN + STANDARD_PAYLOAD_LEN; // 64

function buildHeader(func: number, payloadLen: number): Uint8Array {
  const buf = new Uint8Array(HEADER_LEN);
  buf.set(HEADER_MAGIC, 0);
  buf[5] = func;

  if (payloadLen <= 0xffff) {
    buf[4] = FRAME_FLAG_2BYTE;
    buf[6] = payloadLen & 0xff;
    buf[7] = (payloadLen >> 8) & 0xff;
    buf.set(FRAME_TAIL, 8);
  } else {
    buf[4] = FRAME_FLAG_4BYTE;
    buf[6] = payloadLen & 0xff;
    buf[7] = (payloadLen >> 8) & 0xff;
    buf[8] = (payloadLen >> 16) & 0xff;
    buf[9] = (payloadLen >> 24) & 0xff;
  }
  return buf;
}

function buildSimpleCommand(func: number, freqFlag = 0): Uint8Array {
  const payload = new Uint8Array(STANDARD_PAYLOAD_LEN);
  payload[0] = 0x01;
  payload[1] = freqFlag & 0xff;

  const now = new Date();
  const dateVal =
    (now.getFullYear() % 100) * 10000 +
    (now.getMonth() + 1) * 100 +
    now.getDate();
  payload[2] = (dateVal >> 24) & 0xff;
  payload[3] = (dateVal >> 16) & 0xff;
  payload[4] = (dateVal >> 8) & 0xff;
  payload[5] = dateVal & 0xff;

  const header = buildHeader(func, STANDARD_PAYLOAD_LEN);
  const pkt = new Uint8Array(STANDARD_TOTAL_LEN);
  pkt.set(header, 0);
  pkt.set(payload, HEADER_LEN);
  return pkt;
}

function writeUint16LE(buf: Uint8Array, offset: number, value: number) {
  buf[offset] = value & 0xff;
  buf[offset + 1] = (value >> 8) & 0xff;
}

function writeString(buf: Uint8Array, offset: number, str: string, maxLen: number) {
  for (let i = 0; i < maxLen; i++) {
    buf[offset + i] = i < str.length ? str.charCodeAt(i) & 0xff : 0;
  }
}

export function buildSetupPacket(cfg: SetupConfig): Uint8Array {
  const payload = new Uint8Array(STANDARD_PAYLOAD_LEN);

  payload[0] = cfg.channelNum & 0xff;
  payload[1] = cfg.sendFreq & 0xff;
  payload[2] = cfg.sampleFreq & 0xff;
  writeUint16LE(payload, 3, cfg.sampleDepth);

  let negAccNum = cfg.accNum & 0x3fff;
  if (cfg.negAcc123) negAccNum |= 0x8000;
  if (cfg.negAcc456) negAccNum |= 0x4000;
  writeUint16LE(payload, 5, negAccNum);

  payload[7] = cfg.ampRatio & 0xff;
  payload[8] = cfg.dataChannel & 0xff;
  payload[9] = cfg.compRes & 0xff;
  payload[10] = cfg.sampleFreq & 0xff;
  writeUint16LE(payload, 11, cfg.compDisableDelay);
  payload[13] = cfg.sourceMode & 0xff;
  writeUint16LE(payload, 14, cfg.batteryVoltageMin);
  writeString(payload, 38, cfg.filePrefix, 16);

  const header = buildHeader(FuncCode.SETUP_REQ, STANDARD_PAYLOAD_LEN);
  const pkt = new Uint8Array(STANDARD_TOTAL_LEN);
  pkt.set(header, 0);
  pkt.set(payload, HEADER_LEN);
  return pkt;
}

export const buildContinuousReq = (freqFlag = 0) =>
  buildSimpleCommand(FuncCode.CONTINUOUS_REQ, freqFlag);

export const buildSingleReq = (freqFlag = 0) =>
  buildSimpleCommand(FuncCode.SINGLE_REQ, freqFlag);

export function buildStopReq(freqFlag = 0): Uint8Array {
  const payload = new Uint8Array(STANDARD_PAYLOAD_LEN);
  payload[0] = 0x02;
  payload[1] = freqFlag & 0xff;

  const now = new Date();
  const dateVal =
    (now.getFullYear() % 100) * 10000 +
    (now.getMonth() + 1) * 100 +
    now.getDate();
  payload[2] = (dateVal >> 24) & 0xff;
  payload[3] = (dateVal >> 16) & 0xff;
  payload[4] = (dateVal >> 8) & 0xff;
  payload[5] = dateVal & 0xff;

  const header = buildHeader(FuncCode.STOP_REQ, STANDARD_PAYLOAD_LEN);
  const pkt = new Uint8Array(STANDARD_TOTAL_LEN);
  pkt.set(header, 0);
  pkt.set(payload, HEADER_LEN);
  return pkt;
}

export function buildSplitFrameReq(subCmd: number, frameNo: number): Uint8Array {
  const payload = new Uint8Array(STANDARD_PAYLOAD_LEN);
  payload[0] = subCmd & 0xff;
  writeUint16LE(payload, 1, frameNo);
  const header = buildHeader(FuncCode.SPLITFRAME_REQ, STANDARD_PAYLOAD_LEN);
  const pkt = new Uint8Array(STANDARD_TOTAL_LEN);
  pkt.set(header, 0);
  pkt.set(payload, HEADER_LEN);
  return pkt;
}
