import { BrowserWindow } from "electron";
import type Electron from "electron";
import settings from "electron-settings";
import { isRecord } from "../utils";
import { MessageId } from "./types";
import { getDisplay } from "./screen";
import Window, { WindowRectChangedArgs, WindowDisplayChangedArgs } from "./Window";

class WindowBounds {
  public window?: BrowserWindow;
  private timeout?: NodeJS.Timeout;
  private windowClosed = false;

  constructor(readonly windowInstance: Window) {
  }

  init(window: BrowserWindow): void {
    this.window = window;
    this.window.on("resize", () => this.saveWindowBounds());
    this.window.on("move", () => this.saveWindowBounds());
    this.window.once("show", () => this.saveWindowBounds());
  }

  getSize(displayId?: number, rect?: Electron.Rectangle): Electron.Size {
    const display = getDisplay(displayId, rect);
    let r = rect;
    if (!r) {
      const settingsRect = settings.getSync(this.windowInstance.windowId);
      if (isRecord<number>(settingsRect, "number")) {
        const { x, y, width, height } = settingsRect;
        r = { x, y, width, height };
      }
    }
    if (!r) {
      r = { x: 0, y: 0, width: display.size.width * 0.75, height: display.size.height * 0.75 };
    }
    const { width, height } = r;
    const size: Electron.Size = { width, height };
    if (size.width > display.size.width) {
      size.width = display.size.width;
    }
    if (size.height > display.size.height) {
      size.height = display.size.height;
    }
    size.width = Math.trunc(size.width);
    size.height = Math.trunc(size.height);

    return size;
  }

  getPosition(displayId?: number, rect?: Electron.Rectangle): Electron.Point {
    let r = rect;
    if (!r) {
      const settingsRect = settings.getSync(this.windowInstance.windowId);
      if (isRecord<number>(settingsRect, "number")) {
        const { x, y, width, height } = settingsRect;
        r = { x, y, width, height };
      }
    }

    const { width: w, height: h } = this.getSize(displayId, rect);
    if (r && getDisplay(undefined, r).id === displayId) {
      let { x, y } = r;
      const display = getDisplay(displayId, rect);
      const { x: dispX, y: dispY, width, height } = display.bounds;

      if (x < dispX || x > dispX + width - 100) {
        x = dispX + (width - w) / 2;
      }
      if (y < dispY || y > dispY + height - 100) {
        y = dispY + (height - h) / 2;
      }

      return { x: Math.round(x), y: Math.round(y) };
    }

    const display = getDisplay(displayId, rect);
    const { width, height } = display.size;
    const { x, y } = display.bounds;

    return {
      x: Math.round(x + (width - w) / 2),
      y: Math.round(y + (height - h) / 2),
    };
  }

  saveWindowBounds(): void {
    if (this.timeout) {
      clearTimeout(this.timeout);
    }

    const window = this.window;
    if (window) {
      this.timeout = setTimeout(() => {
        if (this.windowClosed) {
          return;
        }
        const rect = window.getBounds();
        if (isRecord<number>(rect, "number")) {
          settings.setSync(this.windowInstance.windowId, rect);
        }

        if (this.windowInstance.mainWindow) {
          const displayId = getDisplay(undefined, rect).id;
          const args: WindowRectChangedArgs = { windowId: this.windowInstance.windowId, displayId, rect };

          this.windowInstance.mainWindow.webContents.send(MessageId.WindowRectChanged, args);

          if (this.windowInstance.displayId !== displayId && !window.isMinimized()) {
            const args: WindowDisplayChangedArgs = { windowId: this.windowInstance.windowId, displayId };
            this.windowInstance.mainWindow.webContents.send(MessageId.WindowDisplayChanged, args);
            this.windowInstance.displayId = displayId;
          }
        }
      }, 1000);
    }
  }

  notifyClosed(): void {
    this.windowClosed = true;
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
  }
}

export default WindowBounds;
