<template>
  <div
    ref="wall"
    class="fit items-center justify-evenly wall"
    @dragenter="onDragEnter"
    @dragleave="onDragLeave"
    @dragover="onDragOver"
    @drop="onDrop"
    style="background-color: #bce0f0"
  >
    <div id="windows" style="position: absolute">
      <vue3-resize-drag
        :w="item.width * $refs.wall.clientWidth"
        :h="item.height * $refs.wall.clientHeight"
        :x="
          ($refs.wall.parentElement?.offsetLeft ?? 0) +
          $refs.wall.offsetLeft +
          item.x * $refs.wall.clientWidth
        "
        :y="
          ($refs.wall.parentElement?.offsetTop ?? 0) +
          $refs.wall.offsetTop +
          item.y * $refs.wall.clientHeight
        "
        :zIndex="
          $store.state.windows_sort.findIndex((element) => element == item.uuid)
        "
        :isActive="item.uuid == $store.state.selected_window"
        :resizeIconSize="14"
        :isGuide="true"
        v-for="(item, index) in windows"
        :key="index"
        @resizeEndHandler="resizeWindow(item.window_id, $event)"
        @moveEndHandler="moveWindow(item.window_id, $event)"
        style="position: fixed"
      >
        <window
          @close_this_window="closeWindow"
          @close_other_windows="closeOtherWindows"
          @close_all_windows="closeAllWindows"
          @top_window="topWindow"
          @lower_window="lowerWindow"
          @dblclick="(evt) => windowDBClick(item.window_id)"
          @edit_volume="edit_volume"
          @mute_unmute="mute_unmute"
          @start_polling="start_polling"
          @stop_polling="stop_polling"
          @polling_setting="polling_setting"
          :ref="'window_' + item.window_id"
          :id="'window_' + item.window_id"
          :uuid="item.uuid"
          :disable="plan_running"
          class="window fit"
          :mouse_area_flag="area_open_window_flag"
          :signal_source_table_uuid="item.signal_source_table_uuid"
          :window="item"
        >
        </window>
      </vue3-resize-drag>
    </div>
    <div ref="wall_grids" @click="onWallGridsClick">
      <div
        v-for="row in wall_rows"
        :key="row"
        class="row"
        :style="{
          height: item_height + 'px',
        }"
      >
        <div
          :ref="'item' + (row - 1) * wall_cols * col"
          v-for="col in wall_cols"
          :key="col"
          class="col wall_item wall_item_flag"
          :style="{
            width: item_witdh + 'px',
            height: item_height + 'px',
          }"
        >
          <q-popup-proxy context-menu>
            <q-popup-proxy context-menu />
            <q-list>
              <q-item
                :disable="plan_running"
                clickable
                v-close-popup
                @click="closeAllWindows"
              >
                <q-item-section avatar>
                  <q-icon name="close" color="red" />
                </q-item-section>
                <q-item-section> {{ $t("close all windwos") }} </q-item-section>
              </q-item>
            </q-list>
          </q-popup-proxy>
        </div>
      </div>
    </div>
  </div>
  <div
    class="mouse_area_mask"
    v-show="area_open_window_flag"
    style="position: fixed; pointer-events: none; z-index: 999"
    :style="{
      left:
        Math.min(area_open_window_rect.start_x, area_open_window_rect.end_x) +
        'px',
      top:
        Math.min(area_open_window_rect.start_y, area_open_window_rect.end_y) +
        'px',
      width:
        Math.abs(area_open_window_rect.end_x - area_open_window_rect.start_x) +
        'px',
      height:
        Math.abs(area_open_window_rect.end_y - area_open_window_rect.start_y) +
        'px',
    }"
  ></div>
  <edit-volume-dialog ref="edit_volume_dialog" />
  <polling-setting-dialog ref="polling_setting_dialog" />
</template>

<style scoped>
.wall {
  border: 1px solid black;
}

.wall_item {
  border: 1px solid gray;
}

.window {
  position: absolute;
}

.wall_item_flag {
}

.mouse_area_mask {
  background: #aacceec2;
  border: 1px solid #0069bee7;
}
</style>

<script lang="ts">
import GlobalData from "src/common/GlobalData";
import { defineComponent, ref, Ref, computed, onMounted } from "vue";
const elementResizeDetectorMaker = require("element-resize-detector");
import { Protocol } from "src/entities/WSProtocol";
import Window from "src/components/Window.vue";

import { useI18n } from "vue-i18n";
import { useStore } from "src/store";
import EventBus, { EventNamesDefine } from "src/common/EventBus";
import { WindowOpenNotifyEntity } from "src/entities/MultimediaWindowEntity";
import WindowOtherStateChangeNotifyEntity from "src/entities/WindowOtherStateChangeNotifyEntity";
import { useQuasar } from "quasar";
import { NotifyMessage } from "src/common/ClientConnection";

import EditVolumeDialog from "src/components/EditVolumeDialog.vue";
import PollingSettingDialog from "src/components/PollingSettingDialog.vue";

import vue3ResizeDrag from "../third_lib/vue3-resize-drag/components/vue3-resize-drag/index.vue";

class Rect {
  start_x = 0;
  start_y = 0;
  end_x = 0;
  end_y = 0;

  reset() {
    this.start_x = 0;
    this.start_y = 0;
    this.end_x = 0;
    this.end_y = 0;
  }
}

export default defineComponent({
  name: "PageWall",

  components: {
    vue3ResizeDrag,
    Window,
    EditVolumeDialog,
    PollingSettingDialog,
  },
  setup() {
    const $q = useQuasar();
    const $store = useStore();
    const $t = useI18n();

    const edit_volume_dialog: Ref<any> = ref(null);
    const polling_setting_dialog: Ref<any> = ref(null);

    const plan_running = ref(false);

    const windows = computed({
      get: () => $store.state.windows,
      set: (val) => $store.commit("setWindows", val),
    });

    const wall_rows = computed({
      get: () => $store.state.wall_row,
      set: (val) => $store.commit("setWallRow", val),
    });

    const wall_cols = computed({
      get: () => $store.state.wall_col,
      set: (val) => $store.commit("setWallCol", val),
    });

    const wall: Ref<HTMLElement | null> = ref(null);

    const area_open_window_flag = ref(false);
    const area_open_window_rect = ref(new Rect());

    let item_witdh = ref(0);
    const item_height = ref(0);

    const calcWallItemWH = () => {
      item_witdh.value =
        wall?.value?.parentElement?.offsetWidth ?? 0 / wall_cols.value;
      if (wall.value && wall.value.parentElement) {
        const wv_scaler =
          $store.state.device_screen_width / $store.state.device_screen_height;
        item_height.value =
          wall.value.parentElement.offsetWidth / wv_scaler / wall_rows.value;
      } else {
        item_height.value = 0;
      }

      item_witdh.value =
        Math.ceil(item_witdh.value + wall_cols.value) & ~wall_cols.value;
      item_height.value =
        Math.ceil(item_height.value + wall_rows.value) & ~wall_rows.value;
    };

    const wallMouseDown = (evt: MouseEvent) => {
      if (plan_running.value) {
        return;
      }
      if (evt.ctrlKey && wall.value) {
        let left =
          wall.value.offsetLeft + (wall.value.parentElement?.offsetLeft ?? 0);
        let top =
          wall.value.offsetTop + (wall.value.parentElement?.offsetTop ?? 0);

        if (
          left <= evt.clientX &&
          top <= evt.clientY &&
          left + wall.value.offsetWidth >= evt.clientX &&
          top + wall.value.offsetHeight >= evt.clientY
        ) {
          area_open_window_rect.value.start_x = evt.clientX;
          area_open_window_rect.value.start_y = evt.clientY;
          area_open_window_rect.value.end_x = evt.clientX;
          area_open_window_rect.value.end_y = evt.clientY;
          area_open_window_flag.value = true;
          evt.stopPropagation();
        }
      }
    };

    const wallMouseUp = (evt: MouseEvent) => {
      if (area_open_window_flag.value && wall.value) {
        evt.stopPropagation();
        // 大小为0 不开窗
        if (
          area_open_window_rect.value.start_x !=
            area_open_window_rect.value.end_x &&
          area_open_window_rect.value.start_y !=
            area_open_window_rect.value.end_y
        ) {
          // 开窗
          let left =
            wall.value.offsetLeft + (wall.value.parentElement?.offsetLeft ?? 0);
          let top =
            wall.value.offsetTop + (wall.value.parentElement?.offsetTop ?? 0);
          let start_x =
            Math.min(
              area_open_window_rect.value.start_x,
              area_open_window_rect.value.end_x
            ) - left;
          let start_y =
            Math.min(
              area_open_window_rect.value.start_y,
              area_open_window_rect.value.end_y
            ) - top;
          let end_x = Math.abs(
            area_open_window_rect.value.end_x -
              area_open_window_rect.value.start_x
          );
          let end_y = Math.abs(
            area_open_window_rect.value.end_y -
              area_open_window_rect.value.start_y
          );

          GlobalData.getInstance()
            .getCurrentClient()
            ?.openWindow(
              new Protocol.OpenWindowRequestEntity(
                $store.state.selected_signal_source,
                start_x / wall.value.offsetWidth,
                start_y / wall.value.offsetHeight,
                end_x / wall.value.offsetWidth,
                end_y / wall.value.offsetHeight
              )
            );
        }
      }
      area_open_window_flag.value = false;
      area_open_window_rect.value.reset();
    };

    const wallMouseMove = (evt: MouseEvent) => {
      if (area_open_window_flag.value) {
        evt.stopPropagation();
        if (wall.value) {
          let left =
            wall.value.offsetLeft + (wall.value.parentElement?.offsetLeft ?? 0);
          let top =
            wall.value.offsetTop + (wall.value.parentElement?.offsetTop ?? 0);
          let right = left + wall.value.offsetWidth;
          let bottom = top + wall.value.offsetHeight;
          if (evt.clientX < left) {
            area_open_window_rect.value.end_x = left;
          } else if (evt.clientX > right) {
            area_open_window_rect.value.end_x = right;
          } else {
            area_open_window_rect.value.end_x = evt.clientX;
          }

          if (evt.clientY < top) {
            area_open_window_rect.value.end_y = top;
          } else if (evt.clientY > bottom) {
            area_open_window_rect.value.end_y = bottom;
          } else {
            area_open_window_rect.value.end_y = evt.clientY;
          }
        }
      }
    };

    EventBus.getInstance().on(EventNamesDefine.WindowMouseDown, wallMouseDown);
    EventBus.getInstance().on(EventNamesDefine.WindowMouseMove, wallMouseMove);
    EventBus.getInstance().on(EventNamesDefine.WindowMouseUp, wallMouseUp);

    EventBus.getInstance().on(
      EventNamesDefine.DocumentBodyClick,
      (evt: PointerEvent) => {
        if (wall.value) {
          let flag = false;
          {
            let item: HTMLElement | null = evt.srcElement as HTMLElement;
            while (item) {
              if (item == wall.value) {
                flag = true;
                break;
              }
              item = item.parentElement;
            }
          }
          if (!flag) {
            $store.commit("setSelectedWindow", "");
          }
        }
      }
    );

    const __temp__size_a__ = 0.00000001;

    EventBus.getInstance().on(
      EventNamesDefine.NotifyMessage,
      (notify: NotifyMessage) => {
        try {
          switch (notify.packet.command) {
            case Protocol.Commands.kCloseWindow:
              {
                const temp = JSON.parse(notify.data);
                if (temp && temp.window_id) {
                  $store.commit("removeWindow", {
                    window_id: temp.window_id,
                  });
                }
              }
              break;
            case Protocol.Commands.kMoveWindow:
              {
                const temp = JSON.parse(notify.data);
                if (temp && temp.window_id) {
                  const window = $store.state.windows.find(
                    (item) => item.window_id == temp.window_id
                  );
                  if (window) {
                    // 先设置一个坐标让数据变动,然后再设置新数据，保证坐标正常刷新
                    $store.commit("setWindowPropertys", [
                      {
                        window,
                        property_name: "x",
                        value: (temp.x ?? 0) + __temp__size_a__,
                      },
                      {
                        window,
                        property_name: "y",
                        value: (temp.y ?? 0) + __temp__size_a__,
                      },
                    ]);

                    setTimeout(() => {
                      $store.commit("setWindowPropertys", [
                        {
                          window,
                          property_name: "x",
                          value: temp.x ?? 0,
                        },
                        {
                          window,
                          property_name: "y",
                          value: temp.y ?? 0,
                        },
                      ]);
                    }, 0);
                  }
                }
              }
              break;
            case Protocol.Commands.kResizeWindow:
              {
                const temp = JSON.parse(notify.data);
                if (temp && temp.window_id) {
                  const window = $store.state.windows.find(
                    (item) => item.window_id == temp.window_id
                  );
                  if (window) {
                    $store.commit("setWindowPropertys", [
                      {
                        window,
                        property_name: "width",
                        value: (temp.width ?? 0) + __temp__size_a__,
                      },
                      {
                        window,
                        property_name: "height",
                        value: (temp.width ?? 0) + __temp__size_a__,
                      },
                    ]);

                    setTimeout(() => {
                      $store.commit("setWindowPropertys", [
                        {
                          window,
                          property_name: "width",
                          value: temp.width ?? 0,
                        },
                        {
                          window,
                          property_name: "height",
                          value: temp.height ?? 0,
                        },
                      ]);
                    }, 0);
                  }
                }
              }
              break;
            case Protocol.Commands.kOpenWindow:
              {
                const temp = JSON.parse(notify.data) as WindowOpenNotifyEntity;
                if (temp) {
                  $store.commit("pushWindow", temp);
                }
              }
              break;
            case Protocol.Commands.kWindowOtherStateChanged:
              {
                const temp = JSON.parse(
                  notify.data
                ) as WindowOtherStateChangeNotifyEntity;
                if (temp && temp.window_id) {
                  const window = $store.state.windows.find(
                    (item) => item.window_id == temp.window_id
                  );
                  if (window) {
                    $store.commit("setWindowPropertys", [
                      {
                        window,
                        property_name: "playing",
                        value: temp.playing,
                      },
                      {
                        window,
                        property_name: "muted",
                        value: temp.muted,
                      },
                      {
                        window,
                        property_name: "volume",
                        value: temp.volume,
                      },
                      {
                        window,
                        property_name: "volume",
                        value: temp.volume,
                      },
                      {
                        window,
                        property_name: "polling",
                        value: temp.polling,
                      },
                      {
                        window,
                        property_name: "polling_title",
                        value: temp.title,
                      },
                      {
                        window,
                        property_name: "polling_window_type",
                        value: temp.window_type,
                      },
                    ]);
                  }
                }
              }
              break;
            case Protocol.Commands.kTopWindow:
              {
                const temp = JSON.parse(
                  notify.data
                ) as Protocol.TopWindowNotifyEntity;
                if (temp) {
                  for (const window of $store.state.windows) {
                    if (window && window.window_id != temp.new_window_id) {
                      $store.commit("setWindowProperty", {
                        window,
                        property_name: "focus",
                        value: false,
                      });
                    }
                  }
                  const window = $store.state.windows.find(
                    (item) => item.window_id == temp.new_window_id
                  );
                  $store.commit("setWindowProperty", {
                    window,
                    property_name: "focus",
                    value: true,
                  });
                  $store.commit("topWindow", temp.new_window_id);
                }
              }
              break;
            case Protocol.Commands.kLowerWindow:
              {
                const temp = JSON.parse(
                  notify.data
                ) as Protocol.LowerWindowNotifyEntity;
                if (temp) {
                  $store.commit("lowerWindow", temp.window_id);
                }
              }
              break;
            case Protocol.Commands.kCurrentRunningPlanStateChanged:
              {
                const temp = JSON.parse(
                  notify.data
                ) as Protocol.PlanRunningStateChangeNotifyEntity;
                if (temp && temp.plan) {
                  plan_running.value = temp.running;
                }
              }
              break;
          }
        } catch {}
      }
    );

    onMounted(() => {
      if (wall.value) {
        elementResizeDetectorMaker().listenTo(
          wall.value,
          (element: HTMLElement) => {
            if (element) {
              calcWallItemWH();
            }
          }
        );
      }
    });

    const moveWindow = (window_id: number, evt: any) => {
      evt.x = evt.x ?? 0;
      evt.y = evt.y ?? 0;
      if (wall.value) {
        const x =
          evt.left -
          (wall.value.parentElement?.offsetLeft ?? 0) -
          wall.value.offsetLeft;
        const y =
          evt.top -
          (wall.value.parentElement?.offsetTop ?? 0) -
          wall.value.offsetTop;
        GlobalData.getInstance()
          .getCurrentClient()
          ?.moveWindow(
            window_id,
            x / wall.value.clientWidth,
            y / wall.value.clientHeight
          );
      }
    };

    const resizeWindow = (window_id: number, evt: any) => {
      evt.width = evt.width ?? 0;
      evt.height = evt.height ?? 0;
      evt.left = evt.left ?? 0;
      evt.top = evt.top ?? 0;
      // moveWindow(window_id, evt);
      if (wall.value) {
        // GlobalData.getInstance()
        //   .getCurrentClient()
        //   ?.resizeWindow(
        //     window_id,
        //     evt.width / wall.value.clientWidth,
        //     evt.height / wall.value.clientHeight
        //   );
        const x =
          evt.left -
          (wall.value.parentElement?.offsetLeft ?? 0) -
          wall.value.offsetLeft;
        const y =
          evt.top -
          (wall.value.parentElement?.offsetTop ?? 0) -
          wall.value.offsetTop;
        GlobalData.getInstance()
          .getCurrentClient()
          ?.setWindowGeometry(
            window_id,
            x / wall.value.clientWidth,
            y / wall.value.clientHeight,
            evt.width / wall.value.clientWidth,
            evt.height / wall.value.clientHeight
          );
      }
    };

    return {
      windows,
      wall,
      wall_rows,
      wall_cols,
      item_witdh,
      item_height,
      plan_running,
      edit_volume_dialog,
      polling_setting_dialog,
      area_open_window_rect,
      area_open_window_flag,
      onDrop(e: DragEvent) {
        e.preventDefault();
        let target = e.target as any;
        if (target) {
          target.classList.remove("drag-enter");
        }

        let uuid = e.dataTransfer?.getData("uuid");
        if (uuid) {
          let signal_sources = GlobalData.getInstance().signal_source.filter(
            (item) => (item as any)?.uuid == uuid
          );

          if (signal_sources.length) {
            let signal_source = signal_sources[0];
            if (signal_source) {
              let dom: HTMLElement | null = e.target as HTMLElement;
              if (wall.value && dom) {
                if (dom.classList.contains("wall_item_flag")) {
                  // 开窗
                  GlobalData.getInstance()
                    .getCurrentClient()
                    ?.openWindow(
                      new Protocol.OpenWindowRequestEntity(
                        signal_source.uuid,
                        dom.offsetLeft / wall.value.clientWidth,
                        dom.offsetTop / wall.value.clientHeight,
                        dom.offsetWidth / wall.value.clientWidth,
                        dom.offsetHeight / wall.value.clientHeight
                      )
                    );
                } else if (dom.classList.contains("window_flag")) {
                  // 替换窗口
                  let uuid = dom.getAttribute("uuid");
                  if (uuid) {
                    let window = $store.state.windows.find(
                      (item) => item.uuid == uuid
                    );
                    if (window) {
                      let client = GlobalData.getInstance().getCurrentClient();
                      if (client) {
                        let x = window.x;
                        let y = window.y;
                        let width = window.width;
                        let height = window.height;

                        client.closeWindow(window.window_id);
                        setTimeout(() => {
                          client?.openWindow(
                            new Protocol.OpenWindowRequestEntity(
                              signal_source.uuid,
                              x,
                              y,
                              width,
                              height
                            )
                          );
                        }, 100);
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },

      onDragEnter(e: DragEvent) {
        e.stopPropagation();
        let target: HTMLElement | null = e.target as HTMLElement;
        if (target && target.draggable !== true) {
          while (
            target &&
            !target.classList.contains("window_flag") &&
            !target.classList.contains("wall_item_flag")
          ) {
            target = target.parentElement;
          }

          target?.classList.add("drag-enter");
        }
      },

      onDragLeave(e: DragEvent) {
        let target: HTMLElement = e.target as HTMLElement;
        target?.classList.remove("drag-enter");
      },

      onDragOver(e: DragEvent) {
        if (!plan_running.value) {
          e.preventDefault();
        }
      },

      onWallGridsClick(e: MouseEvent) {
        e.stopPropagation();
        $store.commit("setSelectedWindow", "");
      },
      moveWindow,
      resizeWindow,
      closeAllWindows() {
        for (const window of $store.state.windows) {
          if (window) {
            GlobalData.getInstance()
              .getCurrentClient()
              ?.closeWindow(window.window_id);
          }
        }
      },
      topWindow(window_id: number) {
        GlobalData.getInstance().getCurrentClient()?.focusIn(window_id);
      },
      lowerWindow(window_id: number) {
        GlobalData.getInstance().getCurrentClient()?.lowerWindow(window_id);
      },
      closeOtherWindows(window_id: number) {
        for (const window of $store.state.windows) {
          if (window && window.window_id != window_id) {
            GlobalData.getInstance()
              .getCurrentClient()
              ?.closeWindow(window.window_id);
          }
        }
      },
      windowDBClick(window_id: number) {
        if (plan_running.value) {
          return;
        }
        GlobalData.getInstance().getCurrentClient()?.windowFitGrid(window_id);
      },
      closeWindow(window_id: number) {
        GlobalData.getInstance().getCurrentClient()?.closeWindow(window_id);
      },
      edit_volume(window_id: number) {
        const window = windows.value.find(
          (element) => element && element.window_id == window_id
        );
        if (window) {
          edit_volume_dialog.value?.showDialog(window_id, window.volume);
        }
      },
      mute_unmute(window_id: number) {
        const window = windows.value.find(
          (element) => element && element.window_id == window_id
        );
        if (window) {
          if (window.muted) {
            GlobalData.getInstance()
              .getCurrentClient()
              ?.unmuteWindow(window_id);
          } else {
            GlobalData.getInstance().getCurrentClient()?.muteWindow(window_id);
          }
        }
      },
      start_polling(window_id: number) {
        const window = windows.value.find(
          (element) => element && element.window_id == window_id
        );
        if (window) {
          GlobalData.getInstance().getCurrentClient()?.startPolling(window_id);
        }
      },
      stop_polling(window_id: number) {
        const window = windows.value.find(
          (element) => element && element.window_id == window_id
        );
        if (window) {
          GlobalData.getInstance().getCurrentClient()?.stopPolling(window_id);
        }
      },
      async polling_setting(window_id: number) {
        const window = windows.value.find(
          (element) => element && element.window_id == window_id
        );
        if (window) {
          if (window.polling) {
            $q.notify({
              color: "positive",
              icon: "done",
              message: $t.t("please stop polling first") + "!",
              position: "top",
              timeout: 1500,
            });
            return;
          }
          const datas = await polling_setting_dialog.value.showDialogAsync(
            window.polling_signal_sources
          );
          if (datas) {
            try {
              const resposne = await GlobalData.getInstance()
                .getCurrentClient()
                ?.setPollingData(window_id, datas);
              if (resposne) {
                if (resposne.success) {
                  $q.notify({
                    color: "positive",
                    icon: "done",
                    message: $t.t("set polling data") + $t.t("success") + "!",
                    position: "top",
                    timeout: 1500,
                  });
                } else {
                  $q.notify({
                    color: "negative",
                    icon: "warning",
                    message: $t.t("set polling data") + $t.t("failed") + "!",
                    position: "top",
                    timeout: 1500,
                  });
                }
              }
            } catch (e) {
              console.log(e);
            }
          }
        }
      },
    };
  },
});
</script>
