<template>
  <div ref="messageDiv" class="messages">
    <div class="alert">
      <el-alert title="右键自己的消息可以撤回" type="warning" />
      <el-alert title="Alt+回车可以发送消息" type="warning" />
    </div>
    <Message v-for="(item,index) in messages" :key="index" :data="item" @remove="removeMessage"/>
    <!--div class="new-message-counter">
      TODO 新消息提醒
    </div-->
  </div>
  <el-input
      v-model="content"
      :disabled="!hasConnection"
      class="input-area"
      maxlength="500"
      placeholder="请输入..."
      rows="3"
      show-word-limit
      type="textarea"
      ref="contentInput"
  />
<!--  粘贴上传照片-->
  <div class="button-div">
    <el-button :disabled="!hasConnection" class="send-button" plain type="warning" @click="sendMessage">发送</el-button>
  </div>
</template>
  
<script lang="ts" setup>
  import {h, nextTick, onMounted, onUnmounted, ref} from "vue"
  import {ElMessage, ElNotification, InputInstance} from "element-plus"
  import {MessageData, MessageTypes} from "../../entities/MessageData"
  import {useStore} from "../../store";
  import Message from "../../components/Message.vue";
  import requester from "../../api/requester";

  const messageDiv = ref<HTMLDivElement>();
  const messages = ref<Array<MessageData>>([])
  const webSocketConnection = ref<WebSocket>()
  const content = ref<string>("");
  const hasConnection = ref<boolean>(true);
  const idMin = ref<number>(0);
  const idMax = ref<number>(0);
  const isGetting = ref<boolean>(false);
  const store = useStore();
  const contentInput = ref<InputInstance>();
  const sendData = (operator: string, data: any = {}) => {
    data.op = operator
    if(webSocketConnection.value){
      webSocketConnection.value!.send(JSON.stringify(data))
    }
  }
  const handlerWebSocketConnectionOpen = (_: Event) => {
    sendData("login", {"token": window.localStorage.getItem("token")})
  }
  const handlerWebSocketConnectionClose = (event: CloseEvent) => {
    if (event.code !== 1005){
      ElMessage.error("与服务器断开连接")
    }
    messages.value = [];
    hasConnection.value = false;
  }
  const handlerWebSocketConnectionError = (_: Event) => {
    ElMessage.error("与服务器沟通出现错误，请刷新重试！")
  }
  const removeMessage = (id: number) => {
    sendData("remove", {"id": id})
    const messages_temp = messages.value.filter((data) => data.id != id)
    messages.value = []
    nextTick(() => {
      messages.value = messages_temp
    })
  }
  const handlerWebSocketConnectionMessage = (event: MessageEvent<string>) => {
    const object = JSON.parse(event.data);
    switch (object.op){
      case "login":
        LoginCallback(object.data.success);
        break;
      case "get":
        GetCallback(object.data);
        break;
      case "count":
        CountCallback(object.data);
        break;
      case "send":
        SendCallback(object.data.message);
        break;
      case "recall":
        RecallCallback(object.data.id);
        break;
      case "add":
        AddCallback(object.data.message);
        break;
      case "getNew":
        GetCallback(object.data);
        break;
      case "pong":
        PongCallback();
        break;
      default:
        console.error("错误的请求", object.op);
    }
  }
  const LoginCallback = (success: boolean) => {
    if(!success) {
      ElMessage.error("登陆失败，请重新登陆！")
    }else{
      sendData("count")
      sendData("ping")
    }
  }
  const GetCallback = (message: Array<MessageData>) => {
    message = message.sort((a,b) => Date.parse(a.sendtime!) - Date.parse(b.sendtime!));
    let scrollToBottom = false;
    if (messages.value.length === 0){
      scrollToBottom = true;
    }
    messages.value = message.concat(messages.value);
    idMax.value = messages.value[0].id! - 1;
    idMin.value = 0;
    if(idMax.value >= 10){
      idMin.value = idMax.value - 10;
    }
    const beforeHeight = messageDiv.value?.scrollHeight ? messageDiv.value.scrollHeight : -1;
    nextTick(() => {
      if (scrollToBottom){
        messageDiv.value?.scrollTo({top: messageDiv.value.scrollHeight});
      }else {
        messageDiv.value?.scrollTo({top: messageDiv.value.scrollHeight - beforeHeight});
      }
    })
  }
  const CountCallback = (count: number) => {
    let idSent = 1;
    if(count > 10){
      idSent = count - 10
    }
    sendData("get", {"minId": idSent, "count": 10})
  }

  const sendMessage = () => {
    if(content.value.replaceAll(" ","").replaceAll("\n","") === ""){
      ElMessage.error("消息不能为空！")
      return;
    }
    sendData("send",{
      content: content.value,
      sender: store.userinfo!.username,
      // 消息
      type: MessageTypes.text})
    content.value = ""
  }
  const SendCallback = (message: MessageData) => {
    messages.value.push(message);
    nextTick(() => {
      // noinspection JSSuspiciousNameCombination
      messageDiv.value!.scrollTop = messageDiv.value!.scrollWidth
    })
  }
  const RecallCallback = (id: number) => {
    const messages_temp = messages.value.filter((data) => data.id != id)
    messages.value = []
    nextTick(() => {
      messages.value = messages_temp
    })
  }
  const AddCallback = (message: MessageData) => {
    messages.value.push(message);
    let message_content = message.content!;
    if(message_content.length >= 99){
      message_content += message_content.substring(0,98) + "...";
    }
    ElNotification({
      title: message.sender,
      message: h('i', {}, message_content),
    })
  }
  const messageDivOnScroll = (_: Event) => {
    const scrollTop = messageDiv.value!.scrollTop;
    if(scrollTop <= 50){
      if(!isGetting.value){
        sendData("getNew", {"minId": idMin.value,"maxId": idMax.value, "count": 10})
        isGetting.value = true;
      }
    }
  }
  const GetNewCallback = (data: Array<MessageData>) => {
    isGetting.value = false;
    for (let message of messages.value) {
      data.push(message);
    }
    messages.value = []
    const now_max = messageDiv.value!.scrollHeight;
    nextTick(() => {
      messages.value = data
      messages.value.sort((a, b) => a.id! - b.id!)
      idMax.value = messages.value[0].id! - 1;
      idMin.value = 0;
      if(idMax.value >= 10){
        idMin.value = idMax.value - 10;
      }
      nextTick(() => {
        messageDiv.value!.scrollTop += messageDiv.value!.scrollHeight - now_max;
      })
    })
  }
  const PongCallback = () => {
    setTimeout(() => sendData("ping"), 60000);
  }
  const mount = () => {
    webSocketConnection.value = new WebSocket(requester.getUri({url: "/chat"}).replace("http", "ws"));
    webSocketConnection.value.onopen = handlerWebSocketConnectionOpen;
    webSocketConnection.value.onclose = handlerWebSocketConnectionClose;
    webSocketConnection.value.onerror = handlerWebSocketConnectionError;
    webSocketConnection.value.onmessage = handlerWebSocketConnectionMessage;
    messageDiv.value!.onscroll = messageDivOnScroll;
    if (contentInput.value && contentInput.value.textarea) {
      contentInput.value.textarea.addEventListener("keydown", (e) => {
        if (e.altKey && e.key === "Enter"){
          sendMessage();
        }
      })
    }
  }
  const unmount = () => {
    webSocketConnection.value!.close();
  }
  onMounted(mount)
  onUnmounted(unmount)



</script>
  
<style scoped>
  div.messages {
    width: 100%;
    height: 80%;
    display: flex;
    flex-direction: column;
    float: left;
    scrollbar-width: thin;
    overflow-y: scroll;
    overflow-x: hidden;
    background: rgba(250,250,250);
    padding: 10px;
  }

  .input-area {
    margin-top: 20px;
    height: calc(20% - 52px);
  }

  .send-button {
    margin-top: 10px;
  }

  .button-div {
    display: flex;
    flex-direction: row-reverse;
  }
  .new-message-counter {
    float: right;
    background: #0061AB;
    width: 64px;
    height: 64px;
    z-index: 9999;
  }
  .alert {
    position: sticky;
    top: 0;
    left: 0;
    width: 100%;
  }
</style>