import { CommonModule, DOCUMENT } from '@angular/common';
import {
  Component,
  EventEmitter,
  HostBinding,
  Inject,
  Input,
  Output,
} from '@angular/core';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faRectangleXmark } from '@fortawesome/free-regular-svg-icons';
import { WindowConfig } from './window-config';

export enum Resize {
  Top = 't',
  Right = 'r',
  Bottom = 'b',
  Left = 'l',
}

@Component({
  selector: 'fbi-window',
  standalone: true,
  imports: [CommonModule, FontAwesomeModule],
  templateUrl: './window.component.html',
  styleUrls: ['./window.component.scss'],
})
export class WindowComponent {
  @HostBinding('style.height.px') height: number = 200;
  @HostBinding('style.width.px') width: number = 400;
  @HostBinding('style.left.px') x: number = 200;
  @HostBinding('style.top.px') y: number = 200;
  @HostBinding('style.visibility') visibility: string = 'hidden';
  private _display: boolean = false;
  private get visible() {
    return this._display;
  }
  private set visible(value: boolean) {
    this._display = value;
    this.visibility = value ? 'visible' : 'hidden';
  }

  @Input() config!: WindowConfig;
  @Output() closing = new EventEmitter<void>();

  dragging: boolean = false;
  resizing: boolean = false;

  faRectangleXmark = faRectangleXmark;
  Resize = Resize;

  constructor(@Inject(DOCUMENT) private _document: Document) {
    this.visible = true;
  }

  toggle = (display?: boolean) => (this.visible = display ?? !this.visible);
  hide = () => this.toggle(false);
  show = () => this.toggle(true);

  startDrag(event: MouseEvent): void {
    event.preventDefault();
    const { innerHeight, innerWidth } = window;
    const { clientX, clientY } = event;
    const x = this.x;
    const y = this.y;
    const h = this.height;
    const w = this.width;
    const doc = this._document;

    this.dragging = true;
    const dragging = (e: MouseEvent) => {
      this.x = Math.min(Math.max(x + e.clientX - clientX, 0), innerWidth - w);
      this.y = Math.min(Math.max(y + e.clientY - clientY, 0), innerHeight - h);
    };

    const dragEnd = (e: MouseEvent) => {
      this.dragging = false;
      doc.removeEventListener('mousemove', dragging);
      doc.removeEventListener('mouseup', dragEnd);
    };

    doc.addEventListener('mousemove', dragging);
    doc.addEventListener('mouseup', dragEnd);
  }

  startResize(event: MouseEvent, anchor: Resize): void {
    event.preventDefault();
    const { innerHeight, innerWidth } = window;
    const { clientX, clientY } = event;
    const x = this.x;
    const y = this.y;
    const h = this.height;
    const w = this.width;
    const doc = this._document;
    const minH = 32;
    const minW = 100;

    this.resizing = true;
    const resizing = (e: MouseEvent) => {
      const dx = e.clientX - clientX;
      const dy = e.clientY - clientY;

      switch (anchor) {
        case Resize.Top:
          this.y = Math.min(Math.max(y + dy, 0), y + h - minH);
          if (this.y > 0)
            this.height = Math.min(
              Math.max(h - dy, minH),
              innerHeight - this.y
            );
          break;
        case Resize.Bottom:
          this.height = Math.min(Math.max(h + dy, minH), innerHeight - this.y);
          break;
        case Resize.Left:
          this.x = Math.min(Math.max(x + dx, 0), x + w - minW);
          if (this.x > 0)
            this.width = Math.min(Math.max(w - dx, minW), innerWidth - this.x);
          break;
        case Resize.Right:
          this.width = Math.min(Math.max(w + dx, minW), innerWidth - this.x);
          break;
      }
    };

    const resizeEnd = (e: MouseEvent) => {
      this.resizing = false;
      doc.removeEventListener('mousemove', resizing);
      doc.removeEventListener('mouseup', resizeEnd);
    };
    doc.addEventListener('mousemove', resizing);
    doc.addEventListener('mouseup', resizeEnd);
  }

  onClose(event: MouseEvent): void {
    event.preventDefault();
    this.hide();
    this.closing.emit();
  }
}
