import {
  Component,
  ElementRef,
  HostListener,
  OnDestroy,
  OnInit,
  QueryList,
  ViewChild,
  ViewChildren,
} from "@angular/core";
import { SafeRadix32 } from "../../../shared/safe-radix32";
import { base64 } from "rfc4648";
import { AccountService } from "src/app/shared/server-services/account.service";
import { AuthService } from "src/app/shared/server-services/auth.service";
import {
  SubscriptionChatEventType,
  ChatService,
} from "src/app/shared/server-services/chat.service";
import { DialogueService } from "src/app/shared/server-services/dialogue.service";
import { ResourceBase } from "src/app/shared/server-services/resource-base";
import { RouterHandler } from "src/app/shared/services/router-handler.service";
import { ResourceType } from "../../resources-sidebar/sidebar-data";
import { DialogService } from "src/app/shared/services/dialog.service";
import { marker } from "@biesbjerg/ngx-translate-extract-marker";
import { MarkdownEditorComponent } from "./chat-message/markdown-editor/markdown-editor.component";
import { AccountAvatarRecord } from "src/app/shared/server-services/query-records/account-records";
import { MessageRecord } from "src/app/shared/server-services/query-records/room-records";
import {
  SubscriptionServiceEvent,
  SubscriptionServiceEventType,
} from "src/app/shared/server-services/subscription-event";
import { SubscriptionService } from "src/app/shared/server-services/subscription.service";
import { RoomService } from "src/app/shared/server-services/room.service";
import { WorkspaceSubscriptionRoomAccountPermissionEventRecord } from "src/app/shared/server-services/query-records/workspace-records";
import { Router } from "@angular/router";
import { MatSnackBar, MatSnackBarRef } from "@angular/material/snack-bar";
import { CopyChatMessagesSnackbarComponent } from "src/app/shared/snackbars/copy-chat-messages-snackbar/copy-chat-messages-snackbar.component";
import { ClipboardService } from "src/app/shared/services/clipboard.service";
import { ChatMessageComponent } from "./chat-message/chat-message.component";
import { DeviceDetectorService } from "ngx-device-detector";
export interface ChatData {
  id: string;
  message: string;
  date: Date;
  account: AccountAvatarRecord;
  edited: boolean;
  error: boolean;
  errorMessage?: string;
}

@Component({
  selector: "app-chat-layout",
  templateUrl: "./chat-layout.component.html",
  styleUrls: ["./chat-layout.component.scss"],
})
export class ChatLayoutComponent implements OnInit, OnDestroy {
  // at 0, is the newest
  public chatMessages: ChatData[] = [];

  public resourceId;
  public loaded = false;
  public isMessagesLoading = false;
  public isMessageSending: boolean = false;
  private type: ResourceType = null;
  private service: ResourceBase = null;
  public chatEndReached: boolean = false;
  public isReadOnly: boolean = false;
  public resourceName: string = "";
  private isBottomVisible: boolean = true;

  public isNewMessageAtBottom: boolean = false;

  private isSelecting: boolean = false;
  private copySnackbarShowing: boolean = false;
  private selectedChatMessages: HTMLElement[] = [];
  private chatTag: string = "APP-CHAT-MESSAGE";
  private copySnackBarRef: MatSnackBarRef<CopyChatMessagesSnackbarComponent>;
  private canEnterEditOnUpArrow: boolean = true;
  private isChatInputFocused: boolean = true;

  @ViewChild("chatScroll", { read: ElementRef }) chatScroll: ElementRef;

  /**
   * If you are at the bottom of the scrollbar, the incoming messages will scroll you to the bottom.
   * But if you out of this scope, we wont scroll you, so you can search and read earlier messages.
   */
  private autoScrollLimit: number = 200;

  @ViewChild(MarkdownEditorComponent)
  markdownEditorComponent: MarkdownEditorComponent;
  @ViewChildren(ChatMessageComponent)
  chatMessagesRef: QueryList<ChatMessageComponent>;
  private openEditor: ChatMessageComponent;

  private checkReadOnly() {
    if (this.authService.isAnonym()) {
      this.isReadOnly = true;
    } else {
      this.roomService
        .getMyRoomPermissions(this.resourceId)
        .then((myPerm) => {
          this.isReadOnly =
            !myPerm.account ||
            !(
              myPerm.ownerAccountId === myPerm.account?.id ||
              (myPerm.msgBrdReadOnly &&
                myPerm.account?.msgBrdModerate === true) ||
              myPerm.msgBrdReadOnly !== true
            );
        })
        .catch(() => {
          this.isReadOnly = true;
        });
    }
  }

  public onRouterChange = (res) => {
    this.isMessagesLoading = true;
    this.chatEndReached = false;

    this.copySnackBarRef?.dismiss();

    if (res.params.type == "private") {
      this.type = ResourceType.DIALOGUE;
      this.service = this.dialogueService;
    } else {
      this.type = ResourceType.ROOM;
      this.service = this.chatService;
    }

    let outerPinnedKey;
    if (this.authService.isAnonym()) {
      let fragment = this.routerHandler.getRoute().fragment?.roomkey;
      if (!fragment || fragment.length == 0) {
        throw "Can not find pinnedKey";
      }

      outerPinnedKey = base64.parse(fragment);
      //console.log('pinnedKey', outerPinnedKey)

      if (this.resourceId) {
        // unsubscribe prev anonym chat changes
        this.chatService.unsubscribeAllChatChanges(this.resourceId);
      }

      if (outerPinnedKey.byteLength <= 0) {
        console.error("Can not find pinnedKey in URL");
      }
    }

    this.resourceId = res.params.id;
    this.chatMessages = [];

    console.log("load messages");

    if (this.type == ResourceType.ROOM) {
      this.checkReadOnly();
    }

    this.service.getData(this.resourceId, outerPinnedKey).then((data) => {
      if (data["data"]) this.resourceName = data["data"].name;
      else if (data["account"]) this.resourceName = data["account"].avatarName;

      console.log("chat data", data);

      // load these messages and subscribe the changes
      let cachedResourceId = this.resourceId;

      this.service.getLastMessages(this.resourceId).then((messages) => {
        console.log("messages", messages);

        if (messages.length == 0) this.chatEndReached = true;

        // if you switch fast between chats you can get an old chat response on an other chat window
        if (cachedResourceId != this.resourceId) return;

        messages.forEach((msg) => {
          this.loadMessage(msg);
        });

        this.accountService.getMe().then((me) => {
          if (this.chatMessages.length > 0 && this.chatMessages[0] !== me.id) {
            !this.authService.isAnonym() && this.sendSeen();
          }
        });

        // we dont need the callback reference, it will be removed after ngOnDestroy
        if (this.authService.isAnonym()) {
          this.chatService.subscribeChatChanges(
            this.resourceId,
            SubscriptionChatEventType.CREATE,
            this.handleMessageCreate.bind(this)
          );
          this.chatService.subscribeChatChanges(
            this.resourceId,
            SubscriptionChatEventType.MODIFY,
            this.handleMessageModify.bind(this)
          );
          this.chatService.subscribeChatChanges(
            this.resourceId,
            SubscriptionChatEventType.DELETE,
            this.handleMessageDelete.bind(this)
          );
          this.chatService.subscribeChatChanges(
            this.resourceId,
            SubscriptionChatEventType.TYPING,
            this.handleMessageTyping.bind(this)
          );
        }

        setTimeout(() => {
          this.isMessagesLoading = false;
        }, 10);
      });
    });
  };

  private handleRoomMessageCreate = (msg) => {
    this.handleMessageCreate(msg);
  };

  private handleDialogueMessageCreate = (msg) => {
    this.handleMessageCreate(msg);
  };

  private handleMessageCreate = (msg) => {
    if (msg.id == this.resourceId) {
      this.loadMessage({
        id: msg.messageId,
        posterId: msg.posterId,
        content: msg.content,
        edited: false,
      });

      if (this.isBottomVisible) {
        this.sendSeen();
      } else if (
        this.chatScroll.nativeElement.scrollTop * -1 <
        this.autoScrollLimit
      ) {
        // scroll to the bottom, if needed
        setTimeout(() => {
          // watch out, the reverse flexbox cause a negative scroll direction
          if (
            this.chatScroll.nativeElement.scrollTop * -1 <
            this.autoScrollLimit
          ) {
            this.chatScroll.nativeElement.scrollTo({
              top: 0,
              left: 0,
              behavior: "smooth",
            });
          }
        }, 1);
      } else {
        this.isNewMessageAtBottom = true;
      }
    }
  };

  public scrollToBottom(){
    this.chatScroll.nativeElement.scrollTo({
      top: 0,
      left: 0,
      behavior: "smooth",
    });
    this.isNewMessageAtBottom = false;
  }

  private handleRoomMessageModify = (msg) => {
    if (this.type == ResourceType.ROOM) {
      this.handleMessageModify(msg);
    }
  };

  private handleDialogueMessageModify = (msg) => {
    if (this.type == ResourceType.DIALOGUE) {
      this.handleMessageModify(msg);
    }
  };

  private handleMessageModify = (msg) => {
    if (msg.id == this.resourceId) {
      for (let i = 0; i < this.chatMessages.length; i++) {
        if (this.chatMessages[i].id == msg.messageId) {
          this.chatMessages[i].message = msg["content"];
          this.chatMessages[i].edited = msg["edited"];
          this.chatMessages[i].error = msg["error"];
          this.chatMessages[i].error = msg["error"];
        }
      }
    }
  };

  private handleRoomMessageDelete = (msg) => {
    if (this.type == ResourceType.ROOM) {
      this.handleMessageDelete(msg);
    }
  };

  private handleDialogueMessageDelete = (msg) => {
    if (this.type == ResourceType.DIALOGUE) {
      this.handleMessageDelete(msg);
    }
  };

  private handleMessageDelete = (msg) => {
    if (msg.id == this.resourceId) {
      for (let i = 0; i < this.chatMessages.length; i++) {
        if (this.chatMessages[i].id == msg.messageId) {
          return this.chatMessages.splice(i, 1);
        }
      }
    }
  };

  private handleMessageTyping = (msg) => {};

  constructor(
    private chatService: ChatService,
    private dialogueService: DialogueService,
    private dialogService: DialogService,
    private snackbar: MatSnackBar,
    private accountService: AccountService,
    private routerHandler: RouterHandler,
    private subscriptionService: SubscriptionService,
    private authService: AuthService,
    private roomService: RoomService,
    private clipboardService: ClipboardService,
    private deviceDetectorService: DeviceDetectorService
  ) {
    this.isMessagesLoading = true;

    console.log("router", this.routerHandler.getRoute());

    console.log("init chat");

    let firstLoading = true;
    this.subscriptionService.subscribeWorkspaceLoaded(() => {
      if (firstLoading) {
        firstLoading = false;
      } else {
        // reset now like this, later refactor
        this.resourceId = null;
        this.isMessagesLoading = true;
        this.onRouterChange(this.routerHandler.getRoute());
      }
    });
  }

  ngOnInit(): void {
    if (this.routerHandler.isOnPage("room/?/chat/?")) {
      this.onRouterChange(this.routerHandler.getRoute());
    }
    this.routerHandler.subscribe("room/?/chat/?", this.onRouterChange);
    this.subscribeAllWorkspaceChanges();
  }

  private subscribeAllWorkspaceChanges() {
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.handleRoomMessageCreate
    );
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.handleRoomMessageModify
    );
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.handleRoomMessageDelete
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.handleDialogueMessageCreate
    );
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.handleDialogueMessageModify
    );
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.handleDialogueMessageDelete
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.ALL,
      this.onRoomAccountPermissionChange.bind(this)
    );
  }

  private onRoomAccountPermissionChange(
    event: WorkspaceSubscriptionRoomAccountPermissionEventRecord
  ) {
    if (this.resourceId == event.id) {
      this.checkReadOnly();
    }
  }

  private unsubscribeAllWorkspaceChanges() {
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.handleRoomMessageCreate
    );
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.handleRoomMessageModify
    );
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.handleRoomMessageDelete
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.handleDialogueMessageCreate
    );
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.handleDialogueMessageModify
    );
    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.handleDialogueMessageDelete
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.ALL,
      this.onRoomAccountPermissionChange.bind(this)
    );
  }

  ngOnDestroy() {
    // we dont need all of the callback references, this feature simply close the subscription, and delete the handlers
    this.chatService.unsubscribeAllChatChanges(this.resourceId);

    this.unsubscribeAllWorkspaceChanges();

    this.routerHandler.unsubscribe("room/?/chat/?", this.onRouterChange);
    this.copySnackBarRef?.dismiss();
  }

  onSubmit(ev) {
    if (this.isMessageSending || ev.trim().length == 0) return;

    this.isMessageSending = true;

    this.service
      .sendMessage(this.resourceId, ev)
      .then((msgId) => {
        this.markdownEditorComponent.messageSent(true);
        this.sendSeen();
      })
      .catch((err) => {
        console.error("Send message error", err);
        this.markdownEditorComponent.messageSent(false);
        this.dialogService.showSnackbar(
          marker("Could not send message. (max length is 1000 characters)"),
          marker("OK")
        );
      })
      .finally(() => {
        this.isMessageSending = false;
      });
  }

  /**
   * Message id contains the timestamp in saferadix encoded
   * If you cut the last 5 char from the string, you'll get the encoded time
   * start at 2020.01.01 01:00 GMT+0100
   * every tick (calculated number by decodeSafeRadix) means 0.02 sec
   * @param id
   */
  private getTimeFromId(id: string): Date {
    let ticks = SafeRadix32.decodeSafeRadix32(id.slice(0, -5)); // get the ticks from the encoded number
    let timestamp = ticks * 20; // 0.02 sec for every tick
    return new Date(timestamp + 1577836800000); // add 2020.01.01 01:00 to it (this is the start)
  }

  /**
   * Convert raw server message into ChatData. Use loadMessage to install message into this system
   * @param data
   * @returns
   */
  private convertServerRecordToMessage(data: MessageRecord): ChatData {
    return {
      id: data.id,
      message: data.content,
      date: this.getTimeFromId(data.id),
      account: {
        id: data.posterId,
        avatarImageKey: null,
        avatarName: null,
      },
      edited: data.edited,
      error: data.error,
      errorMessage: data.errorMessage,
    };
  }

  /**
   * load, convert, and merge raw server response data into this chatData structure
   * connect the user to the msg in an async call
   * @param data
   */
  private loadMessage(data: MessageRecord) {
    let msg = this.convertServerRecordToMessage(data);
    this.mergeMessage(msg);
    this.accountService.getAccount(msg.account.id).then((acc) => {
      msg.account = acc;
    });
  }

  private getMoreMessages() {
    this.service
      .getMoreMessages(
        this.resourceId,
        this.chatMessages[this.chatMessages.length - 1].id
      )
      .then((oldMessages: MessageRecord[]) => {
        if (oldMessages.length) {
          oldMessages.forEach((msg) => {
            this.loadMessage(msg);
          });
        } else {
          this.chatEndReached = true;
        }

        this.isMessagesLoading = false;
      });
  }

  /**
   * push message into the array
   * @param msg
   */
  private mergeMessage(msg: ChatData) {
    // two most common operation (push, unshift)
    let p = performance.now();
    if (this.chatMessages.length == 0) {
      this.chatMessages.push(msg);
    } else if (msg.date.getTime() < this.chatMessages[0].date.getTime()) {
      this.chatMessages.push(msg);
    } else if (
      msg.date.getTime() >
      this.chatMessages[this.chatMessages.length - 1].date.getTime()
    ) {
      this.chatMessages.unshift(msg);
    } else {
      // check for its place
      for (let i = 0; i < this.chatMessages.length - 1; i++) {
        if (msg.date.getTime() < this.chatMessages[i].date.getTime()) {
          this.chatMessages.splice(i - 1, 0, msg);
        }
      }
    }
  }

  public onMessageDelete(messageId) {
    this.service.deleteMessage(this.resourceId, messageId).then(() => {
      this.dialogService.showSnackbar(marker("Message deleted."));
    });
  }

  public onMessageEdit(messageId, newContent) {
    this.service
      .editMessage(this.resourceId, messageId, newContent)
      .then(() => {
        this.dialogService.showSnackbar(marker("Message edited."));
      });
  }

  chatBoxBottomVisible(isVisible: boolean) {
    this.isBottomVisible = isVisible;
    if (isVisible && !this.isMessagesLoading && this.chatMessages.length > 0) {
      this.sendSeen();
    }
  }

  private sendSeen() {
    this.isNewMessageAtBottom = false;
    if (!this.authService.isAnonym() && !this.isReadOnly) {
      this.service.getData(this.resourceId).then((data) => {
        if (data["seenMessageId"] != data["topMessageId"]) {
          this.service
            .seenMessage(this.resourceId, data["topMessageId"])
            .then(() => {
              // TODO handle false
            })
            .catch((err) => {
              console.log("seen error", err, data["topMessageId"]);
            });
        }
      });
    }
  }

  chatBoxTopVisible(isVisible: boolean) {
    if (
      isVisible &&
      !this.chatEndReached &&
      !this.isMessagesLoading &&
      this.chatMessages.length > 0
    ) {
      this.isMessagesLoading = true;
      this.getMoreMessages();
    }
  }

  public onMouseDown(ev): void {
    this.isSelecting = true;
    this.clearPreviousSelection();
    //window.getSelection().removeAllRanges(); // it ruins the editor in edit mode; can not type character in the input (chrome)
    this.copySnackBarRef?.dismiss();
  }

  private selectChatMessagesBetweenElements(start: Node, end: Node): void {
    var chatParent = start.parentElement;
    var canSelect: boolean = false;

    for (let i = 0; i < chatParent.childNodes.length; i++) {
      const chatMessage = chatParent.childNodes.item(i);
      if (
        chatMessage.nodeType != 1 &&
        (<HTMLElement>chatMessage).tagName != this.chatTag
      )
        continue;

      if (chatMessage == start || chatMessage == end) {
        canSelect = !canSelect;
        if (!canSelect) {
          //still need to add the last message
          (<HTMLElement>chatMessage).classList.add("selected");
          if (this.selectedChatMessages.every((scm) => scm != chatMessage))
            this.selectedChatMessages.push(<HTMLElement>chatMessage);
          continue;
        }
      }

      if (canSelect) {
        (<HTMLElement>chatMessage).classList.add("selected");
        if (this.selectedChatMessages.every((scm) => scm != chatMessage))
          this.selectedChatMessages.push(<HTMLElement>chatMessage);
      } else {
        (<HTMLElement>chatMessage).classList.remove("selected");
        this.selectedChatMessages = this.selectedChatMessages.filter(
          (scm) => scm != chatMessage
        );
      }
    }
  }

  private selectFirstChatMessageDuringDeselect(start: Node) {
    var chatParent = start.parentElement;
    var canSelect: boolean = false;

    for (let i = 0; i < chatParent.childNodes.length; i++) {
      const chatMessage = chatParent.childNodes.item(i);
      if (
        chatMessage.nodeType != 1 &&
        (<HTMLElement>chatMessage).tagName != this.chatTag
      )
        continue;

      canSelect = start == chatMessage;

      if (canSelect) {
        (<HTMLElement>chatMessage).classList.add("selected");
        if (this.selectedChatMessages.every((scm) => scm != chatMessage))
          this.selectedChatMessages.push(<HTMLElement>chatMessage);
      } else {
        (<HTMLElement>chatMessage).classList.remove("selected");
        this.selectedChatMessages = this.selectedChatMessages.filter(
          (scm) => scm != chatMessage
        );
      }
    }
  }

  private getChatElementInParents(start: Node): Node {
    if (start.nodeType == 1 && (<HTMLElement>start).tagName === this.chatTag) {
      //node is HTMLElement and is chat element
      return start;
    }

    var el: HTMLElement = start.parentElement;

    while (el?.parentNode) {
      if (el.tagName === this.chatTag) return el;

      el = el.parentElement;
    }

    return null;
  }

  private clearPreviousSelection(): void {
    this.selectedChatMessages.forEach((cm) => cm.classList.remove("selected"));
    this.selectedChatMessages = [];
  }

  @HostListener("window:mouseup", ["$event"])
  mouseUp(e) {
    if (this.isSelecting) {
      this.isSelecting = false;
    }
  }

  @HostListener("document:selectionchange", ["$event"])
  selectionChange(e) {
    if (!this.isSelecting) return;

    var selection = window.getSelection();

    if (selection.anchorNode == null || selection.focusNode == null) return;

    var startSelectionNode = this.getChatElementInParents(selection.anchorNode);
    var endSelectionNode = this.getChatElementInParents(selection.focusNode);

    if (startSelectionNode && endSelectionNode) {
      if (startSelectionNode != endSelectionNode)
        this.selectChatMessagesBetweenElements(
          startSelectionNode,
          endSelectionNode
        );
      else if (this.selectedChatMessages.length > 0)
        //only run when not starting selection process
        this.selectFirstChatMessageDuringDeselect(startSelectionNode);
    }

    if (this.selectedChatMessages.length > 0 && !this.copySnackbarShowing) {
      this.copySnackbarShowing = true;
      this.copySnackBarRef = this.snackbar.openFromComponent(
        CopyChatMessagesSnackbarComponent,
        {
          horizontalPosition: "center",
          verticalPosition: "top",
          panelClass: ["mat-toolbar", "mat-primary", "nano-snackbar"],
        }
      );
      this.copySnackBarRef.onAction().subscribe((ret) => {
        this.copySelectedMessages();
      });
      this.copySnackBarRef.afterDismissed().subscribe(() => {
        this.copySnackbarShowing = false;
        this.clearPreviousSelection();
        window.getSelection().removeAllRanges();
      });
    }
  }

  @HostListener("document:copy", ["$event"]) onCtrlC(event) {
    if (this.selectedChatMessages.length > 0) {
      event.preventDefault();
      this.copySelectedMessages();
      this.copySnackBarRef?.dismiss();
    }
  }

  @HostListener("document:keydown.arrowup", ["$event"]) editFirstOwnMessage(
    event: KeyboardEvent
  ) {
    var firstOwnMessage = this.chatMessagesRef
      .toArray()
      .find((cmr) => cmr.modifyPermission == true);
    if (
      firstOwnMessage &&
      this.canEnterEditOnUpArrow &&
      this.isChatInputFocused
    )
      firstOwnMessage.editModeOn();
  }

  //close previously opened editors to keep only one editor open
  public onEditorOpened(e: ChatMessageComponent) {
    if (this.openEditor && this.openEditor != e) this.openEditor.onEditClose();
    this.openEditor = e;
  }

  private copySelectedMessages() {
    var copyText: string = "";
    for (var scm of this.selectedChatMessages.reverse()) {
      var name = scm.querySelector(".message-header .name").innerHTML;
      var date = scm.querySelector(".message-header .date").innerHTML;

      var paragraphElements = Array.from(
        scm.querySelectorAll(
          "app-markdown-view .paragraph, app-markdown-view code"
        )
      );

      if (paragraphElements.length == 0) continue;

      var paragraphs: string = paragraphElements
        .map((pe) => `${pe.textContent}\n\n`)
        .join("");

      copyText += `${name}, [${date}] \n${paragraphs}`;
    }

    this.clipboardService
      .copy(copyText.trim())
      .then(() => {
        this.dialogService.showSnackbar(
          marker("Messages copied to clipboard!")
        );
      })
      .catch((err) => {
        console.error("clipboard err", err);
        this.dialogService.openAlertDialog(
          marker("Copy Error"),
          marker("Could not copy to the clipboard")
        );
      });
  }

  onInputChange(message: string): void {
    if (message && message.length > 0) {
      this.canEnterEditOnUpArrow = false;
    } else {
      this.canEnterEditOnUpArrow = true;
    }
  }

  onFocusChanged(isFocused: boolean): void {
    this.isChatInputFocused = isFocused;
    if (
      isFocused &&
      this.chatScroll.nativeElement.scrollTop != 0 &&
      this.deviceDetectorService.isMobile()
    ) {
      setTimeout(()=>{
        this.chatScroll.nativeElement.scrollTo({
          top: 0,
          left: 0,
          behavior: "smooth",
        });
      }, 500) // wait for keyboard shows up
      
      this.isNewMessageAtBottom = false;
    }
  }
}
