import { RectF } from 'foxit_rdk';
import { ASPropName } from '../../common/Constants';
import { FSDialogController } from '../../viewModel/FSDialogController';
import { FSGuideMaskModel } from '../../viewModel/FSGuideMaskModel';
import { FSGuidePageModel } from '../../viewModel/FSGuidePageModel';
import { AppUtil } from '../../utils/AppUtil';
import { FSWindow } from '../../utils/FSWindowAdapter';
import window from '@ohos.window';

const DLG_GUIDE_WIDTH = 233;
const DLG_GUIDE_HEIGHT = 124;
const DLG_GUIDE_PADDING = 8;

@Component
export struct FSGuideMaskView {
  private renderingSetting: RenderingContextSettings = new RenderingContextSettings(true);
  private context2d: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderingSetting);
  private guideMaskModel: FSGuideMaskModel = new FSGuideMaskModel();
  //
  @State @Watch('onPageIndexChange') curGuidePageIndex: number = 0
  @State curGuidePage: FSGuidePageModel | undefined = undefined;
  @State authorRect: RectF | null = null;
  @State showPosition: Position = { x: 0, y: 0 }
  @StorageProp(ASPropName.SP_CUTOUT_MARGIN) @Watch('onCutoutAvoidAreaChange') cutoutAvoidMargin: Margin = { };
  //
  private fsDialogControl?: FSDialogController<FSGuideMaskModel>;
  private windowRectChangeListener?: FSWindow.IWindowRectChangeListener;

  aboutToAppear(): void {
    this.initData();
    this.initListener();
  }

  aboutToDisappear(): void {
    FSWindow.Adapter.getInstance().unregisterWindowRectChangeListener(this.windowRectChangeListener);
  }

  private initData(): void {
    this.fsDialogControl = AppStorage.get(ASPropName.SP_DIALOG_CONTROL);

    const data = this.fsDialogControl?.data;
    if (data != undefined) {
      this.guideMaskModel = data;
    }
    this.curGuidePageIndex = this.guideMaskModel.guidePageIndex;
    this.curGuidePage = this.guideMaskModel.guidePages[this.guideMaskModel.guidePageIndex];

    this.authorRect = AppUtil.getComponentRectF(this.curGuidePage.parentId, this.curGuidePage.id);
  }

  private onCutoutAvoidAreaChange():void{
    this.postInvalidate();
  }

  private initListener(): void {
    this.windowRectChangeListener = {
      onWindowRectChange: (changeOptions: window.RectChangeOptions) => {
        this.postInvalidate();
      }
    }
    FSWindow.Adapter.getInstance().registerWindowRectChangeListener(this.windowRectChangeListener);
  }

  private timeOutID: number = -1;

  build() {
    Stack() {
      if (this.authorRect != null) {
        Canvas(this.context2d)
          .onReady(() => {
            this.invalidate();
          })
          .onAreaChange(() => {
            this.postInvalidate();
          })
          .width('100%')
          .height('100%')

        RelativeContainer() {
          Image($r('app.media.ic_public_close'))
            .alignRules({
              right: { anchor: "__container__", align: HorizontalAlign.End },
              top: { anchor: '__container__', align: VerticalAlign.Top },
            })
            .width(24)
            .objectFit(ImageFit.Contain)
            .margin({
              top: $r('app.float.ux_margin_4vp'),
              right: $r('app.float.ux_margin_8vp')
            })
            .id('id_iv_guide_close')
            .onClick(() => {
              this.fsDialogControl?.dismiss();
            })

          Text(this.curGuidePage?.title)
            .fontColor($r('app.color.t4'))
            .fontSize($r('app.float.ux_text_size_15fp'))
            .fontWeight(FontWeight.Bold)
            .alignRules({
              left: { anchor: "__container__", align: HorizontalAlign.Start },
              top: { anchor: '__container__', align: VerticalAlign.Top },
              right: { anchor: 'id_iv_guide_close', align: HorizontalAlign.Start }
            })
            .padding({
              left: $r('app.float.ux_margin_16vp'),
              top: $r('app.float.ux_margin_16vp'),
              right: $r('app.float.ux_margin_16vp')
            })
            .id('id_tv_guide_title')

          Text(this.curGuidePage?.desc)
            .fontColor($r('app.color.t4'))
            .fontSize($r('app.float.ux_text_size_14fp'))
            .alignRules({
              top: { anchor: 'id_tv_guide_title', align: VerticalAlign.Bottom },
              left: { anchor: "id_tv_guide_title", align: HorizontalAlign.Start },
            })
            .padding({
              left: $r('app.float.ux_margin_16vp'),
              top: $r('app.float.ux_margin_8vp'),
              right: $r('app.float.ux_margin_16vp'),
              bottom: $r('app.float.ux_margin_12vp'),
            })
            .id('id_tv_guide_desc')

          Button(this.isLastGuidePage() ? $r('app.string.fx_string_done') : $r('app.string.fx_string_next'))
            .buttonStyle(ButtonStyleMode.TEXTUAL)
            .fontColor($r('app.color.theme'))
            .alignRules({
              bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
              right: { anchor: '__container__', align: HorizontalAlign.End },
            })
            .margin({
              right: $r('app.float.ux_margin_4vp'),
              bottom: $r('app.float.ux_margin_4vp'),
            })
            .id('id_guide_next_btn')
            .fontSize($r('app.float.ux_text_size_15fp'))
            .onClick(() => {
              if (this.isLastGuidePage()) {
                this.fsDialogControl?.dismiss();
              } else {
                this.curGuidePageIndex++;
              }
            })
        }
        .width(233)
        .height(124)
        .position({ x: this.showPosition.x, y: this.showPosition.y })
        .borderRadius($r('app.float.fs_border_radius_4'))
        .backgroundColor($r('app.color.white'))
      }
    }
    .width('100%')
    .height('100%')
  }

  private onPageIndexChange(): void {
    this.invalidate();
  }

  private postInvalidate(): void {
    if (this.timeOutID != -1) {
      this.timeOutID = -1;
      clearTimeout(this.timeOutID);
    }
    this.timeOutID = setTimeout(() => {
      this.timeOutID = -1;
      this.invalidate();
    }, 300)
  }

  private invalidate(): void {
    this.curGuidePage = this.guideMaskModel.guidePages[this.curGuidePageIndex];
    this.authorRect = AppUtil.getComponentRectF(this.curGuidePage.parentId, this.curGuidePage.id);
    this.calculatePosition();
    this.drawMask();
  }

  private drawMask(): void {
    this.context2d.clearRect(0, 0, this.context2d.width, this.context2d.height);

    this.context2d.fillStyle = this.guideMaskModel.backgroundColor;
    this.context2d.fillRect(0, 0, this.context2d.width, this.context2d.height);
    this.context2d.globalCompositeOperation = "destination-out";

    this.context2d.fillStyle = Color.White;
    this.drawHighlightArea();

    this.context2d.globalCompositeOperation = "source-over";
  }

  private drawHighlightArea(): void {
    if (this.authorRect != null) {
      this.context2d.beginPath();
      const centerX = this.authorRect.centerX();
      const centerY = this.authorRect.centerY();
      this.context2d.moveTo(centerX, centerY);
      let radius: number;
      if (this.curGuidePage && this.curGuidePage.authorItemSize != undefined) {
        radius = Math.min(this.curGuidePage.authorItemSize.width / 2, this.curGuidePage.authorItemSize.height / 2);
      } else {
        radius = Math.min(this.authorRect.width() / 2, this.authorRect.height() / 2);
      }
      this.context2d.arc(centerX, centerY, radius, 0, 360, false);
      this.context2d.closePath();
      this.context2d.fill();
    }
  }

  private isLastGuidePage(): boolean {
    return this.curGuidePageIndex == this.guideMaskModel.guidePages.length - 1;
  }

  private calculatePosition(): void {
    if (this.authorRect == null) {
      return
    }

    const dw = DLG_GUIDE_WIDTH;
    const dh = DLG_GUIDE_HEIGHT;
    const padding = DLG_GUIDE_PADDING;
    const rootWidth = this.context2d.width;
    const rootHeight = this.context2d.height;

    let radius: number;
    if (this.curGuidePage && this.curGuidePage.authorItemSize != undefined) {
      radius = Math.min(this.curGuidePage.authorItemSize.width / 2, this.curGuidePage.authorItemSize.height / 2);
    } else {
      radius = Math.min(this.authorRect.width() / 2, this.authorRect.height() / 2);
    }
    let top = this.authorRect.centerY() - radius;
    let bottom = this.authorRect.centerY()  + radius;
    let posX = this.authorRect.centerX() - dw / 2;
    let posY = bottom + padding;

    const canFitRight = this.authorRect.right + dw + padding <= rootWidth && this.authorRect.centerY() > dh / 2 + 20;
    const canFitLeft = this.authorRect.left - dw - padding >= 0 && this.authorRect.centerY() > dh / 2 + 20;
    const canFitBottom = posY + dh + padding <= rootHeight;
    const canFitTop = top - dh - padding >= 0;

    if (canFitBottom) {
      if (posX < padding || posX + dw > rootWidth) {
        if (canFitRight) {
          posX = this.authorRect.right + padding;
          posY = this.authorRect.centerY() - dh / 2;
        } else if (canFitLeft) {
          posX = this.authorRect.left - dw - padding;
          posY = this.authorRect.centerY() - dh / 2;
        } else {
          posX = this.authorRect.left;
        }
      }
    } else if (canFitTop) {
      posY = top - dh - padding;
      if (posX < padding || posX + dw > rootWidth) {
        if (canFitRight) {
          posX = this.authorRect.right - padding;
          posY = this.authorRect.centerY() - dh / 2;
        } else if (canFitLeft) {
          posX = this.authorRect.left - dw - padding;
          posY = this.authorRect.centerY() - dh / 2;
        } else {
          posX = this.authorRect.left;
        }
      }
    } else {
      if (canFitRight) {
        posX = this.authorRect.right + padding;
        posY = this.authorRect.centerY() - dh / 2;
      } else if (canFitLeft) {
        posX = this.authorRect.left - dw - padding;
        posY = this.authorRect.centerY() - dh / 2;
      }
    }

    posX = Math.max(padding, Math.min(posX, rootWidth - dw - padding));
    posY = Math.max(padding, Math.min(posY, rootHeight - dh - padding));
    this.showPosition = { x: posX, y: posY }
  }
}