<template>
  <div id="devtools">
    <Test @valid-game="testValidGame"> </Test>
    <div class="head" v-show="iframes.length > 1">
      <div class="label">inspect target:</div>
      <CCSelect v-model:value="frameID" placeholder="please select ..." @change="onChangeFrame" :data="getFramesData()"> </CCSelect>
    </div>
    <div v-show="isShowDebug" class="find">
      <div v-if="false">
        <CCButton type="success" @click="onMemoryTest">内存测试</CCButton>
        <span>JS堆栈限制: {{ memory.performance.jsHeapSizeLimit }}</span>
        <span>JS堆栈大小: {{ memory.performance.totalJSHeapSize }}</span>
        <span>JS堆栈使用: {{ memory.performance.usedJSHeapSize }}</span>
      </div>
      <div class="left">
        <div class="tool-btn">
          <div style="padding-left: 5px; flex: 1; user-select: none">Node Tree</div>
          <CCButtonGroup :items="buttonGroup" :recover="true"></CCButtonGroup>
        </div>
        <CCInput placeholder="enter keywords to filter" :data="filterText">
          <slot>
            <i class="matchCase iconfont icon_font_size" @click.stop="onChangeCase" title="match case" :style="{ color: matchCase ? 'red' : '' }"></i>
          </slot>
        </CCInput>
        <div class="treeList">
          <CCTree :value="treeData" @node-click="handleNodeClick"></CCTree>
          <!-- <el-tree
            :data="treeData"
            ref="tree"
            style="display: inline-block"
            :props="defaultProps"
            :highlight-current="true"
            :default-expand-all="false"
            :default-expanded-keys="expandedKeys"
            :filter-node-method="filterNode"
            :expand-on-click-node="false"
            node-key="uuid"
            @node-expand="onNodeExpand"
            @node-collapse="onNodeCollapse"
            @node-click="handleNodeClick"
          >
            <span
              slot-scope="{ node, data }"
              class="leaf"
              :class="data.active ? 'leaf-show' : 'leaf-hide'"
            >
              <span>{{ node.label }}</span>
            </span>
          </el-tree> -->
        </div>
      </div>
      <CCDivider></CCDivider>
      <div class="right">
        <Properties v-if="treeItemData" :data="treeItemData"></Properties>
      </div>
    </div>
    <div v-show="!isShowDebug" class="no-find">
      <span>No games created by cocos creator found!</span>
      <CCButton type="success" @click="onBtnClickUpdatePage">
        <i class="iconfont icon_refresh"></i>
      </CCButton>
    </div>
    <CCDialog></CCDialog>
    <CCFootBar :version="version"></CCFootBar>
  </div>
</template>

<script lang="ts">
import ccui from "@xuyanfeng/cc-ui";
import { ButtonGroupItem } from "@xuyanfeng/cc-ui/types/cc-button-group/const";
import { Option } from "@xuyanfeng/cc-ui/types/cc-select/const";
import { ITreeData } from "@xuyanfeng/cc-ui/types/cc-tree/const";
import { _UnwrapAll, Store, storeToRefs } from "pinia";
import { defineComponent, nextTick, onMounted, PropType, reactive, Ref, ref, toRaw, watch } from "vue";
import PluginConfig from "../../../cc-plugin.config";
import { Msg, Page, PluginEvent } from "../../core/types";
import Bus, { BusMsg } from "./bus";
import { connectBackground } from "./connectBackground";
import { EngineData, FrameDetails, Info, NodeInfoData, ObjectData, ObjectItemRequestData, TreeData } from "./data";
import { appStore, RefreshType } from "./store";
import Test from "./test/test.vue";
import Properties from "./ui/propertys.vue";
import SettingsVue from "./ui/settings.vue";
ccui.components.CCAd;
const { CCTree, CCFootBar, CCDialog, CCInput, CCButton, CCInputNumber, CCSelect, CCButtonGroup, CCCheckBox, CCColor, CCDivider } = ccui.components;
interface FrameInfo {
  label: string;
  value: number;
}

export default defineComponent({
  components: { Test, CCFootBar, CCDialog, CCTree, CCDivider, CCButtonGroup, Properties, SettingsVue, CCInput, CCButton, CCInputNumber, CCSelect, CCCheckBox, CCColor },
  name: "devtools",
  props: {},
  setup(props, ctx) {
    appStore().init();
    const { config } = storeToRefs(appStore());
    const treeItemData = ref<NodeInfoData | null>({ uuid: "", group: [] });
    const isShowDebug = ref<boolean>(true);
    const frameID = ref<number>(0);
    const iframes = ref<Array<FrameInfo>>([]);
    const btnRefresh: ButtonGroupItem = reactive<ButtonGroupItem>({
      icon: "icon_refresh",
      click: () => {
        onBtnClickUpdateTree();
      },
      visible: true,
    });
    const buttonGroup = ref<ButtonGroupItem[]>([
      btnRefresh,
      {
        icon: "icon_settings",
        click: () => {
          ccui.dialog.showDialog({
            comp: SettingsVue,
            title: "Settings",
          });
        },
      },
    ]);
    function _checkSelectedUUID() {
      if (selectedUUID) {
        const b = _findUuidInTree(toRaw(treeData.value), selectedUUID);
        if (b) {
          return true;
        }
      }
      selectedUUID = null;
      treeItemData.value = null;
      return false;
    }

    function _findUuidInTree(data: TreeData[], targetUUID: string) {
      function circle(tree: TreeData[]) {
        for (let i = 0; i < tree.length; i++) {
          let item: TreeData = tree[i];
          if (item.id === targetUUID) {
            return true;
          }
          if (circle(item.children || [])) {
            return true;
          }
        }
        return false;
      }

      return circle(data);
    }

    /**
     * 请求属性的列表,如果一个属性请求失败,会阻断后续的相同请求,因为都已经失败了,就没必要再响应请求了
     */
    const requestList: Array<{ id: string; cb: Function }> = [];

    function _expand(uuid: string) {
      let expandKeys: Array<string> = [];

      function circle(array: any) {
        for (let i = 0; i < array.length; i++) {
          let item = array[i];
          expandKeys.push(item.uuid);
          if (item.uuid === uuid) {
            return true;
          } else {
            let find = circle(item.children);
            if (find) {
              return true;
            } else {
              expandKeys.pop();
            }
          }
        }
      }

      circle(treeData);

      expandKeys.forEach((key) => {
        if (!expandedKeys.value.find((el) => el === key)) {
          expandedKeys.value.push(key);
        }
      });
      // 高亮uuid
    }

    // 问题:没有上下文的权限,只能操作DOM
    function _executeScript(para: Object) {
      // chrome.tabs.executeScript()//v2版本使用的函数
      const tabID = chrome.devtools.inspectedWindow.tabId;
      chrome.scripting.executeScript({ files: ["js/execute.js"], target: { tabId: tabID } }, (results: chrome.scripting.InjectionResult[]) => {});
    }

    function _inspectedCode() {
      let injectCode = "";
      chrome.devtools.inspectedWindow.eval(injectCode, (result, isException) => {
        if (isException) {
          console.error(isException);
        } else {
          console.log(`执行结果:${result}`);
        }
      });
    }
    const elTree = ref<HTMLElement>();

    function _initChromeRuntimeConnect() {
      const msgFunctionMap: Record<string, Function> = {};
      msgFunctionMap[Msg.TreeInfo] = (data: Array<TreeData>) => {
        isShowDebug.value = true;
        if (!Array.isArray(data)) {
          data = [data];
        }
        treeData.value = data;
        if (_checkSelectedUUID()) {
          updateNodeInfo();
          nextTick(() => {
            if (elTree.value) {
              //@ts-ignore
              elTree.value.setCurrentKey(selectedUUID);
            }
          });
        }
      };
      msgFunctionMap[Msg.Support] = (isCocosGame: boolean) => {
        isShowDebug.value = isCocosGame;
        if (isCocosGame) {
          syncSettings();
          onBtnClickUpdateTree();
        } else {
          _clearTimer();
          treeData.value.length = 0;
          treeItemData.value = null;
          selectedUUID = null;
        }
      };
      msgFunctionMap[Msg.NodeInfo] = (eventData: NodeInfoData) => {
        isShowDebug.value = true;
        treeItemData.value = eventData;
      };
      msgFunctionMap[Msg.MemoryInfo] = (eventData: any) => {
        memory.value = eventData;
      };
      msgFunctionMap[Msg.UpdateProperty] = (data: Info) => {
        const uuid = data.path[0];
        const key = data.path[1];
        const value = data.data;
        let treeArray: Array<TreeData> = [];

        function circle(array: Array<TreeData>) {
          array.forEach((item) => {
            treeArray.push(item);
            circle(item.children);
          });
        }

        // 更新指定uuid节点的tree的name
        circle(treeData.value);
        let ret = treeArray.find((el) => el.id === uuid);
        if (ret) {
          if (key === "name") {
            ret.text = value;
          }
          if (key === "active") {
            ret.active = !!value;
          }
        }
      };
      msgFunctionMap[Msg.UpdateFrames] = (details: FrameDetails[]) => {
        // 先把iframes里面无效的清空了
        iframes.value = iframes.value.filter((item) => {
          details.find((el) => el.frameID === item.value);
        });

        // 同步配置
        details.forEach((item) => {
          let findItem = iframes.value.find((el) => el.value === item.frameID);
          if (findItem) {
            findItem.label = item.url;
          } else {
            iframes.value.push({
              label: item.url,
              value: item.frameID,
            });
          }
        });
        // 第一次获取到frame配置后,自动获取frame数据
        if (frameID === null && iframes.value.length > 0 && !iframes.value.find((el) => el.value === frameID.value)) {
          frameID.value = iframes[0].value;
          onChangeFrame();
        }
      };
      msgFunctionMap[Msg.GetObjectItemData] = (requestData: ObjectItemRequestData) => {
        if (requestData.id !== null) {
          let findIndex = requestList.findIndex((el) => el.id === requestData.id);
          if (findIndex > -1) {
            let del = requestList.splice(findIndex, 1)[0];
            del.cb(requestData.data);
          }
        }
      };
      // 接收来自background.js的消息数据
      connectBackground.onBackgroundMessage((data: PluginEvent, sender: any) => {
        if (!data) {
          return;
        }
        if (data.target === Page.Devtools) {
          console.log("[Devtools]", data);
          PluginEvent.finish(data);
          const { msg } = data;
          if (msg) {
            const func = msgFunctionMap[msg];
            if (func) {
              func(data.data);
            } else {
              console.warn(`没有${msg}消息的函数`);
            }
          }
        }
      });
    }
    _initChromeRuntimeConnect();
    window.addEventListener(
      "message",
      (event) => {
        console.log("on vue:" + JSON.stringify(event));
      },
      false
    );
    Bus.on(BusMsg.ShowPlace, (data: EngineData) => {
      console.log(data);
      _expand(data.engineUUID);
    });
    Bus.on(BusMsg.RequestObjectData, (data: ObjectData, cb: Function) => {
      if (!data.id || requestList.find((el) => el.id === data.id)) {
        return;
      }
      requestList.push({ id: data.id, cb });
      connectBackground.sendMsgToContentScript(Msg.GetObjectItemData, data);
    });
    Bus.on(BusMsg.UpdateSettings, () => {
      syncSettings();
    });
    Bus.on(BusMsg.LogData, (data: string[]) => {
      connectBackground.sendMsgToContentScript(Msg.LogData, data);
    });
    onMounted(() => {
      syncSettings();
    });
    const treeData = ref<TreeData[]>([]);
    const expandedKeys = ref<Array<string>>([]);
    const memory = ref<{
      performance: {
        jsHeapSizeLimit?: number;
        totalJSHeapSize?: number;
        usedJSHeapSize?: number;
      };
      console: Object;
    }>({
      performance: {},
      console: {},
    });
    // el-tree的渲染key
    const defaultProps = ref<{ children: string; label: string }>({
      children: "children",
      label: "name",
    });
    const filterText = ref<string>("");
    watch(filterText, (val) => {
      // TODO: 过滤树
      updateFilterText(val);
    });

    const matchCase = ref<boolean>(false);

    function onEnableTreeWatch(watch: boolean, time = 300) {
      if (watch) {
        _clearTimer();
        timerID = setInterval(() => {
          onBtnClickUpdateTree();
        }, time);
      } else {
        _clearTimer();
      }
    }
    let timerID: NodeJS.Timer | null = null;

    function _clearTimer() {
      if (timerID !== null) {
        clearInterval(timerID);
        timerID = null;
      }
    }
    function syncSettings() {
      const { refreshType, refreshTime } = config.value;
      switch (refreshType) {
        case RefreshType.Auto: {
          btnRefresh.visible = false;
          onEnableTreeWatch(true, refreshTime);
          break;
        }
        case RefreshType.Manual: {
          btnRefresh.visible = true;
          onEnableTreeWatch(false);
        }
      }
    }
    function updateNodeInfo() {
      if (selectedUUID) {
        connectBackground.sendMsgToContentScript(Msg.NodeInfo, selectedUUID);
      }
    }
    let selectedUUID: string | null = null;
    function updateFilterText(val: any) {
      (elTree.value as any)?.filter(val);
    }
    function onBtnClickUpdateTree() {
      connectBackground.sendMsgToContentScript(Msg.TreeInfo, frameID);
    }
    function onChangeFrame() {
      connectBackground.sendMsgToContentScript(Msg.UseFrame, frameID);
    }
    const elLeft = ref<HTMLDivElement>();
    const version = ref(PluginConfig.manifest.version);
    return {
      version,
      buttonGroup,
      elTree,
      memory,
      defaultProps,
      filterText,
      matchCase,
      iframes,
      isShowDebug,
      expandedKeys,
      treeData,
      treeItemData,
      frameID,
      testValidGame(b: boolean) {
        isShowDebug.value = !!b;
      },
      getFramesData(): Option[] {
        const frames: FrameInfo[] = toRaw(iframes.value);
        const options: Option[] = [];
        frames.forEach((frame) => {
          options.push({
            label: frame.label,
            value: frame.value,
          });
        });
        return options;
      },
      onChangeCase() {
        matchCase.value = !matchCase.value;
        updateFilterText(filterText);
      },
      handleNodeClick(data: TreeData) {
        selectedUUID = data.id;
        updateNodeInfo();
      },
      filterNode(value: any, data: any) {
        if (!value) {
          return true;
        } else {
          if (matchCase) {
            // 严格匹配大写
            return data?.name?.indexOf(value) !== -1;
          } else {
            return data?.name?.toLowerCase().indexOf(value.toLowerCase()) !== -1;
          }
        }
      },

      onBtnClickUpdatePage() {
        connectBackground.sendMsgToContentScript(Msg.Support);
      },
      onMemoryTest() {
        connectBackground.sendMsgToContentScript(Msg.MemoryInfo);
      },

      onChangeFrame,
      onNodeExpand(data: TreeData) {
        if (data.hasOwnProperty("uuid") && data.id) {
          expandedKeys.value.push(data.id);
        }
      },
      onNodeCollapse(data: TreeData) {
        if (data.hasOwnProperty("uuid")) {
          let index = expandedKeys.value.findIndex((el) => el === data.id);
          if (index !== -1) {
            expandedKeys.value.splice(index, 1);
          }
        }
      },
    };
  },
});
</script>

<style scoped lang="less">
#devtools {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: #5c5c5c;
  color: white;
  .head {
    display: flex;
    flex-direction: row;
    align-items: center;
    padding: 1px 0;
    border-bottom: solid 1px grey;

    .label {
      margin: 0 3px;
    }
  }

  .no-find {
    display: flex;
    flex: 1;
    flex-direction: row;
    align-items: center;
    justify-content: center;

    span {
      margin-right: 20px;
    }
  }

  .find {
    display: flex;
    flex: 1;
    flex-direction: row;
    overflow: auto;

    .left {
      display: flex;
      flex-direction: column;
      min-width: 200px;
      width: 300px;

      .tool-btn {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
      }

      .matchCase {
        width: 30px;
        height: 26px;
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
      }

      .treeList {
        margin-top: 3px;
        height: 100%;
        border-radius: 4px;
        min-height: 20px;
        overflow: auto;
        width: 100%;

        .leaf {
          width: 100%;
        }

        .leaf-show {
          color: black;
        }

        .leaf-hide {
          color: #c7bbbb;
          text-decoration: line-through;
        }

        &::-webkit-scrollbar {
          width: 6px;
          height: 6px;
          background: #999;
          border-radius: 2px;
        }

        &::-webkit-scrollbar-thumb {
          background-color: #333;
          border-radius: 2px;
        }
      }
    }

    .right {
      flex: 1;
      overflow-x: hidden;
      overflow-y: overlay;

      &::-webkit-scrollbar {
        width: 6px;
        background: #999;
        border-radius: 2px;
        height: 6px;
      }

      &::-webkit-scrollbar-thumb {
        background-color: #333;
        border-radius: 2px;
      }
    }
  }
}
</style>