import { ref, watchEffect } from 'vue'
import { defineStore } from 'pinia'
import { isString, toNumber, isUndefined } from 'lodash';
import { Common } from '@/Common';
import z from "zod"
import { isNumber } from 'mathjs';
import { JtagClient } from "@/APIClient";
import { Mutex, withTimeout } from 'async-mutex';
import { useConstraintsStore } from "@/stores/constraints";
import { useDialogStore } from './dialog';

export const useEquipments = defineStore('equipments', () => {
  // Global Stores
  const constrainsts = useConstraintsStore();
  const dialog = useDialogStore();

  const boardAddr = ref("127.0.0.1");
  const boardPort = ref(1234);
  const jtagBitstream = ref<File>();
  const jtagBoundaryScanFreq = ref(100);
  const jtagClientMutex = withTimeout(new Mutex(), 1000, new Error("JtagClient Mutex Timeout!"))
  const jtagClient = new JtagClient();

  const enableJtagBoundaryScan = ref(false);

  function setAddr(address: string | undefined): boolean {
    if (isString(address) && z.string().ip("4").safeParse(address).success) {
      boardAddr.value = address;
      return true;
    }

    return false;
  }

  function setPort(port: string | number | undefined): boolean {
    if (isString(port) && port.length != 0) {
      const portNumber = toNumber(port);
      if (z.number().nonnegative().max(65535).safeParse(portNumber).success) {
        boardPort.value = portNumber;
        return true;
      }
    }
    else if (isNumber(port)) {
      if (z.number().nonnegative().max(65535).safeParse(port).success) {
        boardPort.value = port;
        return true;
      }
    }
    return false;
  }

  watchEffect(() => {
    if (enableJtagBoundaryScan.value) jtagBoundaryScan();
  });

  async function jtagBoundaryScan() {
    const release = await jtagClientMutex.acquire();
    try {
      const portStates = await jtagClient.boundaryScanLogicalPorts(
        boardAddr.value,
        boardPort.value,
      );

      constrainsts.batchSetConstraintStates(portStates);
    } catch (error) {
      dialog.error("边界扫描发生错误");
      console.error(error);
      enableJtagBoundaryScan.value = false;
    } finally {
      release();

      if (enableJtagBoundaryScan.value)
        setTimeout(jtagBoundaryScan, 1000 / jtagBoundaryScanFreq.value);
    }
  }

  async function jtagUploadBitstream(bitstream: File): Promise<boolean> {
    try {
      const resp = await jtagClient.uploadBitstream(
        boardAddr.value,
        Common.toFileParameterOrNull(bitstream),
      );
      return resp;
    } catch (e) {
      dialog.error("上传错误");
      console.error(e);
      return false;
    }
  }

  async function jtagDownloadBitstream(): Promise<boolean> {
    const release = await jtagClientMutex.acquire();
    try {
      const resp = await jtagClient.downloadBitstream(
        boardAddr.value,
        boardPort.value
      );
      return resp;
    } catch (e) {
      dialog.error("上传错误");
      console.error(e);
      return false;
    } finally {
      release();
    }
  }

  async function jtagGetIDCode(isQuiet: boolean = false): Promise<number> {
    const release = await jtagClientMutex.acquire();
    try {
      const resp = await jtagClient.getDeviceIDCode(
        boardAddr.value,
        boardPort.value
      );
      return resp;
    } catch (e) {
      if (!isQuiet) dialog.error("获取IDCode错误");
      return 0xffff_ffff;
    } finally {
      release();
    }
  }

  async function jtagSetSpeed(speed: number): Promise<boolean> {
    const release = await jtagClientMutex.acquire();
    try {
      const resp = await jtagClient.setSpeed(
        boardAddr.value,
        boardPort.value,
        speed
      );
      return resp;
    } catch (e) {
      dialog.error("设置Jtag速度失败");
      return false;
    } finally {
      release();
    }
  }

  return {
    boardAddr,
    boardPort,
    setAddr,
    setPort,
    jtagBitstream,
    jtagBoundaryScanFreq,
    jtagClientMutex,
    jtagClient,
    jtagUploadBitstream,
    jtagDownloadBitstream,
    jtagGetIDCode,
    jtagSetSpeed,
    enableJtagBoundaryScan,
  }
})

