/* eslint-disable react/no-unused-class-component-methods */
/* eslint-disable react/no-unused-prop-types */
/* eslint-disable react/require-default-props */
import { CoordinateController, CoordinateType, InteractiveConfig, InteractiveController, InteractiveType, SceneEvent, EventType, ViewEntity } from '@turbox3d/event-manager';
import { remove, Vec2, Vec3, getContextParam, getContextEnv } from '@turbox3d/shared';
import { SceneTool, CommandManager } from '@turbox3d/command-manager';
import { Component, ComponentProps } from './component';

declare global {
  interface Window {
    $$turbox_hot: boolean;
  }
}

interface IInteractiveControllerInfo<Container, DisplayObject, Renderer> {
  [id: string]: InteractiveController<Container, DisplayObject, Renderer>;
}

interface ICanvasInfo<Renderer> {
  height: number;
  width: number;
  renderer: Renderer;
}

export interface ViewportInfo {
  x: number;
  y: number;
  width: number;
  height: number;
  visible: boolean;
  resolution?: number;
}

export enum SceneType {
  Scene2D = '2d',
  Scene3D = '3d',
}

export interface BaseSceneProps {
  /**
   * 用以标示当前场景的 ID
   * 当多个不同 id 的场景关联到同一容器中时，可用于区分场景和隔离交互控制器
   */
  id: string;
  /**
   * 用以标示当前场景的类型
   */
  type?: symbol;
  /**
   * 画布容器标识
   * 多个不同 id 的场景可关联同一个容器/渲染器上下文
   */
  container: string;
  /**
   * 画布背景色，使用十六进制。默认：0xffffff
   */
  backgroundColor?: number;
  /**
   * 画布背景图，优先级高于背景色
   */
  backgroundImage?: string;
  /**
   * 画布背景透明度。默认：1
   */
  backgroundAlpha?: number;
  /**
   * 画布是否可缩放。默认：true
   */
  scalable?: boolean;
  /**
   * 画布是否可拖拽。默认：true
   */
  draggable?: boolean;
  /**
   * 点击场景未选中任何目标的回调
   */
  onClickNothing?: (event: SceneEvent) => void;
  /**
   * 处理本场景的 commandMgr
   */
  commandMgr?: CommandManager;
  /** 相机的初始位置 */
  cameraPosition?: Vec3 | Vec2;
  /** 坐标系类型 */
  coordinateType?: 'top' | 'front' | 'left';
  /** resizeTo 适配的 dom id 或元素引用 */
  resizeTo?: string | Window;
  /** 分辨率 */
  resolution?: number;
  /** 保留绘制缓存数据，用来截图 */
  preserveDrawingBuffer?: boolean;
  /** 颜色输出模式 renderer.outputColorSpace */
  outputColorSpace?: string;
  /** 最大帧率限制 */
  maxFPS?: number;
  /** 禁用 resize */
  disableResize?: boolean;
  /** 渲染标志（用来打开或关闭渲染 ticker，若为 false，则当前帧不渲染） */
  renderFlag?: boolean;
  /** 场景初始化完成的回调 */
  initialized?: (sceneTool: SceneTool) => void;
  /** 场景缩放系数限制（[0.1, 2]） */
  zoomRange?: number[];
}

export interface ViewInfo {
  position: Vec2 | Vec3;
  scale: Vec2 | Vec3;
  visible: boolean;
  width?: number;
  height?: number;
}

export interface SceneContext {
  getCommandManager: () => CommandManager | undefined;
  getSceneTools: () => SceneTool;
}

export abstract class BaseScene<
  ApplicationContext,
  Renderer,
  Scene,
  Camera,
  Raycaster,
  Container,
  DisplayObject,
  Viewport
> extends Component<BaseSceneProps> {
  /** 默认背景色 */
  static BACKGROUND_COLOR = 0xffffff;
  /** 默认背景透明度 */
  static BACKGROUND_ALPHA = 1;
  /** 默认画布宽度 */
  static DEFAULT_WIDTH = 600;
  /** 默认画布高度 */
  static DEFAULT_HEIGHT = 400;
  // static DEFAULT_SCALE_NUM = 0.01;
  static SCALE_BIGGER = 1.1;
  static SCALE_SMALLER = 1 / 1.1;
  /** 挂载点与应用上下文的映射关系 */
  static appMap = new Map<string, any>();
  /** 应用上下文与不同视角交互控制器的映射关系 */
  static interactiveMap = new Map<any, IInteractiveControllerInfo<any, any, any>>();
  /** 应用上下文是否已经挂载的状态 */
  static appMountedStatus = new Map<string, boolean>();
  /** 模型实际容器 */
  view: Container;
  /** 默认的场景视图类型标识 */
  abstract defaultSceneViewType: symbol;
  /** 模型视口容器（有多视图的时候才有） */
  viewport?: Viewport;
  /** 场景类型，标识是 2d 还是 3d 场景 */
  abstract sceneType: SceneType;
  /** 场景实例，仅 3d 下有 */
  scene?: Scene;
  /** 相机实例，仅 3d 下有 */
  camera?: Camera;
  /** 射线实例，仅 3d 下有 */
  raycaster?: Raycaster;

  tickers: Array<(deltaTime: number) => void> = [];

  width: number = BaseScene.DEFAULT_WIDTH;

  height: number = BaseScene.DEFAULT_HEIGHT;

  coordinate: CoordinateController;

  mountTimer: number;

  sceneContext: SceneContext;

  resolution: number;

  backgroundImage: DisplayObject;

  maxFPS: number;

  renderFlag = true;

  constructor(props: Exclude<ComponentProps<BaseSceneProps>, ViewEntity>) {
    super(props);
    this.maxFPS = this.props.maxFPS || 60;
    if (this.props.viewport) {
      this.resolution = this.props.viewport.resolution || getContextParam('devicePixelRatio');
    } else {
      this.resolution = this.props.resolution || getContextParam('devicePixelRatio');
    }
    this.updateRenderFlag(this.props.renderFlag);
    if (!BaseScene.appMap.has(this.props.container)) {
      const app = this.createApp();
      // 关闭默认交互
      this.destroyRendererInteraction(app);
      // 缓存，挂载到同一元素的使用一个 gl 上下文
      BaseScene.appMap.set(this.props.container, app);
    }
    this.view = this.createView();
    // 初始化坐标系系统
    const currentApp = this.getCurrentApp();
    if (currentApp) {
      this.coordinate = this.createCoordinateController(currentApp);
    }
    // 初始化交互控制器
    this.initInteractiveController();
    this.sceneContext = {
      getCommandManager: () => this.props.commandMgr,
      getSceneTools: this.getSceneTools,
    };
  }

  componentDidMount() {
    this.loadCanvas();
  }

  componentDidUpdate() {
    const { viewport, disableResize = false, renderFlag = true } = this.props;
    const app = this.getCurrentApp();
    if (!app) {
      return;
    }
    this.updateRenderFlag(renderFlag);
    this.setBackGroundImage(app);
    if (this.sceneType === SceneType.Scene3D) {
      this.updateCameraInfo();
    }
    if (!viewport) {
      this.resolution = this.props.resolution || getContextParam('devicePixelRatio');
      this.updateResolution(app);
      !disableResize && this.resizeStageByCanvas(app);
      return;
    }
    const ctrl = this.getCurrentInteractiveController();
    if (ctrl) {
      ctrl.updateViewportInfo(viewport);
    }
    this.resolution = viewport.resolution || getContextParam('devicePixelRatio');
    this.updateResolution(app);
    !disableResize && this.resizeStageByCanvas(app);
    // 重新计算 viewport
    this.computeViewport();
  }

  /**
   * 卸载画布的钩子（主要做一些清理回收工作，浏览器环境下无需另外实现）
   */
  abstract unmountCanvas(): void;

  componentWillUnmount() {
    if (getContextParam('$$turbox_hot')) {
      const app = this.getCurrentApp();
      if (app) {
        this.removeAppChildrenView(app);
      }
      return;
    }
    // 取消事件监听
    const ctrl = this.getCurrentInteractiveController();
    if (ctrl) {
      ctrl.removeAllListener();
    }
    if (BaseScene.appMap.has(this.props.container)) {
      const app = this.getCurrentApp();
      if (app) {
        this.destroyApp(app);
      }
      BaseScene.appMap.delete(this.props.container);
      BaseScene.appMountedStatus.delete(this.props.container);
    }
    if (getContextEnv() === 'browser') {
      this.mountTimer && cancelAnimationFrame(this.mountTimer);
      window.removeEventListener('resize', this.resizeHandler, false);
    } else {
      // vm 环境
      this.unmountCanvas();
    }
  }

  /** 移除应用子视图 */
  abstract removeAppChildrenView(app: ApplicationContext): void;

  /** 销毁 gl app 及相关清理工作的实现 */
  abstract destroyApp(app: ApplicationContext): void;

  /** 创建 gl app instance，并返回 */
  abstract createApp(): ApplicationContext;

  /** 创建根视图，给 view 设值 */
  abstract createView(): Container;

  /** 获取根视图的基本信息（position、scale、visible） */
  abstract getViewInfo(): ViewInfo;

  /** 添加子视图的接口 */
  abstract addChildView(view: DisplayObject): void;

  /** 关闭渲染器的默认交互 */
  abstract destroyRendererInteraction(app: ApplicationContext): void;

  abstract getCanvasView(app: ApplicationContext): ICanvasInfo<Renderer>;

  /** 获取 hitTarget 的原始实现方法 */
  abstract getHitTargetOriginal(): (
    point: Vec2,
    container: Container,
    configMap: Map<DisplayObject, InteractiveConfig>,
    interactiveType: InteractiveType
  ) => {
    /** event 的直接对象 */
    originalTarget?: DisplayObject;
    /** originalTarget 或 originalTarget 的祖先中的第一个可被交互的元素 */
    target?: DisplayObject;
    /** 选中对象的具体场景鼠标位置 */
    originalTargetPoint?: Vec2 | Vec3;
  };

  /** 更新分辨率 */
  abstract updateResolution(app: ApplicationContext): void;

  private initInteractiveController() {
    const app = this.getCurrentApp();
    if (!app) {
      return;
    }
    const interactiveController = InteractiveController.create<Container, DisplayObject, Renderer>({
      renderer: this.getCanvasView(app).renderer,
      container: this.view,
      viewport: this.props.viewport,
      coordinateType: this.props.coordinateType,
      canvasHandler: {
        onClick: this.onClick,
        onDBClick: this.onDBClick,
        onRightClick: this.onRightClick,
        onDragStart: this.onDragStart,
        onDragMove: this.onDragMove,
        onDragEnd: this.onDragEnd,
        onPinchStart: this.onPinchStart,
        onPinch: this.onPinch,
        onPinchEnd: this.onPinchEnd,
        onRotateStart: this.onRotateStart,
        onRotate: this.onRotate,
        onRotateEnd: this.onRotateEnd,
        onPress: this.onPress,
        onPressUp: this.onPressUp,
        onPointerMove: this.onPointerMove,
        onPointerUp: this.onPointerUp,
        onWheel: this.onWheel,
      },
      getCoordinateCtrl: this.getCoordinateCtrl,
      getHitTargetOriginal: this.getHitTargetOriginal(),
      maxFPS: this.maxFPS,
    });
    const obj = BaseScene.interactiveMap.get(app);
    if (!obj) {
      BaseScene.interactiveMap.set(app, {
        [this.props.id]: interactiveController,
      });
    } else {
      const ic = obj[this.props.id];
      ic && ic.removeAllListener();
      obj[this.props.id] = interactiveController;
    }
  }

  /** 更新渲染状态 */
  private updateRenderFlag(flag = true) {
    this.renderFlag = flag;
  }

  private getCoordinateCtrl = () => this.coordinate;

  private getViewEntity() {
    return {
      id: this.props.id,
      type: this.props.type || this.defaultSceneViewType,
    };
  }

  getCurrentInteractiveController = () => {
    const app = this.getCurrentApp();
    if (!app) {
      return;
    }
    const obj = BaseScene.interactiveMap.get(app);
    if (!obj) {
      return;
    }
    return obj[this.props.id];
  };

  getCurrentApp = () => BaseScene.appMap.get(this.props.container) as ApplicationContext | undefined;

  private updateCursorHandler = (cursor = 'inherit') => {
    const app = this.getCurrentApp();
    if (!app) {
      return;
    }
    this.updateCursor(app, cursor);
  };

  abstract updateCursor(app: ApplicationContext, cursor: string): void;

  private getSceneTools = () => {
    const ctrl = this.getCurrentInteractiveController();
    return {
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      updateInteractiveObject: ctrl ? ctrl.updateInteractiveObject : () => {},
      updateCursor: this.updateCursorHandler,
      hitTarget: ctrl ? ctrl.hitTarget : ((() => {}) as (point: Vec2) => undefined),
      coordinateTransform: this.coordinateTransform,
      getCamera: () => this.camera,
      getRaycaster: () => this.raycaster,
      getScene: () => this.scene,
      getRootView: () => this.view,
      getScreenShot: (
        sx?: number,
        sy?: number,
        w?: number,
        h?: number,
        fileType?: string,
        quality?: number,
        isBase64?: boolean
      ) => this.getScreenShot(sx, sy, w, h, fileType, quality, isBase64),
      getApp: () => this.getCurrentApp(),
      addTicker: (ticker: (deltaTime: number) => void) => {
        this.tickers.push(ticker);
      },
      removeTicker: (ticker: (deltaTime: number) => void) => {
        remove(this.tickers, ticker);
      },
    };
  };

  /**
   * 坐标转化
   */
  private coordinateTransform = (point: Vec2, type: CoordinateType, z?: number) => (this.coordinate ? this.coordinate.transform(point, type, z) : { x: 0, y: 0 });

  /**
   * 将渲染器/画布挂载到视图布局上（浏览器环境下无需另外实现）
   */
  abstract mountCanvas(renderer: Renderer): void;

  private loadCanvas() {
    const app = this.getCurrentApp();
    if (!app) {
      return;
    }
    const renderer = this.getCanvasView(app).renderer;
    if (getContextEnv() === 'browser') {
      const { container } = this.props;
      const el = document.getElementById(container);
      // 如果容器 DOM 没有准备好，留到下一帧再检测
      if (!el) {
        this.mountTimer = requestAnimationFrame(this.loadCanvas);
        return;
      }
      // 挂载
      if (!BaseScene.appMountedStatus.has(container)) {
        BaseScene.appMountedStatus.set(container, true);
        if (getContextParam('$$turbox_hot')) {
          el.innerHTML = '';
        }
        el.appendChild(renderer as unknown as HTMLCanvasElement);
      }
      window.addEventListener('resize', this.resizeHandler, false);
    } else {
      // vm 环境
      const { container } = this.props;
      if (!BaseScene.appMountedStatus.has(container)) {
        BaseScene.appMountedStatus.set(container, true);
        this.mountCanvas(renderer);
      }
    }
    // 根据容器大小确定画布大小
    this.resizeStageByCanvas(app);
    // 初始化背景图
    this.setBackGroundImage(app);
    if (this.sceneType === SceneType.Scene3D) {
      // 更新相机信息
      this.updateCameraInfo();
      // 初始化天空盒
      this.initSkyBox(app);
    }
    // 初始化模型容器
    this.addRootViewContainer(app);
    // 开始监听画布上的交互
    const ctrl = this.getCurrentInteractiveController();
    ctrl && ctrl.startListener();
    this.props.initialized && this.props.initialized(this.getSceneTools());
  }

  private updateCameraInfo() {
    const { cameraPosition } = this.props;
    this.updateCameraPosition({
      x: cameraPosition?.x || 0,
      y: cameraPosition?.y || 0,
      z: (cameraPosition as Vec3)?.z || 0,
    });
  }

  /** 更新相机位置 */
  abstract updateCameraPosition(position: Vec3): void;

  private resizeHandler = () => {
    const { disableResize = false } = this.props;
    const app = this.getCurrentApp();
    if (!app) {
      return;
    }
    !disableResize && this.resizeStageByCanvas(app);
  };

  /** 初始化坐标系控制系统 */
  abstract createCoordinateController(app: ApplicationContext): CoordinateController;

  private resizeStageByCanvas(app: ApplicationContext) {
    const { viewport, coordinateType } = this.props;
    if (viewport) {
      this.resizeViewport(app);
    }
    this.resizeStage(app);
    this.width = this.getCanvasView(app).width / this.resolution;
    this.height = this.getCanvasView(app).height / this.resolution;
    if (this.sceneType === SceneType.Scene2D && coordinateType) {
      this.compute2dOriginView();
    }
  }

  /** 初始化天空盒 */
  abstract initSkyBox(app: ApplicationContext): void;

  /**
   * resize 场景大小
   */
  abstract resizeStage(app: ApplicationContext): void;

  /** 设置背景图 */
  abstract setBackGroundImage(app: ApplicationContext): void;

  /**
   * 计算2d原始视图的缩放和位置
   */
  private compute2dOriginView = () => {
    const { camera2dSize, cameraPosition, coordinateType, viewport } = this.props;
    const width = viewport ? viewport.width : this.width;
    const height = viewport ? viewport.height : this.height;
    const rangeW = (camera2dSize && camera2dSize.x) || width;
    const rangeH = (camera2dSize && camera2dSize.y) || height;
    // 根据相机范围计算模型容器的缩放比例
    // 取二者中较小值，保证可以完整的展示场景
    const scale = Math.min(width / rangeW, height / rangeH);
    const centerX = width / 2;
    const centerY = height / 2;
    const pX = cameraPosition?.x === void 0 ? centerX : cameraPosition.x;
    let pY: number;
    if (cameraPosition?.y === void 0) {
      pY = centerY;
    } else if (coordinateType === 'front' || coordinateType === 'left') {
      pY = -cameraPosition.y;
    } else {
      pY = cameraPosition.y;
    }
    // 模型世界坐标以画布中心为原点
    const x = centerX - pX * scale;
    const y = centerY - pY * scale;
    if (viewport) {
      this.setViewVisible(viewport.visible);
    }
    this.setViewPosition({ x, y });
    if (coordinateType === 'front' || coordinateType === 'left') {
      this.setViewScale({ x: scale, y: -scale });
    } else {
      this.setViewScale({ x: scale, y: scale });
    }
  };

  /** 计算视口的缩放和位置、可见性 */
  private computeViewport() {
    const { viewport } = this.props;
    if (!viewport) {
      return;
    }
    // resize 提高性能做法：放大对应视口的精灵，先让显示逻辑正确
    const viewportInfo = this.getViewportInfo();
    if (
      viewportInfo &&
      viewportInfo.width &&
      viewportInfo.width > 0 &&
      viewportInfo.height &&
      viewportInfo.height > 0
    ) {
      const ratio = Math.min(viewport.width / viewportInfo.width, viewport.height / viewportInfo.height);
      const scale = viewportInfo!.scale;
      this.setViewportScale({ x: scale.x * ratio, y: scale.y * ratio });
    }
    // 重新计算对应视口的精灵
    this.setViewportPosition({ x: viewport.x, y: viewport.y });
    this.setViewportVisible(viewport.visible);
  }

  /** resize 视口 */
  abstract resizeViewport(app: ApplicationContext): void;

  /** 设置根视图的位置 */
  abstract setViewPosition(position: Partial<Vec3>): void;

  /** 设置根视图的缩放 */
  abstract setViewScale(scale: Partial<Vec3>): void;

  /** 设置根视图是否可见 */
  abstract setViewVisible(visible: boolean): void;

  /** 添加根视图容器到 app stage 的实现 */
  abstract addRootViewContainer(app: ApplicationContext): void;

  onClick = (event: SceneEvent) => {
    const { onClickNothing, commandMgr } = this.props;
    if (onClickNothing) {
      onClickNothing(event);
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onClick, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onDBClick = (event: SceneEvent) => {
    const { onClickNothing, commandMgr } = this.props;
    if (onClickNothing) {
      onClickNothing(event);
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onDBClick, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onRightClick = (event: SceneEvent) => {
    const { onClickNothing, commandMgr } = this.props;
    if (onClickNothing) {
      onClickNothing(event);
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onRightClick, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onDragStart = (event: SceneEvent) => {
    const { draggable = true, commandMgr } = this.props;
    if (draggable) {
      this.canvasDragImpl(event, 'start');
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onDragStart, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onDragMove = (event: SceneEvent) => {
    const { draggable = true, commandMgr } = this.props;
    if (draggable) {
      this.canvasDragImpl(event, 'move');
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onDragMove, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onDragEnd = (event: SceneEvent) => {
    const { draggable = true, commandMgr } = this.props;
    if (draggable) {
      this.canvasDragImpl(event, 'end');
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onDragEnd, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPinchStart = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onPinchStart, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPinch = (event: SceneEvent) => {
    const { scalable = true, commandMgr } = this.props;
    if (scalable) {
      this.canvasScaleImpl(event);
    }
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onPinch, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPinchEnd = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onPinchEnd, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onRotateStart = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onRotateStart, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onRotate = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onRotate, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onRotateEnd = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onRotateEnd, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPress = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onPress, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPressUp = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onPressUp, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPointerMove = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onCarriageMove, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onPointerUp = (event: SceneEvent) => {
    const { commandMgr } = this.props;
    if (commandMgr) {
      commandMgr.distributeEvent(EventType.onCarriageEnd, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  onWheel = (event: SceneEvent) => {
    const { scalable = true, commandMgr } = this.props;
    if (scalable) {
      this.canvasScaleImpl(event);
    }
    const ctrl = this.getCurrentInteractiveController();
    if (commandMgr && ctrl) {
      commandMgr.distributeEvent(EventType.onWheel, this.getViewEntity(), event, this.getSceneTools());
    }
  };

  /** 画布拖拽功能的实现，注意：如果已经引入其他交互插件则无需重复实现，避免实现上的冲突 */
  abstract canvasDragImpl(event: SceneEvent, type: 'start' | 'move' | 'end'): void;

  /** 画布缩放功能的实现，注意：如果已经引入其他交互插件则无需重复实现，避免实现上的冲突 */
  abstract canvasScaleImpl(event: SceneEvent): void;

  /** 获取视口的实现 */
  abstract getViewport(): Viewport | undefined;

  /** 获取视口基础信息的实现 */
  abstract getViewportInfo(): ViewInfo | undefined;

  /** 初始化视口，给 viewport 设值 */
  abstract createViewport(): void;

  /** 设置视口的位置 */
  abstract setViewportPosition(position: Partial<Vec3>): void;

  /** 设置视口的缩放 */
  abstract setViewportScale(scale: Partial<Vec3>): void;

  /** 设置视口是否可见 */
  abstract setViewportVisible(visible: boolean): void;

  /** 获取截图 */
  abstract getScreenShot(
    sx?: number,
    sy?: number,
    w?: number,
    h?: number,
    fileType?: string,
    quality?: number,
    isBase64?: boolean
  ): Promise<string | Blob>;
}
