import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  ElementRef,
  EventEmitter,
  HostListener,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  Output,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { marker } from '@biesbjerg/ngx-translate-extract-marker';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subscription } from 'rxjs';
import { RouterHandler } from 'src/app/shared/services/router-handler.service';
import { focusChatInput } from '../../focus-chat-input-observer';

@Component({
  selector: 'app-native-editor',
  templateUrl: './native-editor.component.html',
  styleUrls: ['./native-editor.component.scss'],
})
export class NativeEditorComponent implements OnInit, OnDestroy, AfterViewInit, OnChanges {
  /**
   * Initial content of the editor
   */
  @Input() content?: string = '';
  @ViewChild('emojiBtn', { read: ElementRef }) emojiBtn: ElementRef;
  @ViewChild('editor', { read: ElementRef }) editor: ElementRef;

  @Output() readonly changeEvent = new EventEmitter<string>();
  @Output() readonly submitEvent = new EventEmitter<string>();
  @Output() readonly closeEvent = new EventEmitter<boolean>();
  @Output() readonly focusChangeEvent = new EventEmitter<boolean>();

  @Input() autofocus?: boolean = false; // autofocus
  @Input() messageSending: boolean = false;

  public toggledEmojiPopup: boolean = false;
  public messageLengthTooLong: boolean = false;
  public messageMaxLength: number = 1000;

  public value: string = '';

  private focusChatInputSubsctiption: Subscription;

  /**
   * For emoji insert, or style toggle we should know the last selection
   */
  public lastSelection: { start: number; end: number } = {
    start: 0,
    end: 0,
  };

  // on mobile the enter input should line break instead of form submit
  private isOnDesktop: boolean = true;

  public placeholder = marker('Type message here...');

  constructor(
    private ref: ElementRef,
    private deviceService: DeviceDetectorService,
    private routerHandler: RouterHandler,
    private changeRef: ChangeDetectorRef
  ) {
    this.isOnDesktop = this.deviceService.isDesktop();

    if (this.isOnDesktop) {
      this.focusChatInputSubsctiption = focusChatInput.subscribe(() => {
        this.editor.nativeElement.focus();
      });
    }
  }

  public keydown = (e: KeyboardEvent) => {
    if (e.key == 'Escape') {
      this.closeEvent.emit(true);
    } else if (e.key == 'Enter') {
      if (e.altKey || e.ctrlKey || e.shiftKey || !this.isOnDesktop) {
        // just insert enter normally
      } else {
        e.preventDefault(); // prevent inserting enter
        this.submitEvent.emit(this.value);
      }
    }
  };

  ngOnInit(): void {
    this.routerHandler.subscribe('room/?/chat/?', this.routerChange);
  }

  ngAfterViewInit(): void {
    this.editor.nativeElement.addEventListener('focus', () => {
      this.focusChangeEvent.emit(true);
    });
    this.editor.nativeElement.addEventListener('blur', () => {
      this.focusChangeEvent.emit(false);
    });
    this.editor.nativeElement.addEventListener('input', () => {
      this.setInputHeight();
      this.changeEvent.emit(this.value);
    });

    document.addEventListener('selectionchange', this.saveLastSelection);

    if (this.autofocus) {
      this.editor.nativeElement.focus();
    }
    this.changeRef.detectChanges(); // resolve expression changed error
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['content']) {
      if (this.content != undefined && this.content != null && this.value != this.content) {
        this.value = this.content;
        this.setInputHeight();
      }
    }
  }

  public saveLastSelection = (ev) => {
    const activeElement = document.activeElement;

    // Make sure this is your textarea
    if (activeElement == this.editor.nativeElement) {
      this.lastSelection = {
        start: (<HTMLTextAreaElement>activeElement).selectionStart,
        end: (<HTMLTextAreaElement>activeElement).selectionEnd,
      };
    }
  };

  public submit() {
    this.editor.nativeElement.focus();
    this.submitEvent.next(this.value);
  }

  public messageSent(success: boolean) {
    if (success) {
      this.emptyEditor();
      this.setInputHeight();
    }
  }

  public handleEmojiInsert(emoji) {
    this.insertText(emoji);
    this.setInputHeight();
  }

  public onToggleEmojiPopup() {
    this.toggledEmojiPopup = !this.toggledEmojiPopup;
    if (this.toggledEmojiPopup) {
      history.pushState({ emoji: true }, '');
    }
  }

  @HostListener('window:popstate', ['$event'])
  onWindowPopState($event) {
    this.toggledEmojiPopup = false;
  }

  @HostListener('document:click', ['$event'])
  documentClick(event: MouseEvent) {
    var emojiPickerElement = this.ref.nativeElement.querySelector('app-emoji-picker');

    var emojiButtonElement = this.emojiBtn.nativeElement;

    if (!emojiPickerElement.contains(event.target) && !emojiButtonElement.contains(event.target)) {
      this.toggledEmojiPopup = false;
    }
  }

  routerChange = (res) => {
    if (this.autofocus && this.isOnDesktop) {
      this.editor.nativeElement.focus();
    }

    this.emptyEditor();
    this.setInputHeight();
  };

  ngOnDestroy(): void {
    this.routerHandler.unsubscribe('room/?/chat/?', this.routerChange);
    document.removeEventListener('selectionchange', this.saveLastSelection);
    this.focusChatInputSubsctiption && this.focusChatInputSubsctiption.unsubscribe();
  }

  private emptyEditor() {
    this.value = '';
  }

  public change(ev) {
    this.messageLengthTooLong = this.value.length > this.messageMaxLength;
  }

  private insertText(text: string) {
    if (this.lastSelection) {
      this.value =
        this.value.substring(0, this.lastSelection.start) +
        text +
        this.value.substring(this.lastSelection.start);
    } else {
      this.value += text;
    }
    this.lastSelection = {
      start: this.lastSelection.start + text.length,
      end: this.lastSelection.end + text.length,
    };
  }

  private setInputHeight(): void {
    setTimeout(() => {
      this.editor.nativeElement.style.height = '0px';
      this.editor.nativeElement.style.height = this.editor.nativeElement.scrollHeight + 'px';
      console.log('set input height to', this.editor.nativeElement.style.height);
    }, 10);
  }
}
