<template>
    <div class="container">
      <Breadcrumb :items="[$t('menu.list'), $t('bl')]" />
      <a-card class="general-card" :loading="loading">
        <template #title>
          <div style="color: red; font-weight: bold;">⚠:实验性功能 使用可能会损坏手台</div>
            {{ $t('bl') }} {{ $t('global.onStart') }}
        </template>
        <a-row style="margin-bottom: 16px">
          <a-col :span="12">
            <a-space style="width: 130%">
              <!-- <a-button type="primary" @click="readConfig">
                <template #icon>
                  <icon-plus />
                </template>
                {{ $t('cps.onDeviceRead') }}(仅用于检视已用区块)
              </a-button> -->
              <a-button @click="writeConfig">
                <template #icon>
                  <icon-plus />
                </template>
                {{ $t('cps.onDeviceWrite') }}
              </a-button> (固件名称仅支持英文)
            </a-space>
          </a-col>
        </a-row>
        <div style="display: flex; justify-content: space-between; margin-left: 10px; margin-right: 10px; align-items: flex-end; margin-bottom: 3px;">
          <div>EEPROM:</div>
          <div>
            {{ state.showAdd }}
            <t-button size="small" variant="outline" @click="clearAll(256)">清空</t-button>
          </div>
        </div>
        <div style="width: 100%; overflow: scroll; user-select: none;">
          <div style="height: 328px; display: flex; flex-direction: column; margin: 0; padding: 0; flex-wrap: wrap">
            <div
              @click="clearEp2(index)"
              :ondragover="(event: any)=>{showAdd(index);event.preventDefault()}"
              :ondrop="()=>{targetOver(item, index)}"
              :title="
                item == -2 ? '引导程序占用区' : (item != -1 ? state.rom[item].binaryName : 
                (index * 64 + 0x40000).toString(16).toUpperCase() + ' - ' + (index * 64 + 0x40000 + 63).toString(16).toUpperCase()
              )"
              :style="item == -1 ? 'background-color: white; border: 1px solid #ddd; height: 10px;' : (
                item == -2 ? 'background-color: #373737; border: 1px solid #ddd; height: 10px;' : 'background-color: ' + state.rom[item].color + '; border: 1px solid #ddd; height: 10px;'
              )"
              :key="index" v-for="(item, index) in state.calendar">
              &nbsp;
            </div>
          </div>
        </div>
        <a-button style="margin-bottom: 10px;" @click="selectFile">{{ state.binaryFile ? state.binaryName : $t('tool.selectFirmware') }}</a-button>(选择固件后将固件卡片拖拽到上方 EEPROM)
        <br>
        <t-space break-line>
          <t-card draggable="true" :ondragstart="()=>{state.nowDrag = index}" v-for="(item, index) in state.rom" :title="item.binaryName" :bordered="true" hover-shadow :style="{ width: '400px' }">
            <template #actions>
              <div :style="'width: 10px; height: 10px; background-color: ' + item.color + ';'"></div>
            </template>
            <t-input v-model="item.binaryName" @change="changeName(index)" show-limit-number :maxlength="13" />
          </t-card>
        </t-space>
      </a-card>
        <div id="statusArea" style="height: 20em; background-color: azure; color: silver; overflow: auto; padding: 20px; margin-top: 10px;"
        v-html="state.status"></div>
    </div>
  </template>
  
  <script lang="ts" setup>
  import useLoading from '@/hooks/loading';
  import { useAppStore } from '@/store';
  import { eeprom_write, eeprom_reboot, eeprom_init, eeprom_read, uint8ArrayToString, stringToUint8Array, check_eeprom, hexReverseStringToUint8Array, unpack } from '@/utils/serial.js';
  import { onMounted, reactive, nextTick } from 'vue';

  const appStore = useAppStore();
  const { loading, setLoading } = useLoading(true);

  const state : any = reactive({
    calendar: [],
    rom: [],
    bl: undefined,
    blName: '',
    nowDrag: -1,
    showAdd: '',
    status: ''
  })

  const readRange = async (start: any, end: any) =>{
    await eeprom_init(appStore.connectPort);
    let rawEEPROM = new Uint8Array(end - start);
    for (let i = start; i < end; i += 0x40) {
      const data = await eeprom_read(appStore.connectPort, i, 0x40, appStore.configuration?.uart);
      rawEEPROM.set(data, i - start);
    }
    return rawEEPROM;
  }

  const writeRange = async (start: any = 0, uint8Array: any, remark: string = '') => {
    for (let i = start; i < uint8Array.length + start; i += 0xC0) {
      await eeprom_write(appStore.connectPort, i, uint8Array.slice(i - start, i - start + 0xC0), uint8Array.slice(i - start, i - start + 0xC0).length, appStore.configuration?.uart);
      state.status = state.status + remark +  "写入进度:" + (((i - start) / uint8Array.length) * 100).toFixed(1) + "%<br/>";
      nextTick(()=>{
        const textarea = document?.getElementById('statusArea');
        if(textarea)textarea.scrollTop = textarea?.scrollHeight;
      })
    }
  }

  const showAdd = async (index: any) => {
    state.showAdd = (index * 64 + 0x40000).toString(16).toUpperCase()
    setTimeout(() => {
      state.showAdd = ''
    }, 5000);
  }

  onMounted(()=>{
    loadBL()
    const calendar = []
    for(let i=0; i < 262144 / 64; i++){
      if(i < 0x44000 / 64 / 16 - 16){
        calendar.push(-2);
      }else{
        calendar.push(-1);
      }
    }
    state.calendar = calendar
  })

  const loadBL = async() => {
    const latestVersion = JSON.parse(await (await fetch('https://k5.vicicode.cn/diyapi/bl.json')).text())!.latest;
    state.blName = latestVersion;
    const fontPacket = await fetch('https://k5.vicicode.cn/diyapi/' + latestVersion);
    if(fontPacket.body){
      const reader = fontPacket.body.getReader();
      const chunks = [];
      while(true) {
        const {done, value} = await reader.read();
        if (done) {
          break;
        }
        chunks.push(...value)
      }
      let bl = new Uint8Array(0x3000);
      bl.set(chunks, 0);
      state.bl = bl;
      setLoading(false);
    }
  }

  const readConfig = () =>{

  }

  const writeConfig = async () => {
    if(appStore.connectState != true){alert(sessionStorage.getItem('noticeConnectK5')); return;};
    const eepromSize = await check_eeprom(appStore.connectPort, appStore.configuration?.uart);
    setLoading(true);
    if(eepromSize < 0x80000){
      alert("只支持 4Mbit 以上 EEPROM 写入");
      setLoading(false);
      return
    }
    await eeprom_init(appStore.connectPort);
    await writeRange(0x41000, state.bl, '引导程序');

    const firmware = [];
    for(let i = 256; i < 4096; i++){
      if(state.calendar[i] >= 0){
        console.log(i);
        firmware.push({
          ...state.rom[state.calendar[i]],
          start: 0x40000 + i * 0x40,
          end: 0x40000 + (i + Math.ceil(state.rom[state.calendar[i]].binaryFile.length / 0x40)) * 0x40 - 1,
        })
        i += Math.ceil(state.rom[state.calendar[i]].binaryFile.length / 0x40) - 1;
      }
    }
    await writeRange(0x40000, new Uint8Array([firmware.length]), '固件数量');

    const _name_bl_array = new Uint8Array(8);
    _name_bl_array.set(stringToUint8Array(state.blName.split('.')[0]))
    await writeRange(0X40008, _name_bl_array, '引导程序版本')

    const writeMeta: any = [];
    firmware.map((item: any)=>{
      const _meta_name = new Uint8Array(16)
      const _meta_address_start = new Uint8Array(4)
      const _meta_address_end = new Uint8Array(4)
      _meta_name.set(stringToUint8Array(item.binaryName.replace(/[^\x00-\xff]/g, '')))
      _meta_address_start.set(hexReverseStringToUint8Array((item.start).toString(16)));
      _meta_address_end.set(hexReverseStringToUint8Array((item.end).toString(16)));
      writeMeta.push(..._meta_name, ..._meta_address_start, ..._meta_address_end, ...new Uint8Array(8))
    })
    await writeRange(0x40020, writeMeta, '固件元数据')

    for(let i = 0; i < firmware.length; i++){
      await writeRange(firmware[i].start, firmware[i].binaryFile, firmware[i].binaryName + ' 固件文件')
    }

    await eeprom_reboot(appStore.connectPort);
    state.status = state.status + "写入完成<br/>";
    setLoading(false);
  }

  const selectFile = () => {
    const input = document.createElement('input');
    input.type = 'file';
    input.multiple = true;
    input.onchange = async () => {
      if(input.files){
        for(let i = 0; i < input.files.length; i++){
          const blob = new Blob([input.files[i]], { type: 'application/octet-stream' });
          const rawEEPROM = new Uint8Array(await blob.arrayBuffer());
          const firmware = {
            binaryFile: unpack(rawEEPROM),
            binaryName: input.files[i].name.replace(/[^\x00-\xff]/g, ''),
            color: getColor()
          }
          state.rom.push(firmware)
        }
      }
    };
    input.click();
  }

  const targetOver = (can: number, slot: any) => {
    if(slot < 256){
      return
    }
    if(slot + Math.ceil(state.rom[state.nowDrag].binaryFile.length / 0x40) > 4096){
      return
    }
    if(can == 2){
      return
    }
    for(let i = slot; i < slot + Math.ceil(state.rom[state.nowDrag].binaryFile.length / 0x40); i += 1){
      clearEp(i);
    }
    for(let i = slot; i < slot + Math.ceil(state.rom[state.nowDrag].binaryFile.length / 0x40); i += 1){
      state.calendar[i] = state.nowDrag
    }
    console.log((slot * 64 + 0x40000).toString(16))
    console.log((Math.ceil(state.rom[state.nowDrag].binaryFile.length / 0x40) * 0x40 + (slot * 64 + 0x40000) - 1).toString(16))
  }

  const getColor = () => {
    var letters = '0123456789ABCDEF';
    var color = '#';
    for (var i = 0; i < 6; i++) {
      color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
  }

  const clearEp = (i: number) => {
    if(i > 4095 || i < 256){
      return
    }
    let flag = 0
    if(state.calendar[i] != -1){
      flag = 1
      state.calendar[i] = -1
    }
    if(flag){
      if(state.calendar[i - 1] != -1){
        clearEp(i - 1)
      }
      if(state.calendar[i + 1] != -1){
        clearEp(i + 1)
      }
    }
  }

  const clearEp2 = (i: number) => {
    if(i > 4095 || i < 256){
      return
    }
    let flag = -99
    if(state.calendar[i] != -1){
      flag = state.calendar[i]
      state.calendar[i] = -1
    }
    if(state.calendar[i - 1] === flag){
      clearEp2(i - 1)
    }
    if(state.calendar[i + 1] === flag){
      clearEp2(i + 1)
    }
  }

  const clearAll = (i: number) => {
    if(i > 4095 || i < 256){
      return
    }
    state.calendar[i] = -1
    clearAll(i + 1)
  }

  const changeName = (val: any) => {
    state.rom[val].binaryName = state.rom[val].binaryName.replace(/[^\x00-\xff]/g, '')
  };
  </script>
  
  <script lang="ts">
    export default {
      name: 'BL',
    };
  </script>
  
  <style scoped lang="less">
    :deep(::-webkit-scrollbar-thumb){
      border-radius: 0 !important;
    }
    :deep(.scrollbar::-webkit-scrollbar) {
      height: 10px;
    }
    :deep(.t-table__content::-webkit-scrollbar) {
      height: 15px;
    }
    .container {
      padding: 0 20px 20px 20px;
    }
    :deep(.arco-table-th) {
      &:last-child {
        .arco-table-th-item-title {
          margin-left: 16px;
        }
      }
    }
    .action-icon {
      margin-left: 12px;
      cursor: pointer;
    }
    .active {
      color: #0960bd;
      background-color: #e3f4fc;
    }
    .setting {
      display: flex;
      align-items: center;
      width: 200px;
      .title {
        margin-left: 12px;
        cursor: pointer;
      }
    }
    .ttable {
      :deep(.t-table__affixed-header-elm-wrap){
        height: 60px !important;
      }
      :deep(.t-table__content){
        scrollbar-width: auto !important;
      }
    }
  </style>