/*
 * Copyright (c) 2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { UIContext, window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { sensor } from '@kit.SensorServiceKit';
import { WindowInfo } from '../../model/WindowInfo';

const TAG = '[WindowUtil]'

export enum ImmersiveType {
  NORMAL,
  IMMERSIVE,
  FULLSCREEN_IMMERSIVE
}

export class WindowOperationClass {
  public uiContext?: UIContext;
  public mainWindow: window.Window;
  public mainWindowInfo: WindowInfo = new WindowInfo();
  public onStatusTypeChange: (statusType: window.WindowStatusType) => void = (statusType: window.WindowStatusType) => {
    this.mainWindowInfo.windowStatusType = statusType;
  }
  // Callback function after window resize.
  public onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
    this.mainWindowInfo.windowSize = windowSize;
    if (this.uiContext) {
      this.mainWindowInfo.widthBp = this.uiContext.getWindowWidthBreakpoint();
      this.mainWindowInfo.heightBp = this.uiContext.getWindowHeightBreakpoint();
    }
  };
  // [Start avoid_area]
  // Callback when the system safe area insets change.
  public onAvoidAreaChange: (avoidOptions: window.AvoidAreaOptions) => void =
    (avoidOptions: window.AvoidAreaOptions) => {
      if (avoidOptions.type === window.AvoidAreaType.TYPE_SYSTEM) {
        this.mainWindowInfo.AvoidSystem = avoidOptions.area;
      } else if (avoidOptions.type === window.AvoidAreaType.TYPE_CUTOUT) {
        this.mainWindowInfo.AvoidCutout = avoidOptions.area;
      } else if (avoidOptions.type === window.AvoidAreaType.TYPE_SYSTEM_GESTURE) {
        this.mainWindowInfo.AvoidSystemGesture = avoidOptions.area;
      } else if (avoidOptions.type === window.AvoidAreaType.TYPE_KEYBOARD) {
        this.mainWindowInfo.AvoidKeyboard = avoidOptions.area;
      } else if (avoidOptions.type === window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
        this.mainWindowInfo.AvoidNavigationIndicator = avoidOptions.area;
      }
    };
  // [End avoid_area]

  constructor(mainWindow: window.Window) {
    this.mainWindow = mainWindow;
  }

  async setImmersiveType(type: ImmersiveType): Promise<void> {
    if (type === ImmersiveType.NORMAL) {
      this.mainWindow.setWindowLayoutFullScreen(false)
        .then(() => {
          hilog.info(0x0000, TAG, `Succeeded in setting immersive mode.`);
        })
        .catch((err: BusinessError) => {
          hilog.error(0x0000, TAG, `Failed to set immersive mode. Code: ${err.code}, message: ${err.message}`);
        });
      this.setSystemBarEnabled(true);
      // Only used after the function loadContent() or setUIContent().
      try {
        this.mainWindow.setWindowDecorVisible(true);
      } catch (err) {
        hilog.error(0x0000, TAG,
          `Failed to set Window Decor Visible true. Code: ${err.code}, message: ${err.message}`);
      }
      this.recover();
    } else if (type === ImmersiveType.IMMERSIVE) {
      this.mainWindow.setWindowLayoutFullScreen(true)
        .then(() => {
          hilog.info(0x0000, TAG, `Succeeded in setting immersive mode.`);
        })
        .catch((err: BusinessError) => {
          hilog.error(0x0000, TAG, `Failed to set immersive mode. Code: ${err.code}, message: ${err.message}`);
        });
      this.setSystemBarEnabled(true);
      try {
        this.mainWindow.setWindowDecorVisible(true);
      } catch (err) {
        hilog.error(0x0000, TAG,
          `Failed to set Window Decor Visible false. Code: ${err.code}, message: ${err.message}`);
      }
      this.recover();
    } else if (type === ImmersiveType.FULLSCREEN_IMMERSIVE) {
      this.mainWindow.setWindowLayoutFullScreen(true)
        .then(() => {
          hilog.info(0x0000, TAG, `Succeeded in setting immersive mode.`);
        })
        .catch((err: BusinessError) => {
          hilog.error(0x0000, TAG, `Failed to set immersive mode. Code: ${err.code}, message: ${err.message}`);
        });
      try {
        // Maximized state of free window mode on PC devices.
        if (this.mainWindow.getWindowStatus() === window.WindowStatusType.MAXIMIZE ||
          (this.mainWindow.getWindowStatus() === window.WindowStatusType.FLOATING &&
            this.mainWindow.getWindowDecorHeight() !== 0)) {
          this.mainWindow.maximize()
            .then(() => {
              hilog.info(0x0000, TAG, `Succeeded in maximizing the window.`);
            })
            .catch((err: BusinessError) => {
              hilog.error(0x0000, TAG,
                `Failed to maximize the window. Code: ${err.code}, message: ${err.message}`);
            });
        }
      } catch (err) {
        hilog.info(0x0000, TAG, `Failed to get WindowStatus. Code: ${err.code}, message: ${err.message}`);
      }

      this.setSystemBarEnabled(false);
      try {
        this.mainWindow.setWindowDecorVisible(false);
      } catch (err) {
        hilog.error(0x0000, TAG,
          `Failed to set Window Decor Visible false. Code: ${err.code}, message: ${err.message}`);
      }
    }
    this.mainWindowInfo.isImmersive = type;
  }

  setUIContext(): void {
    try {
      this.uiContext = this.mainWindow.getUIContext();
    } catch (err) {
      hilog.info(0x0000, TAG, `Failed to get UIContext. Code: ${err.code}, message: ${err.message}`);
    }
  }

  async setSystemBarEnabled(isVisible: boolean): Promise<void> {
    this.mainWindow.setSpecificSystemBarEnabled('status', isVisible)
      .then(() => {
        hilog.info(0x0000, TAG,
          `Succeeded in setting status bar to be invisible ${isVisible ? 'visible' : 'invisible'}.`);
      })
      .catch((err: BusinessError) => {
        hilog.error(0x0000, TAG,
          `Failed to set status bar to be invisible. Code: ${err.code}, message: ${err.message}`);
      });
    this.mainWindow.setSpecificSystemBarEnabled('navigationIndicator', isVisible)
      .then(() => {
        hilog.info(0x0000, TAG, `Succeeded in setting navigation indicator to be invisible.`);
      })
      .catch((err: BusinessError) => {
        hilog.error(0x0000, TAG,
          `Failed to set navigation indicator to be invisible. Code: ${err.code}, message: ${err.message}`);
      });
  }

  async recover(): Promise<void> {
    try {
      if (this.mainWindow.getWindowStatus() === window.WindowStatusType.FULL_SCREEN) {
        this.mainWindow.recover()
          .then(() => {
            hilog.info(0x0000, TAG, `Succeeded in recovering the window.`);
          })
          .catch((err: BusinessError) => {
            hilog.error(0x0000, TAG, `Failed to recover the window. Code: ${err.code}, message: ${err.message}`);
          });
      }
    } catch (err) {
      hilog.info(0x0000, TAG, `Failed to get WindowStatus. Code: ${err.code}, message: ${err.message}`);
    }

  }

  async setWindowOrientation(orientation: window.Orientation): Promise<void> {
    this.mainWindow.setPreferredOrientation(orientation)
      .then(() => {
        hilog.info(0x0000, TAG, `Succeeded in setting window orientation.`);
        // Update window orientation.
        this.mainWindowInfo.orientation = orientation;
      })
      .catch((err: BusinessError) => {
        hilog.error(0x0000, TAG, `Failed to set window orientation. Code: ${err.code}, message: ${err.message}`);
      });
  }

  updateWindowInfo(): void {
    try {
      // First time get window status.
      this.mainWindowInfo.windowStatusType = this.mainWindow.getWindowStatus();
      this.mainWindow.on('windowStatusChange', this.onStatusTypeChange);
      // First time get window size.
      let width: number = this.mainWindow.getWindowProperties().windowRect.width;
      let height: number = this.mainWindow.getWindowProperties().windowRect.height;
      let windowSize: window.Size = {
        width: width,
        height: height
      }
      this.mainWindowInfo.windowSize = windowSize;
      // First time get width/height breakpoint.
      this.mainWindowInfo.widthBp = this.uiContext!.getWindowWidthBreakpoint();
      this.mainWindowInfo.heightBp = this.uiContext!.getWindowHeightBreakpoint();
      // Register for window size change monitoring, update window size and width/height breakpoint.
      this.mainWindow.on('windowSizeChange', this.onWindowSizeChange);

      // First time get avoid area infos.
      this.mainWindowInfo.AvoidSystem = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
      this.mainWindowInfo.AvoidNavigationIndicator =
        this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
      this.mainWindowInfo.AvoidCutout = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_CUTOUT);
      this.mainWindowInfo.AvoidSystemGesture =
        this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM_GESTURE);
      this.mainWindowInfo.AvoidKeyboard = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_KEYBOARD);
      this.mainWindow.on('avoidAreaChange', this.onAvoidAreaChange);
    } catch (err) {
      hilog.error(0x0000, TAG, `Failed to update WindowInfo. Code: ${err.code}, message: ${err.message}`);
    }
  }

  async release(): Promise<void> {
    try {
      this.mainWindow.off('windowStatusChange');
      this.mainWindow.off('windowSizeChange');
      this.mainWindow.off('avoidAreaChange');
    } catch (err) {
      hilog.error(0x0000, TAG, `Failed to off mainWindow events. Code: ${err.code}, message: ${err.message}`);
    }
    try {
      sensor.off(sensor.SensorId.GRAVITY);
    } catch (err) {
      hilog.error(0x0000, TAG, `Failed to off sensor gravity event. Code: ${err.code}, message: ${err.message}`);
    }
  }
}