import { Component, ElementRef, Input, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { DatabaseFile } from 'api-types';
import { PostsService } from 'app/posts.services';

@Component({
    selector: 'app-twitch-chat',
    templateUrl: './twitch-chat.component.html',
    styleUrls: ['./twitch-chat.component.scss'],
    standalone: false
})
export class TwitchChatComponent implements OnInit, OnDestroy {

  full_chat = null;
  visible_chat = null;
  chat_response_received = false;
  downloading_chat = false;

  current_chat_index = null;

  CHAT_CHECK_INTERVAL_MS = 200;
  chat_check_interval_obj = null;

  scrollContainer = null;

  @Input() db_file: DatabaseFile = null;
  @Input() sub = null;
  @Input() current_timestamp = null;

  @ViewChild('scrollContainer') scrollRef: ElementRef;
  @ViewChildren('chat') chat: QueryList<any>;

  constructor(private postsService: PostsService) { }

  ngOnInit(): void {
    this.getFullChat();
  }

  ngOnDestroy(): void {
    if (this.chat_check_interval_obj) { clearInterval(this.chat_check_interval_obj); }
  }

  private isUserNearBottom(): boolean {
    const threshold = 150;
    const position = this.scrollContainer.scrollTop + this.scrollContainer.offsetHeight;
    const height = this.scrollContainer.scrollHeight;
    return position > height - threshold;
  }

  scrollToBottom = (force_scroll = false) => {
    if (force_scroll || this.isUserNearBottom()) {
      this.scrollContainer.scrollTop = this.scrollContainer.scrollHeight;
    }
  }

  addNewChatMessages() {
    const next_chat_index = this.getIndexOfNextChat();
    if (!this.scrollContainer) {
      this.scrollContainer = this.scrollRef.nativeElement;
    }
    if (this.current_chat_index === null) {
      this.current_chat_index = next_chat_index;
    }

    if (Math.abs(next_chat_index - this.current_chat_index) > 25) {
      this.visible_chat = [];
      this.current_chat_index = next_chat_index - 25;
      setTimeout(() => this.scrollToBottom(true), 100);
    }

    const latest_chat_timestamp = this.visible_chat.length ? this.visible_chat[this.visible_chat.length - 1]['timestamp'] : 0;

    for (let i = this.current_chat_index + 1; i < this.full_chat.length; i++) {
      if (this.full_chat[i]['timestamp'] >= latest_chat_timestamp && this.full_chat[i]['timestamp'] <= this.current_timestamp) {
        this.visible_chat.push(this.full_chat[i]);
        this.current_chat_index = i;
      } else if (this.full_chat[i]['timestamp'] > this.current_timestamp) {
        break;
      }
    }
  }

  getIndexOfNextChat() {
    const index = binarySearch(this.full_chat, 'timestamp', this.current_timestamp);
    return index;
  }

  getFullChat() {
    this.postsService.getFullTwitchChat(this.db_file.id, this.db_file.isAudio ? 'audio' : 'video', null, this.sub).subscribe(res => {
      this.chat_response_received = true;
      if (res['chat']) {
        this.initializeChatCheck(res['chat']);
      }
    });
  }

  downloadTwitchChat() {
    this.downloading_chat = true;
    let vodId = this.db_file.url.split('videos/').length > 1 && this.db_file.url.split('videos/')[1];
    vodId = vodId.split('?')[0];
    if (!vodId) {
      this.postsService.openSnackBar($localize`VOD url for this video is not supported. VOD ID must be after "twitch.tv/videos/"`);
    }
    this.postsService.downloadTwitchChat(this.db_file.id, this.db_file.isAudio ? 'audio' : 'video', vodId, null, this.sub).subscribe(res => {
      if (res['chat']) {
        this.initializeChatCheck(res['chat']);
      } else {
        this.downloading_chat = false;
        this.postsService.openSnackBar($localize`Download failed.`)
      }
    }, err => {
      this.downloading_chat = false;
      this.postsService.openSnackBar($localize`Chat could not be downloaded.`)
    });
  }

  initializeChatCheck(full_chat) {
    this.full_chat = full_chat;
    this.visible_chat = [];
    this.chat_check_interval_obj = setInterval(() => this.addNewChatMessages(), this.CHAT_CHECK_INTERVAL_MS);
  }

}

function binarySearch(arr, key, n) {
  let min = 0;
  let max = arr.length - 1;
  let mid;
  while (min <= max) {
    // tslint:disable-next-line: no-bitwise
    mid = (min + max) >>> 1;
    if (arr[mid][key] === n) {
      return mid;
    } else if (arr[mid][key] < n) {
      min = mid + 1;
    } else {
      max = mid - 1;
    }
  }

  return min;
}
