import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { trigger, state, style, transition, animate, AnimationEvent } from '@angular/animations';
import { NgModule, Component, ElementRef, OnDestroy, Input, Output, EventEmitter, Renderer2, ViewChild, inject } from '@angular/core';

import { DomHandler } from '../domhandler';
import { MenuItemComponent } from './menuitem.component';

@Component({
  standalone: true,
  selector: 'menu-item-content,[menuItemContent]',
  template: `
        @if (!item.routerLink) {
          <a [attr.href]="item.url||null" class="ui-menuitem-link ui-corner-all" [attr.data-automationid]="item.automationId" [attr.target]="item.target" [attr.title]="item.title" [attr.id]="item.id"
            [ngClass]="{'ui-state-disabled':item.disabled}" (click)="menu.itemClick($event, item)">
            @if (item.icon) {
              <span class="ui-menuitem-icon" [ngClass]="item.icon"></span>
            }
            <span class="ui-menuitem-text">{{item.label}}</span>
          </a>
        }
        @if (item.routerLink) {
          <a [routerLink]="item.routerLink" [attr.data-automationid]="item.automationId" [queryParams]="item.queryParams" [routerLinkActive]="'ui-state-active'"
            [routerLinkActiveOptions]="item.routerLinkActiveOptions||{exact:false}" class="ui-menuitem-link ui-corner-all" [attr.target]="item.target" [attr.id]="item.id"
            [attr.title]="item.title" [ngClass]="{'ui-state-disabled':item.disabled}" (click)="menu.itemClick($event, item)">
            @if (item.icon) {
              <span class="ui-menuitem-icon" [ngClass]="item.icon"></span>
            }
            <span class="ui-menuitem-text">{{item.label}}</span>
          </a>
        }
        `
})
export class MenuItemContentComponent
{
  @Input() item: MenuItemComponent;
  menu: MenuComponent;

  constructor()
  {
    const menu = inject(MenuComponent);
    this.menu = menu;
  }
}

@Component({
  standalone: true,
  selector: 'p-menu',
  template: `
        @if (!popup || visible) {
          <div #container [ngClass]="{'ui-menu ui-widget ui-widget-content ui-corner-all': true, 'ui-menu-dynamic ui-shadow': popup}"
            [class]="styleClass" [ngStyle]="style" (click)="preventDocumentDefault=true"
            [@overlayAnimation]="{value: 'visible', params: {showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions}}" [@.disabled]="popup !== true" (@overlayAnimation.start)="onOverlayAnimationStart($event)">
            <ul>
              @if (hasSubMenu(); as submenu) {
                @for (submenu of model; track submenu) {
                  @if (submenu.separator) {
                    <li class="ui-menu-separator ui-widget-content" [ngClass]="{'ui-helper-hidden': submenu.visible === false}"></li>
                  }
                  @if (!submenu.separator) {
                    <li class="ui-submenu-header ui-widget-header ui-corner-all" [attr.data-automationid]="submenu.automationId" [ngClass]="{'ui-helper-hidden': submenu.visible === false}">{{submenu.label}}</li>
                  }
                  @for (item of submenu.items; track item) {
                    @if (item.separator) {
                      <li class="ui-menu-separator ui-widget-content" [ngClass]="{'ui-helper-hidden': (item.visible === false || submenu.visible === false)}"></li>
                    }
                    @if (!item.separator) {
                      <li class="ui-menuitem ui-widget ui-corner-all" [menuItemContent]="item" [ngClass]="{'ui-helper-hidden': (item.visible === false || submenu.visible === false)}" [ngStyle]="item.style" [class]="item.styleClass"></li>
                    }
                  }
                }
              }
              @if (!hasSubMenu(); as item) {
                @for (item of model; track item) {
                  @if (item.separator) {
                    <li class="ui-menu-separator ui-widget-content" [ngClass]="{'ui-helper-hidden': item.visible === false}"></li>
                  }
                  @if (!item.separator) {
                    <li class="ui-menuitem ui-widget ui-corner-all" [menuItemContent]="item" [ngClass]="{'ui-helper-hidden': item.visible === false}" [ngStyle]="item.style" [class]="item.styleClass"></li>
                  }
                }
              }
            </ul>
          </div>
        }
        `,
  animations: [
    trigger('overlayAnimation', [
      state('void', style({
        transform: 'translateY(5%)',
        opacity: 0
      })),
      state('visible', style({
        transform: 'translateY(0)',
        opacity: 1
      })),
      transition('void => visible', animate('{{showTransitionParams}}')),
      transition('visible => void', animate('{{hideTransitionParams}}'))
    ])
  ]
})
export class MenuComponent implements OnDestroy
{
  el = inject(ElementRef);
  renderer = inject(Renderer2);

  @Input() model: MenuItemComponent[];
  @Input() popup: boolean;
  @Input() style: any;
  @Input() styleClass: string;
  @Input() appendTo: any;
  @Input() autoZIndex: boolean = true;
  @Input() baseZIndex: number = 0;
  @Input() showTransitionOptions: string = '225ms ease-out';
  @Input() hideTransitionOptions: string = '195ms ease-in';
  @ViewChild('container', { static: false }) containerViewChild: ElementRef;
  @Output() showed: EventEmitter<any> = new EventEmitter();
  @Output() hidden: EventEmitter<any> = new EventEmitter();
  container: HTMLDivElement;
  documentClickListener: any;
  documentResizeListener: any;
  preventDocumentDefault: boolean;
  target: any;
  visible: boolean;

  toggle(event: any)
  {
    if (this.visible)
      this.hide();
    else
      this.show(event);

    this.preventDocumentDefault = true;
  }

  show(event: any)
  {
    this.target = event.currentTarget;
    this.visible = true;
    this.preventDocumentDefault = true;
  }

  onOverlayAnimationStart(event: AnimationEvent)
  {
    switch (event.toState)
    {
      case 'visible':
        if (this.popup)
        {
          this.container = event.element;
          this.moveOnTop();
          this.showed.emit({});
          this.appendOverlay();
          DomHandler.absolutePosition(this.container, this.target);
          this.bindDocumentClickListener();
          this.bindDocumentResizeListener();
        }
        break;

      case 'void':
        this.onOverlayHide();
        this.hidden.emit({});
        break;
    }
  }

  appendOverlay()
  {
    if (this.appendTo)
    {
      if (this.appendTo === 'body')
        document.body.appendChild(this.container);
      else
        DomHandler.appendChild(this.container, this.appendTo);
    }
  }

  restoreOverlayAppend()
  {
    if (this.container && this.appendTo)
    {
      this.el.nativeElement.appendChild(this.container);
    }
  }

  moveOnTop()
  {
    if (this.autoZIndex)
    {
      this.container.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
    }
  }

  hide()
  {
    this.visible = false;
  }

  onWindowResize()
  {
    this.hide();
  }

  itemClick(event, item: MenuItemComponent)
  {
    if (item.disabled)
    {
      event.preventDefault();
      return;
    }

    if (!item.url)
    {
      event.preventDefault();
    }

    if (item.command)
    {
      item.command({
        originalEvent: event,
        item: item
      });
    }

    if (this.popup)
    {
      this.hide();
    }
  }

  bindDocumentClickListener()
  {
    if (!this.documentClickListener)
    {
      this.documentClickListener = this.renderer.listen('document', 'click', () =>
      {
        if (!this.preventDocumentDefault)
        {
          this.hide();
        }

        this.preventDocumentDefault = false;
      });
    }
  }

  unbindDocumentClickListener()
  {
    if (this.documentClickListener)
    {
      this.documentClickListener();
      this.documentClickListener = null;
    }
  }

  bindDocumentResizeListener()
  {
    this.documentResizeListener = this.onWindowResize.bind(this);
    window.addEventListener('resize', this.documentResizeListener);
  }

  unbindDocumentResizeListener()
  {
    if (this.documentResizeListener)
    {
      window.removeEventListener('resize', this.documentResizeListener);
      this.documentResizeListener = null;
    }
  }

  onOverlayHide()
  {
    this.unbindDocumentClickListener();
    this.unbindDocumentResizeListener();
    this.preventDocumentDefault = false;
    this.target = null;
  }

  ngOnDestroy()
  {
    if (this.popup)
    {
      this.restoreOverlayAppend();
      this.onOverlayHide();
    }
  }

  hasSubMenu(): boolean
  {
    if (this.model)
    {
      for (let item of this.model)
      {
        if (item.items)
        {
          return true;
        }
      }
    }
    return false;
  }
}

@NgModule({
  imports: [CommonModule, RouterModule],
  exports: [MenuComponent, RouterModule],
  declarations: [MenuComponent, MenuItemContentComponent]
})
export class MenuModule { }
