<template>
  <v-layout
    column
    fill-height
  >
    <v-toolbar color="grey">
      <v-toolbar-title flex>
        {{ title }}
      </v-toolbar-title>
      <v-spacer />
      <v-text-field
        v-if="this.hideUrl == undefined"
        :disabled="disableUrl"
        label="Telws URL"
        single-line
        prepend-icon="mdi-link-variant"
        hide-details
        v-model="telwsurl"
      />
      <v-tooltip bottom>
        <template v-slot:activator="{ on }">
          <v-btn
            icon
            @click="connect"
            v-on="on"
          >
            <v-icon>mdi-lan-connect</v-icon>
          </v-btn>
        </template>
        <span>Connect</span>
      </v-tooltip>
      <v-tooltip bottom>
        <template v-slot:activator="{ on }">
          <v-btn
            icon
            @click="disconnect"
            v-on="on"
          >
            <v-icon>mdi-lan-disconnect</v-icon>
          </v-btn>
        </template>
        <span>Disconnect</span>
      </v-tooltip>
      <v-tooltip bottom>
        <template v-slot:activator="{ on }">
          <v-btn
            icon
            v-on="on"
            @click="popChangeBackgroundDialog"
          >
            <v-icon>mdi-palette</v-icon>
          </v-btn>
        </template>
        <span>Change Console Background Color</span>
      </v-tooltip>
    </v-toolbar>
    <v-layout fill-height>
      <xterm-vue
        ref="term"
        v-on:title-change="titleChange"
      />
    </v-layout>
    <v-toolbar color="grey">
      <v-text-field
        placeholder="Write here and press Enter to send..."
        single-line
        prepend-icon="mdi-typewriter"
        hide-details
        v-model="writeBarText"
        @keydown.enter="pasteWriteBar"
      />
    </v-toolbar>
    <v-dialog
      v-model="userAuthDialog"
      max-width="290"
    >
      <v-card>
        <v-card-title class="headline">Simple AAA Auth</v-card-title>

        <v-card-text>
          <v-form>
            <v-text-field
              ref="authUsername"
              v-model="authDialogUsername"
              label="Username"
              @keydown.enter="focusPasswordTextfield"
            />
            <v-text-field
              ref="authPassword"
              v-model="authDialogPassword"
              label="Password"
              type="password"
              @keydown.enter="doUserLogin"
            />
          </v-form>
        </v-card-text>

        <v-card-actions>
          <v-spacer></v-spacer>

          <v-btn
            color="danger"
            text
            @click="cancelUserLogin"
          >
            Cancel
          </v-btn>

          <v-btn
            color="success"
            text
            @click="doUserLogin"
          >
            Login
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
    <v-dialog
      v-model="changeBackgroundDialog"
      max-width="290"
    >
      <v-card>
        <v-card-title class="headline">Change Background</v-card-title>

        <v-card-text>
          <v-color-picker
            flat
            hide-mode-switch
            mode="hexa"
            v-model="background"
          />
        </v-card-text>

        <v-card-actions>
          <v-spacer></v-spacer>

          <v-btn
            color="danger"
            text
            @click="closeChangeBackgroundDialog"
          >
            Close
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-layout>
</template>

<script>
//import XtermVuePlugin from "@swzry/xterm-vue";
import axios from "axios";
import smcrypto from "sm-crypto";
const ConnState = {
  IDLE: 0,
  GET_INFO: 1,
  USER_AUTH: 2,
  SERVER_AUTH: 3,
  AUTH_OK: 4,
  CONNECTED: 5
};
export default {
  name: "telws_view",
  props: [
    "hideUrl",
    "defaultUrl",
    "autoConnect",
    "defaultBackground",
    "disableUrl"
  ],
  watch: {
    background(newVal, oldVal) {
      if (this.$refs.term.$term) {
        console.log("Change Background", this.$refs.term.$term, newVal);
        this.$refs.term.$term.setOption("theme", { background: newVal });
      }
    }
  },
  data: () => ({
    background: "",
    writeBarText: "",
    title: "Untitled",
    telwsurl: "",
    changeBackgroundDialog: false,
    userAuthDialog: false,
    authDialogUsername: "",
    authDialogPassword: "",
    connSM: {
      state: ConnState.IDLE,
      axiosCancel: undefined,
      continueDo: false,
      url_prefix: "",
      auth_jwt: "",
      wsurl: "",
      wsconn: undefined
    },
    encryptConfig: undefined
  }),
  mounted() {
    this.$refs.term.fit();
    if (this.defaultBackground) {
      this.background = this.defaultBackground;
    } else {
      this.background = "#002b36";
    }
    if (this.defaultUrl) {
      this.telwsurl = this.defaultUrl;
    }
    if (this.autoConnect != undefined) {
      this.connect();
    }
  },
  destroyed() {
    this.disconnect();
  },
  methods: {
    popChangeBackgroundDialog() {
      this.changeBackgroundDialog = true;
    },
    closeChangeBackgroundDialog() {
      this.changeBackgroundDialog = false;
    },
    focusUsernameTextfield() {
      this.$refs.authUsername.focus();
    },
    focusPasswordTextfield() {
      this.$refs.authPassword.focus();
    },
    pasteWriteBar(event) {
      this.$refs.term.paste(this.writeBarText);
      this.writeBarText = "";
    },
    titleChange(title) {
      this.title = title;
    },
    popUserLoginDialog() {
      this.authDialogUsername = "";
      this.authDialogPassword = "";
      this.userAuthDialog = true;
      var self = this;
      this.$nextTick(() => {
        self.focusUsernameTextfield();
      });
    },
    doUserLogin() {
      this.userAuthDialog = false;
      var authData = {
        method: "simple-aaa",
        username: this.authDialogUsername,
        password: this.authDialogPassword
      };
      var jsondata = JSON.stringify(authData);
      var cm = 1;
      if (this.encryptConfig.cipherMode === "C1C3C2") {
        cm = 1;
      } else {
        cm = 0;
      }
      if (this.encryptConfig) {
        var ct = smcrypto.sm2.doEncrypt(
          jsondata,
          this.encryptConfig.pubkey,
          cm
        );
        var tureCipher = this.encryptConfig.header + ct;
        this.doLoginAuth(tureCipher);
      } else {
        this.connSM.state = ConnState.IDLE;
        this.$refs.term.$term.writeln("Password encypt failed.");
        this.$refs.term.$term.onData(function(data) {});
      }
    },
    doWebsocketConnect() {
      if (this.connSM.wsconn) {
        this.connSM.wsconn.close();
      }
      this.$refs.term.$term.writeln(
        "Connecting Websocket '" + this.connSM.wsurl + "'..."
      );
      var trueUrl = this.connSM.wsurl + "?token=" + this.connSM.auth_jwt;
      this.connSM.wsconn = new WebSocket(trueUrl);
      var tty = this.$refs.term.$term;
      var wsconn = this.connSM.wsconn;
      var self = this;
      wsconn.onopen = function() {
        //wsconn.binaryType = "arrayBuffer"
        wsconn.binaryType = "blob";
        tty.writeln("Connected.\r\n");
        self.connSM.state = ConnState.CONNECTED;
      };
      wsconn.onmessage = function(event) {
        var er = new FileReader();
        er.onload = function(e) {
          var buf = new Uint8Array(er.result);
          tty.write(buf);
        };
        er.readAsArrayBuffer(event.data);
      };
      wsconn.onclose = function() {
        tty.writeln("\r\n\r\nConnection Closed.");
      };
      wsconn.onerror = function() {
        tty.writeln("\r\n\r\nConnection Error.");
      };
      this.$refs.term.$term.onData(function(data) {
        if (wsconn) {
          var bd = Buffer.from(data, "utf8");
          wsconn.send(bd);
        }
      });
    },
    doLoginAuth(authData) {
      var adjson = { authData: authData };
      var self = this;
      this.connSM.state = ConnState.SERVER_AUTH;
      axios
        .post(this.connSM.url_prefix + "login.satori", adjson, {
          cancelToken: new axios.CancelToken(function executor(c) {
            self.connSM.cancelToken = c;
          })
        })
        .then(resp => {
          if (resp.data.suc) {
            this.connSM.auth_jwt = resp.data.jwt;
            this.connSM.state = ConnState.AUTH_OK;
            this.doWebsocketConnect();
          } else {
            if (resp.data.etype == "auth_failed") {
              this.$refs.term.$term.writeln("Simple AAA Auth Failed.");
              this.$refs.term.$term.writeln(
                "Reason: " + resp.data.auth_error_msg
              );
            } else {
              this.$refs.term.$term.writeln("Failed to do remote auth.");
              this.$refs.term.$term.writeln("Error: " + resp.data.etype);
            }
            this.connSM.state = ConnState.IDLE;
          }
        })
        .catch(errmsg => {
          this.$refs.term.$term.writeln("Failed to do remote auth.");
          console.warn(errmsg);
          if (errmsg.response) {
            console.warn(errmsg.response);
            this.$refs.term.$term.writeln("Error: " + errmsg.response.data);
          } else if (errmsg.request) {
            console.warn(errmsg.request);
            this.$refs.term.$term.writeln(
              "Error: request failed. See javascript console for detail."
            );
          } else {
            this.$refs.term.$term.writeln(
              "Error: unknown. See javascript console for more detail."
            );
          }
          this.connSM.state = ConnState.IDLE;
        });
    },
    cancelUserLogin() {
      this.userAuthDialog = false;
      this.connSM.state = ConnState.IDLE;
      this.$refs.term.$term.writeln("Auth cancel by user.");
      this.$refs.term.$term.onData(function(data) {});
    },
    disconnect() {
      if (this.connSM.state == ConnState.IDLE) {
        return;
      }
      this.connSM.continueDo = false;
      if (this.connSM.cancelToken) {
        this.connSM.cancelToken();
      }
      this.connSM.state = ConnState.IDLE;
      this.$refs.term.$term.writeln("");
      this.$refs.term.$term.writeln("Disconnected.");
      this.$refs.term.$term.onData(function(data) {});
      if (this.connSM.wsconn) {
        this.connSM.wsconn.close();
      }
    },
    connect() {
      if (this.connSM.state != ConnState.IDLE) {
        this.disconnect();
      }
      this.$refs.term.$term.clear();
      if (this.telwsurl.charAt(this.telwsurl.length - 1) == "/") {
        this.connSM.url_prefix = this.telwsurl;
      } else {
        this.connSM.url_prefix = this.telwsurl + "/";
      }
      var turl = new URL(this.connSM.url_prefix);
      var schpre = "ws:";
      if (turl.protocol === "https:") {
        schpre = "wss:";
      }
      this.connSM.wsurl =
        schpre + "//" + turl.host + turl.pathname + "ws.satori";
      this.$refs.term.$term.writeln("Connecting " + this.telwsurl + "...");
      var self = this;
      this.connSM.continueDo = true;
      this.connSM.state = ConnState.GET_INFO;
      axios
        .get(this.telwsurl, {
          cancelToken: new axios.CancelToken(function executor(c) {
            self.connSM.cancelToken = c;
          })
        })
        .then(resp => {
          if (self.connSM.continueDo) {
            this.$refs.term.$term.writeln("Telws server info fetched.");
            console.log(resp.data);
            if (resp.data.auth_methods) {
              if (resp.data.auth_methods.indexOf("simple-aaa") > -1) {
                this.encryptConfig = resp.data.encrypt;
                this.connSM.state = ConnState.USER_AUTH;
                this.popUserLoginDialog();
              } else {
                this.$refs.term.$term.writeln(
                  "Failed to connect to telws server."
                );
                this.$refs.term.$term.writeln(
                  "Error: no auth method supported by this client."
                );
                this.connSM.state = ConnState.IDLE;
              }
            } else {
              this.$refs.term.$term.writeln(
                "Failed to connect to telws server."
              );
              this.$refs.term.$term.writeln("Error: not a valid telws server.");
              this.connSM.state = ConnState.IDLE;
            }
          }
        })
        .catch(errmsg => {
          this.$refs.term.$term.writeln("Failed to connect to telws server.");
          console.warn(errmsg);
          if (errmsg.response) {
            console.warn(errmsg.response);
            this.$refs.term.$term.writeln("Error: " + errmsg.response.data);
          } else if (errmsg.request) {
            console.warn(errmsg.request);
            this.$refs.term.$term.writeln(
              "Error: request failed. See javascript console for detail."
            );
          } else {
            this.$refs.term.$term.writeln(
              "Error: unknown. See javascript console for more detail."
            );
          }
          this.connSM.state = ConnState.IDLE;
        });
    }
  }
};
</script>
