import { ConnectTableEntity } from "src/entities/ConnectTableEntity";
import { PollingEntity } from "src/entities/PollingEntity";
import ReconnectingWebSocket from "reconnecting-websocket";
import NormalWindowRequestEntity from "src/entities/NormalWindowRequestEntity";
import { PlanEntity } from "src/entities/PlanEntity";
import { SignalSourceEntity } from "src/entities/SignalSourceEntity";
import { StringKeyValueEntity } from "src/entities/StringKeyValueEntity";
import SubtitleEntity from "src/entities/SubtitleEntity";
import { Protocol } from "src/entities/WSProtocol";
import EventBus, { EventNamesDefine } from "./EventBus";
import { EdgeBlendingPoint } from "src/entities/EdgeBlendingEntities";
import { ExternalControlTableEntity } from "src/entities/ExternalControlTableEntity";
import { SerialPortConfigEntity } from "src/entities/SerialPortConfigEntity";
import TimingTaskEntity from "src/entities/TimingTaskEntity";
import JointActionEquipmentTableEntity from "src/entities/JointActionEquipmentTableEntity";
import { CustomProtocol } from "src/entities/WSProtocolCustom";
import ClientConnectionCustom from "./ClientConnectionCustom";
import MagicWallConfig from "src/entities/MagicWallConfig";
import WuJieReconnectingWebSocket from "./WuJieReconnectingWebSocket";

class _RpcInfo {
  send_timestamp: number;
  timeout_timestamp: number;
  callback: (is_fail: boolean, packet: Protocol.Commands, data: string) => void;

  constructor(
    timeout_timestamp: number,
    callback: (
      is_fail: boolean,
      packet: Protocol.Commands,
      data: string
    ) => void
  ) {
    this.timeout_timestamp = timeout_timestamp;
    this.send_timestamp = new Date().getTime();
    this.callback = callback;
  }

  public reject() {
    this.callback(true, new Protocol.Commands(), "");
  }

  public reslove(is_fail: boolean, packet: Protocol.Commands, data: string) {
    this.callback(is_fail, packet, data);
  }
}

export default class ClientConnection {
  ws: ReconnectingWebSocket | null | WuJieReconnectingWebSocket = null;
  url = "";
  user_name = "";
  password = "";
  _is_login = false;
  _rpc_id_counter = 0;
  rpc_map = new Map<number, _RpcInfo>();

  custom_connection: ClientConnectionCustom = new ClientConnectionCustom(this);

  public login_callback:
    | ((this: ClientConnection, logined: boolean) => void)
    | null = null;

  constructor(
    url: string,
    user_name?: string | null,
    password?: string | null
  ) {
    this.url = url;
    this.user_name = user_name ?? "";
    this.password = password ?? "";
    const $wujie = (window as any).$wujie;

    if (this.ws) {
      this.ws.close();
    }
    if ($wujie) {
      this.ws = new WuJieReconnectingWebSocket();
    } else {
      this.ws = new ReconnectingWebSocket(url);
    }
    this.initializeWs();
    setInterval(() => {
      this.checkRpcTimeout();
    }, 1000);
  }

  get is_connected() {
    return this.ws && this.ws.readyState == WebSocket.OPEN;
  }

  get is_login() {
    return this._is_login;
  }

  get customConnection() {
    return this.custom_connection ?? new ClientConnectionCustom(this);
  }

  checkRpcTimeout() {
    const current_datetime = new Date().getTime();
    this.rpc_map.forEach((v, k, m) => {
      if (current_datetime - v.send_timestamp > v.timeout_timestamp) {
        v.reject();
        m.delete(k);
      }
    });
  }

  initializeWs() {
    if (this.ws) {
      this.ws.onclose = (ev: any) => {
        this.onClose(ev);
      };
      this.ws.onerror = (ev: any) => {
        this.onError(ev);
      };
      this.ws.onopen = (ev: any) => {
        this.onOpen(ev);
      };
      this.ws.onmessage = (ev) => {
        this.onMessage(ev);
      };
    }
  }

  login() {
    if (this.is_connected) {
      const request = new Protocol.LoginRequest(this.user_name, this.password);
      this.ws?.send(JSON.stringify(request));
    }
  }

  onClose(ev: any) {
    this._is_login = false;
    EventBus.getInstance().emit(EventNamesDefine.WebSocketClose, this);
  }

  onError(ev: any) {
    this._is_login = false;
    EventBus.getInstance().emit(EventNamesDefine.WebSocketError, this);
  }

  onOpen(ev: any) {
    this._is_login = false;
    this.login();
  }

  onMessage(ev: MessageEvent) {
    try {
      const packet = JSON.parse(ev.data) as Protocol.PacketEntity;

      if (packet) {
        if (packet.has_exception) {
          console.error(ev.data);
          this.rpc_map.get(packet.rpc_id)?.reject();
          this.rpc_map.delete(packet.rpc_id);
        }
        if (
          Protocol.Commands.AllCommands.has(packet.command) ||
          CustomProtocol.Commands.AllCommands.has(packet.command)
        ) {
          if (
            packet.flag == Protocol.PacketEntity.FLAG_RESPONSE ||
            packet.flag == Protocol.PacketEntity.FLAG_NOTIFY
          ) {
            if (packet.command == Protocol.Commands.kLogin) {
              const login_response = JSON.parse(
                ev.data
              ) as Protocol.LoginResponse;
              if (login_response) {
                this._is_login =
                  !login_response.has_exception && login_response.success;
                if (this.is_login) {
                  EventBus.getInstance().emit(
                    EventNamesDefine.WebSocketConnected,
                    this
                  );
                }
                if (
                  this.login_callback &&
                  typeof this.login_callback == "function"
                ) {
                  this.login_callback(this._is_login);
                }
              }
            } else if (this.rpc_map.has(packet.rpc_id)) {
              this.rpc_map.get(packet.rpc_id)?.reslove(false, packet, ev.data);
              this.rpc_map.delete(packet.rpc_id);
            } else {
              EventBus.getInstance().emit(
                packet.flag == Protocol.PacketEntity.FLAG_NOTIFY
                  ? EventNamesDefine.NotifyMessage
                  : packet.flag == Protocol.PacketEntity.FLAG_RESPONSE
                  ? EventNamesDefine.ResponseMessage
                  : EventNamesDefine.UnKnow,
                {
                  packet: packet,
                  data: ev.data,
                }
              );
            }
          }
        } else {
          console.error("unknow command: " + packet.command, packet);
        }
      }
    } catch (e) {
      console.error(e);
    }
  }

  public async doRpc<_ResponseType>(
    request: Protocol.PacketEntity
  ): Promise<_ResponseType | null> {
    return new Promise((resolve, reject) => {
      const rpc_id = ++this._rpc_id_counter;
      if (this.rpc_map.has(rpc_id)) {
        this.rpc_map.get(rpc_id)?.reject();
        this.rpc_map.delete(rpc_id);
      }
      request.rpc_id = rpc_id;
      this.rpc_map.set(
        rpc_id,
        new _RpcInfo(
          request.timeout,
          (is_fail: boolean, packet: Protocol.Commands, data: string) => {
            if (is_fail) {
              reject();
            } else {
              try {
                const response = JSON.parse(data) as _ResponseType;
                if (response) {
                  resolve(response);
                } else {
                  reject();
                }
              } catch {
                reject();
              }
            }
          }
        )
      );
      if (request) {
        request.timeout;
      }
      this.ws?.send(JSON.stringify(request));
    });
  }

  public async getSignalSources() {
    try {
      return await this.doRpc<Protocol.GetSignalSourcesResponse>(
        new Protocol.GetSignalSourcesRequest()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addSignalSourceGroup(parent_uuid: string, name: string) {
    try {
      return await this.doRpc<Protocol.AddSignalSourceGroupResponseEntity>(
        new Protocol.AddSignalSourceGroupRequestEntity(0, parent_uuid, name)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editSignalSourceGroup(
    uuid: string,
    name: string,
    parent_uuid: string
  ) {
    try {
      return await this.doRpc<Protocol.EditSignalSourceGroupResponseEntity>(
        new Protocol.EditSignalSourceGroupRequestEntity(
          0,
          uuid,
          name,
          parent_uuid
        )
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deleteSignalSourceGroup(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeleteSignalSourceGroupResponseEntity>(
        new Protocol.DeleteSignalSourceGroupRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addSignalSource(item: SignalSourceEntity) {
    try {
      return await this.doRpc<Protocol.AddSignalSourceResponseEntity>(
        new Protocol.AddSignalSourceRequestEntity(0, item)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editSignalSource(item: SignalSourceEntity) {
    try {
      return await this.doRpc<Protocol.EditSignalSourceResponseEntity>(
        new Protocol.EditSignalSourceRequestEntity(0, item)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deleteSignalSource(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeleteSignalSourceResponseEntity>(
        new Protocol.DeleteSignalSourceRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getModes() {
    try {
      return await this.doRpc<Protocol.GetModesResponseEntity>(
        new Protocol.GetModesRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addPlanGroup(group_uuid: string, name: string) {
    try {
      return await this.doRpc<Protocol.AddPlanGroupResponseEntity>(
        new Protocol.AddPlanGroupRequestEntity(0, group_uuid, name)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editPlanGroup(uuid: string, name: string, parent_uuid: string) {
    try {
      return await this.doRpc<Protocol.EditPlanGroupResponseEntity>(
        new Protocol.EditPlanGroupRequestEntity(0, uuid, name, parent_uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deletePlanGroup(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeletePlanGroupResponseEntity>(
        new Protocol.DeletePlanGroupRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addPlan(entity?: PlanEntity) {
    try {
      return await this.doRpc<Protocol.AddPlanResponseEntity>(
        new Protocol.AddPlanRequestEntity(0, entity)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editPlan(entity?: PlanEntity) {
    try {
      return await this.doRpc<Protocol.EditPlanResponseEntity>(
        new Protocol.EditPlanRequestEntity(0, entity)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deletePlan(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeletePlanResponseEntity>(
        new Protocol.DeletePlanRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deletePollingGroup(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeletePollingGroupResponseEntity>(
        new Protocol.DeletePollingGroupRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addPolling(
    group_uuid: string,
    name: string,
    datas: StringKeyValueEntity[]
  ) {
    try {
      return await this.doRpc<Protocol.AddPollingResponseEntity>(
        new Protocol.AddPollingRequestEntity(0, group_uuid, name, datas, "")
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editPolling(entity: PollingEntity) {
    try {
      return await this.doRpc<Protocol.EditPollingResponseEntity>(
        new Protocol.EditPollingRequestEntity(0, entity)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deletePolling(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeletePollingResponseEntity>(
        new Protocol.DeletePollingRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getCurrentRunningPlan() {
    try {
      return await this.doRpc<Protocol.GetCurrentRunningPlanResponseEntity>(
        new Protocol.GetCurrentRunningPlanRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public runPlan(uuid: string) {
    this.ws?.send(JSON.stringify(new Protocol.RunPlanRequestEntity(uuid)));
  }

  public stopCurrentRunningPlan() {
    this.ws?.send(
      JSON.stringify(new Protocol.StopCurrentRunningPlanRequestEntity())
    );
  }

  public async getPlans() {
    try {
      return await this.doRpc<Protocol.GetPlansResponseEntity>(
        new Protocol.GetPlansRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addModeGroup(group_uuid: string, name: string) {
    try {
      return await this.doRpc<Protocol.AddModeGroupResponseEntity>(
        new Protocol.AddModeGroupRequestEntity(0, group_uuid, name)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editModeGroup(uuid: string, name: string, parent_uuid: string) {
    try {
      return await this.doRpc<Protocol.EditModeGroupResponseEntity>(
        new Protocol.EditModeGroupRequestEntity(0, uuid, name, parent_uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async updateModeWindowList(uuid: string) {
    try {
      return await this.doRpc<Protocol.UpdateModeWindowListResponseEntity>(
        new Protocol.UpdateModeWindowListRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deleteModeGroup(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeleteModeGroupResponseEntity>(
        new Protocol.DeleteModeGroupRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addMode(
    group_uuid?: string,
    name?: string,
    index?: number,
    joint_action_equipments?: StringKeyValueEntity[]
  ) {
    try {
      return await this.doRpc<Protocol.AddModeResponseEntity>(
        new Protocol.AddModeRequestEntity(
          0,
          name,
          group_uuid,
          index,
          joint_action_equipments
        )
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editMode(
    uuid?: string,
    name?: string,
    index?: number,
    group_uuid?: string,
    joint_action_equipments?: StringKeyValueEntity[]
  ) {
    try {
      return await this.doRpc<Protocol.EditModeResponseEntity>(
        new Protocol.EditModeRequestEntity(
          0,
          name,
          uuid,
          index,
          group_uuid,
          joint_action_equipments
        )
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async deleteMode(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeleteModeResponseEntity>(
        new Protocol.DeleteModeRequestEntity(0, uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public callMode(uuid: string) {
    this.ws?.send(JSON.stringify(new Protocol.CallModeRequestEntity(uuid)));
  }

  public cleanBrowserCache() {
    this.ws?.send(
      JSON.stringify(
        new Protocol.NormalRequestEntity(Protocol.Commands.kCleanBrowserCache)
      )
    );
  }

  public async getApplicationSettins() {
    try {
      return await this.doRpc<Protocol.GetApplicationConfigResponseEntity>(
        new Protocol.GetApplicationConfigRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getWindows() {
    try {
      return await this.doRpc<Protocol.GetWindowsResponseEntity>(
        new Protocol.GetWindowsRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getDeviceAttribute() {
    try {
      return await this.doRpc<Protocol.GetDeviceAttributeResponseEntity>(
        new Protocol.GetDeviceAttributeRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public setDeviceAttribute(attribute: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.SetDeviceAttributeRequestEntity(attribute))
    );
  }

  public moveWindow(window_id: number, x: number, y: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.MoveWindowRequestEntity(window_id, x, y))
    );
  }

  public resizeWindow(window_id: number, width: number, height: number) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.ResizeWindowRequestEntity(window_id, width, height)
      )
    );
  }

  public setWindowGeometry(
    window_id: number,
    x: number,
    y: number,
    width: number,
    height: number,
    limit: boolean = true
  ) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetWindowGeometryRequestEntity(
          window_id,
          x,
          y,
          width,
          height,
          limit
        )
      )
    );
  }

  public closeWindow(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.CloseWindowRequestEntity(window_id))
    );
  }

  public openWindow(data: Protocol.OpenWindowRequestEntity) {
    this.ws?.send(JSON.stringify(data));
  }

  public replaceWindow(data: Protocol.ReplaceWindowRequestEntity) {
    this.ws?.send(JSON.stringify(data));
  }

  public replaceWindow2(
    window_id: number,
    signal_source: string,
    polling?: boolean,
    ext_data?: string
  ) {
    this.replaceWindow(
      new Protocol.ReplaceWindowRequestEntity(
        window_id,
        signal_source,
        polling,
        ext_data
      )
    );
  }

  public openPolling(data: Protocol.OpenPollingRequestEntity) {
    this.ws?.send(JSON.stringify(data));
  }

  public focusIn(window_id: number) {
    this.ws?.send(
      JSON.stringify(
        new NormalWindowRequestEntity(Protocol.Commands.kTopWindow, window_id)
      )
    );
  }

  public lowerWindow(window_id: number) {
    this.ws?.send(
      JSON.stringify(
        new NormalWindowRequestEntity(Protocol.Commands.kLowerWindow, window_id)
      )
    );
  }

  public setWallRowCol(wall_row: number, wall_col: number) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(
          0,
          "wall_row_col",
          wall_row.toString() + "X" + wall_col.toString()
        )
      )
    );
  }

  public setConfigure(k: string, v: string) {
    this.ws?.send(
      JSON.stringify(new Protocol.SetApplicationConfigRequestEntity(0, k, v))
    );
  }

  public setUserName(user_name: string) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(
          0,
          "user_name",
          user_name
        )
      )
    );
  }

  public setPassword(password: string) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(0, "password", password)
      )
    );
  }

  public setSpecialVideoLayoutRotation(rotation: string) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(
          0,
          "special_video_layout_rotation",
          rotation
        )
      )
    );
  }

  public setWebsocketPort(port: number) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(
          0,
          "websocket_port",
          port.toString()
        )
      )
    );
  }

  public setHttpPort(port: number) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(
          0,
          "httpserver_port",
          port.toString()
        )
      )
    );
  }

  public setPowerOnPlan(uuid: string) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetApplicationConfigRequestEntity(0, "power_on_plan", uuid)
      )
    );
  }

  public windowFitGrid(window_id: number) {
    this.ws?.send(
      JSON.stringify(
        new NormalWindowRequestEntity(
          Protocol.Commands.kWindowFitGrid,
          window_id
        )
      )
    );
  }

  public windowFullScreen(window_id: number, full_screen: boolean) {
    this.ws?.send(
      JSON.stringify(new Protocol.WindowFullScreen(window_id, full_screen))
    );
  }

  public async getSubtitle() {
    try {
      return await this.doRpc<Protocol.GetSubtitleResponseEntity>(
        new Protocol.GetSubtitleRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getRegisterInfo() {
    try {
      return await this.doRpc<Protocol.GetRegisterInfoResponseEntity>(
        new Protocol.GetRegisterInfoRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async registerDevice(
    register_code: string,
    active_code: string,
    online: boolean,
    active_forever: boolean,
    secret_key?: string,
    hour?: number
  ) {
    try {
      return await this.doRpc<Protocol.RegisterDeviceResponseEntity>(
        new Protocol.RegisterDeviceRequestEntity(
          register_code,
          active_code,
          online,
          active_forever,
          secret_key,
          hour,
          0
        )
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setSubtitle(subtitle: SubtitleEntity) {
    try {
      return await this.doRpc<Protocol.SetSubtitleResponseEntity>(
        new Protocol.SetSubtitleRequestEntity(0, subtitle)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setSystemNetwork(
    request: Protocol.SetSystemNetworkRequestEntity
  ) {
    return await this.doRpc<Protocol.SetSystemNetworkResponseEntity>(request);
  }
  public async setSystemGraphics(
    request: Protocol.SetSystemGraphicsRequestEntity
  ) {
    return await this.doRpc<Protocol.SetSystemGraphicsResponseEntity>(request);
  }
  public async setSystemOther(request: Protocol.SetSystemOtherRequestEntity) {
    return await this.doRpc<Protocol.SetSystemOtherResponseEntity>(request);
  }
  public async getSupportResolutions() {
    try {
      return await this.doRpc<Protocol.GetSupportResolutionsResponseEntity>(
        new Protocol.GetSupportResolutionsRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getOutputBoardSetting() {
    try {
      return await this.doRpc<Protocol.GetOutputBoardSettingResponseEntity>(
        new Protocol.GetOutputBoardSettingRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setOutputBoardSetting(
    request: Protocol.SetOutputBoardSettingRequestEntity
  ) {
    try {
      return await this.doRpc<Protocol.SetOutputBoardSettingResponseEntity>(
        request
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async switchOutputBoardSplitState() {
    try {
      this.ws?.send(
        JSON.stringify(new Protocol.SwitchOutputBoardSplitStateRequestEntity())
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async restoreOutputBoard() {
    try {
      return await this.doRpc<Protocol.RestoreOutputBoardResponseEntity>(
        new Protocol.RestoreOutputBoardRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setOutputBoardSpliceIndex(
    device_index: number,
    splice_index: number
  ) {
    try {
      return await this.doRpc<Protocol.RpcOutputBoardSpliceIndexResponseEntity>(
        new Protocol.RpcOutputBoardSpliceIndexRequestEntity(
          device_index,
          splice_index
        )
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setWindowVolume(window_id: number, volume: number) {
    this.ws?.send(
      JSON.stringify(
        new Protocol.SetWindowVolumeRequestEntity(window_id, volume, 0)
      )
    );
  }

  public async unmuteWindow(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.UnMuteWindowRequestEntity(window_id, 0))
    );
  }

  public async muteWindow(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.MuteWindowRequestEntity(window_id, 0))
    );
  }

  public async playWindow(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.PlayWindowRequestEntity(window_id, 0))
    );
  }

  public async windowPlayNext(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.WindowPlayNextRequestEntity(window_id, 0))
    );
  }

  public async windowPlayPrev(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.WindowPlayPrevRequestEntity(window_id, 0))
    );
  }

  public async pauseWindow(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.PauseWindowRequestEntity(window_id, 0))
    );
  }

  public async startPolling(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.StartPollingRequestEntity(window_id))
    );
  }

  public async stopPolling(window_id: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.StopPollingRequestEntity(window_id))
    );
  }

  public async setWindowPollingData(
    window_id: number,
    name: string,
    datas?: StringKeyValueEntity[]
  ) {
    return await this.doRpc<Protocol.SetWindowPollingDataResponseEntity>(
      new Protocol.SetWindowPollingDataRequestEntity(window_id, name, datas)
    );
  }

  public async getBuildInfo() {
    return await this.doRpc<Protocol.GetBuildInfoResponseEntity>(
      new Protocol.GetBuildInfoRequestEntity(0)
    );
  }

  public async getSystemTimes() {
    return await this.doRpc<Protocol.GetSystemTimesResponseEntity>(
      new Protocol.GetSystemTimesRequestEntity(0)
    );
  }

  public restoreDevice(delete_upload: boolean = false) {
    this.ws?.send(
      JSON.stringify(new Protocol.RestoreDeviceRequestEntity(delete_upload))
    );
  }

  public restartDevice(delay_ms?: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.RestartDeviceRequestEntity(delay_ms))
    );
  }

  public async getUsbDevices() {
    try {
      return await this.doRpc<Protocol.GetUsbDevicesResponseEntity>(
        new Protocol.GetUsbDevicesRequestEntity(0)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getPollings() {
    try {
      return await this.doRpc<Protocol.GetPollingsResponseEntity>(
        new Protocol.GetPollingsRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async fileOperator(
    from_path: string,
    to_path: string,
    operator_type: string,
    force_operator: boolean = true
  ) {
    try {
      return await this.doRpc<Protocol.FileOperatorResponseEntity>(
        new Protocol.FileOperatorRequestEntity(
          from_path,
          to_path,
          operator_type,
          force_operator
        )
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getEdgeBlendingInfo() {
    return await this.doRpc<Protocol.GetEdgeBlendingInfoResponseEntity>(
      new Protocol.GetEdgeBlendingInfoRequestEntity(0)
    );
  }

  public async setEdgeBlendingInfo(
    enable_blending: boolean,
    enable_correct: boolean,
    width: number,
    height: number,
    col: number,
    row: number,
    point_count: number
  ) {
    return await this.doRpc<Protocol.SetEdgeBlendingInfoResponseEntity>(
      new Protocol.SetEdgeBlendingInfoRequestEntity(
        enable_blending,
        enable_correct,
        width,
        height,
        col,
        row,
        point_count,
        0
      )
    );
  }

  public async wakeUpDevice() {
    return await this.doRpc<Protocol.SetDevicePowerResponseEntity>(
      new Protocol.SetDevicePowerRequestEntity(
        Protocol.SetDevicePowerRequestEntity.kPowerStatePowerOn
      )
    );
  }

  public async deviceStandByMode() {
    return await this.doRpc<Protocol.SetDevicePowerResponseEntity>(
      new Protocol.SetDevicePowerRequestEntity(
        Protocol.SetDevicePowerRequestEntity.kPowerStateStandBy
      )
    );
  }

  public async getExternalControlDatas() {
    return await this.doRpc<Protocol.RpcGetExternalControlDatasResponseEntity>(
      new Protocol.RpcGetExternalControlDatasRequestEntity()
    );
  }

  public async deleteExternalControlData(number: number) {
    return await this.doRpc<Protocol.RpcDeleteExternalControlDataResponseEntity>(
      new Protocol.RpcDeleteExternalControlDataRequestEntity(number)
    );
  }

  public async editExternalControlData(entity: ExternalControlTableEntity) {
    return await this.doRpc<Protocol.RpcEditExternalControlDataResponseEntity>(
      new Protocol.RpcEditExternalControlDataRequestEntity(entity)
    );
  }

  public async addExternalControlData(entity: ExternalControlTableEntity) {
    return await this.doRpc<Protocol.RpcAddExternalControlDataResponseEntity>(
      new Protocol.RpcAddExternalControlDataRequestEntity(entity)
    );
  }

  public async getExternalControlSerialPortConfig() {
    return await this.doRpc<Protocol.RpcGetExternalControlSerialPortConfigResponseEntity>(
      new Protocol.RpcGetExternalControlSerialPortConfigRequestEntity()
    );
  }

  public async setExternalControlSerialPortConfig(
    serial_port: SerialPortConfigEntity,
    tcp_address: string,
    tcp_port: number,
    udp_address: string,
    udp_port: number,
    current_type: string
  ) {
    return await this.doRpc<Protocol.RpcSetExternalControlSerialPortConfigResponseEntity>(
      new Protocol.RpcSetExternalControlSerialPortConfigRequestEntity(
        serial_port,
        tcp_address,
        tcp_port,
        udp_address,
        udp_port,
        current_type
      )
    );
  }

  public async callExternalControlData(number: number) {
    return await this.doRpc<Protocol.RpcCallExternalControlDataResponseEntity>(
      new Protocol.RpcCallExternalControlDataRequestEntity(number)
    );
  }

  public setEdgeBlendingPoint(point: EdgeBlendingPoint) {
    this.ws?.send(
      JSON.stringify(new Protocol.SetEdgeBlendingPointRequestEntity(point))
    );
  }

  public setServerLanguage(language: string) {
    this.ws?.send(
      JSON.stringify(new Protocol.SetLanguageRequestEntity(language))
    );
  }

  public async getConnectList() {
    return await this.doRpc<Protocol.GetConnectionListResponseEntity>(
      new Protocol.GetConnectionListRequestEntity()
    );
  }
  public async setConnectItem(entity: ConnectTableEntity) {
    return await this.doRpc<Protocol.SetConnectionItemResponseEntity>(
      new Protocol.SetConnectionItemRequestEntity(entity)
    );
  }

  public async setHdmiRotation(rotation: number) {
    this.ws?.send(
      JSON.stringify(new Protocol.SetHDMIRotationRequestEntity(rotation))
    );
  }

  public async getTimingTasks() {
    try {
      return await this.doRpc<Protocol.GetTimingTasksResponseEntity>(
        new Protocol.GetTimingTasksRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async addTimingTask(task: TimingTaskEntity) {
    try {
      return await this.doRpc<Protocol.AddTimingTaskResponseEntity>(
        new Protocol.AddTimingTaskRequestEntity(task)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async editTimingTask(task: TimingTaskEntity) {
    try {
      return await this.doRpc<Protocol.EditTimingTaskResponseEntity>(
        new Protocol.EditTimingTaskRequestEntity(task)
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async deleteTimingTask(uuid: string) {
    try {
      return await this.doRpc<Protocol.DeleteTimingTaskResponseEntity>(
        new Protocol.DeleteTimingTaskRequestEntity(uuid)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getSystemNetworkInfo() {
    try {
      return await this.doRpc<Protocol.GetSystemNetworkInfoResponseEntity>(
        new Protocol.GetSystemNetworkInfoRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getMagicWallConfig() {
    try {
      return await this.doRpc<Protocol.RpcGetMagicWallConfigResponseEntity>(
        new Protocol.RpcGetMagicWallConfigRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async setMagicWallConfig(config: MagicWallConfig) {
    try {
      return await this.doRpc<Protocol.RpcSetMagicWallConfigResponseEntity>(
        new Protocol.RpcSetMagicWallConfigRequestEntity(config)
      );
    } catch (e) {
      console.error(e);
    }
  }
  //
  public async getMagicWallGridState() {
    try {
      return await this.doRpc<Protocol.RpcGetMagicWallGridStateResponseEntity>(
        new Protocol.RpcGetMagicWallGridStateRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setMagicWallGridState(show_grid: boolean) {
    try {
      return await this.doRpc<Protocol.RpcSetMagicWallGridStateResponseEntity>(
        new Protocol.RpcSetMagicWallGridStateRequestEntity(show_grid)
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async testA(
    x: number,
    y: number,
    w: number,
    h: number,
    angle: number
  ) {
    try {
      return await this.doRpc<Protocol.RpcTestAResponseEntity>(
        new Protocol.RpcTestARequestEntity(x, y, w, h, angle)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setHdmiInDecodeType(index: number, type: string) {
    try {
      return await this.doRpc<Protocol.SetHdmiInDecodeTypeResponseEntity>(
        new Protocol.SetHdmiInDecodeTypeRequestEntity(index, type)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async setHdmiInAudioDevice(index: number, type: string) {
    try {
      return await this.doRpc<Protocol.SetHdmiInAudioDeviceResponseEntity>(
        new Protocol.SetHdmiInAudioDeviceRequestEntity(index, type)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getCityList(language?: string) {
    try {
      return await this.doRpc<Protocol.GetCityListResponseEntity>(
        new Protocol.GetCityListRequestEntity(language)
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getPowerState() {
    try {
      return await this.doRpc<Protocol.GetPowerStateResponseEntity>(
        new Protocol.GetPowerStateRequestEntity()
      );
    } catch (e) {
      console.error(e);
    }
  }

  public async getJointActionEquipments() {
    return await this.doRpc<Protocol.GetJointActionEquipmentResponseEntity>(
      new Protocol.GetJointActionEquipmentRequestEntity()
    );
  }

  public async setJointActionEquipment(
    entity: JointActionEquipmentTableEntity
  ) {
    return await this.doRpc<Protocol.SetJointActionEquipmentResponseEntity>(
      new Protocol.SetJointActionEquipmentRequestEntity(entity)
    );
  }

  public async deleteJointActionEquipment(uuid: string) {
    return await this.doRpc<Protocol.DeleteJointActionEquipmentResponseEntity>(
      new Protocol.DeleteJointActionEquipmentRequestEntity(uuid)
    );
  }

  public async setBlendingCorrection(
    row: number,
    column: number,
    correction_type: number,
    control_point_count: number,
    control_point: number,
    x: number,
    y: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingCorrectionRequestEntity(
        row,
        column,
        correction_type,
        control_point_count,
        control_point,
        x,
        y
      )
    );
  }

  public async setBlendingAlphaParam(
    row: number,
    column: number,
    location: number,
    value: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingAlphaParamRequestEntity(
        row,
        column,
        location,
        value
      )
    );
  }

  public async setBlendingPowerParam(
    row: number,
    column: number,
    location: number,
    value: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingPowerParamRequestEntity(
        row,
        column,
        location,
        value
      )
    );
  }

  public async setBlendingGammaParam(
    row: number,
    column: number,
    location: number,
    value: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingGammaParamRequestEntity(
        row,
        column,
        location,
        value
      )
    );
  }

  public async EnableBlending(enable: boolean) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.EnableBlendingRequestEntity(enable)
    );
  }

  public async SetBlendingOverlap(
    row: number,
    column: number,
    location: number,
    enable: boolean,
    width: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingOverlapRequestEntity(
        row,
        column,
        location,
        enable,
        width
      )
    );
  }

  public async GetBlendingConfig(name: string) {
    return await this.doRpc<Protocol.GetBlendingConfigResponseEntity>(
      new Protocol.GetBlendingConfigRequestEntity(name)
    );
  }

  public async SaveBlendingConfig(name: string) {
    return await this.doRpc<Protocol.SaveBlendingConfigResponseEntity>(
      new Protocol.SaveBlendingConfigRequestEntity(name)
    );
  }

  public async SetBlendingHorDensity(
    row: number,
    column: number,
    control_point_count: number,
    control_point: number,
    value: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingHorDensityRequestEntity(
        row,
        column,
        control_point_count,
        control_point,
        value
      )
    );
  }

  public async SetBlendingVerDensity(
    row: number,
    column: number,
    control_point_count: number,
    control_point: number,
    value: number
  ) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingVerDensityRequestEntity(
        row,
        column,
        control_point_count,
        control_point,
        value
      )
    );
  }

  public async ResetBlendingConfig() {
    return await this.doRpc<Protocol.GetBlendingConfigResponseEntity>(
      new Protocol.ResetBlendingConfigRequestEntity()
    );
  }

  public async SetBlendingOption(id: string, value: string) {
    return await this.doRpc<Protocol.NoneResponse>(
      new Protocol.SetBlendingOptionRequestEntity(id, value)
    );
  }

  public async EnumBlendingScene() {
    return await this.doRpc<Protocol.EnumBlendingSceneResponseEntity>(
      new Protocol.EnumBlendingSceneRequestEntity()
    );
  }

  public async ApplyBlendingScene(name: string) {
    return await this.doRpc<Protocol.GetBlendingConfigResponseEntity>(
      new Protocol.ApplyBlendingSceneRequestEntity(name)
    );
  }

  public async DeleteBlendingScene(name: string) {
    return await this.doRpc<Protocol.RpcBlendingResponseEntity>(
      new Protocol.DeleteBlendingSceneRequestEntity(name)
    );
  }

  public async AddBlendingCtrlPoint(
    type: string,
    row: number,
    column: number,
    count: number
  ) {
    return await this.doRpc<Protocol.GetBlendingConfigResponseEntity>(
      new Protocol.AddBlendingCtrlPointRequestEntity(type, row, column, count)
    );
  }

  public async DelBlendingCtrlPoint(
    type: string,
    row: number,
    column: number,
    count: number
  ) {
    return await this.doRpc<Protocol.GetBlendingConfigResponseEntity>(
      new Protocol.DelBlendingCtrlPointRequestEntity(type, row, column, count)
    );
  }

  public async SetProjectorResolution(
      width: number,
      height: number
  ) {
    return await this.doRpc<Protocol.GetBlendingConfigResponseEntity>(
      new Protocol.SetProjectorResolutionRequestEntity(width, height)
    );
  }

  public async setCloudServerSetting(
    cloud_server_address: string,
    cloud_server_verify_key: string,
    cloud_server_use_wss: boolean,
    cloud_server_enable: boolean
  ) {
    this.setConfigure("cloud_server_address", cloud_server_address);
    this.setConfigure("cloud_server_verify_key", cloud_server_verify_key);
    this.setConfigure("cloud_server_use_wss", cloud_server_use_wss ? "1" : "0");
    this.setConfigure("cloud_server_enable", cloud_server_enable ? "1" : "0");
  }

  public destory() {
    this.ws?.close();
    if (this.ws) {
      this.ws.onclose = null;
      this.ws.onerror = null;
      this.ws.onopen = null;
      this.ws.onmessage = null;
    }
    this.ws = null;
  }
  public async getFileListFileManager(dir_path: string) {
    try {
      return await this.doRpc<Protocol.RpcFileManagerGetFileListResponse>(
        new Protocol.RpcFileManagerGetFileListRequest(dir_path)
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async RenameFileManager(
    base_dir: string,
    file_name: string,
    new_file_name: string
  ) {
    try {
      return await this.doRpc<Protocol.RpcFileManagerRenameResponse>(
        new Protocol.RpcFileManagerRenameRequest(
          base_dir,
          file_name,
          new_file_name
        )
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async DeleteFileManager(base_dir: string, file_name: string) {
    try {
      return await this.doRpc<Protocol.RpcFileManagerDeleteResponse>(
        new Protocol.RpcFileManagerDeleteRequest(base_dir, file_name)
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async CreateDirectoryFileManager(base_dir: string, dir_name: string) {
    try {
      return await this.doRpc<Protocol.RpcFileManagerCreateDirectoryResponse>(
        new Protocol.RpcFileManagerCreateDirectoryRequest(base_dir, dir_name)
      );
    } catch (e) {
      console.error(e);
    }
  }
  public async CheckModeIndex(mode_index: number) {
    try {
      return await this.doRpc<Protocol.RpcCheckModeIndexResponseEntity>(
        new Protocol.RpcCheckModeIndexRequestEntity(mode_index)
      );
    } catch (e) {
      console.error(e);
    }
  }
}

export interface NotifyMessage {
  packet: Protocol.PacketEntity;
  data: string;
}