"use strict";

function Socket(options) {
  function getStringFromData(buf, begin, len, defaultText) {
    var szValue;
    if ("function" == typeof TextDecoder) szValue = new TextDecoder("utf-8").decode(new Uint8Array(buf.buffer, buf.byteOffset + begin, len)); else try {
      szValue = String.fromCharCode.apply(null, new Uint8Array(buf.buffer, buf.byteOffset + begin, len)),
      szValue = decodeURIComponent(escape(szValue)).replace(/\0/g, "");
    } catch (ex) {
      szValue = "";
    }
    return szValue.length > 0 ? szValue : defaultText;
  }
  function getPostCodeFromData(buf) {
    var index1, index2, szValue = "";
    return buf.length >= 2 && (index1 = ("00" + buf[1].toString(16).toUpperCase()).substr(-2),
    index2 = ("00" + buf[0].toString(16).toUpperCase()).substr(-2), szValue = index1 + index2),
    szValue;
  }
  function IRC_CMD(dataView) {
    this.cmd, this.size, this.seq, this.flags, this.buf;
    try {
      dataView && (this.cmd = dataView.getUint32(0, !0), this.size = dataView.getUint32(4, !0),
      this.seq = dataView.getUint16(8, !0), this.flags = dataView.getUint16(10, !0), this.buf = new Uint8Array(dataView.buffer, dataView.byteOffset + 12));
    } catch (e) {}
  }
  function IRC_SERVER_HELLO(dataView) {
    this.iv, this.status, this.flags, this.session_key;
    try {
      dataView && (4 === me.version ? this.status = dataView.getUint8(0, !0) : (this.status = dataView.getUint8(16),
      this.flags = dataView.getUint32(17, !0)));
    } catch (e) {}
  }
  function Handshake(wsuri, sessionKey, channel, openCommand, success, failure) {
    var socket, onfail = failure || function(sock, reason) {
      console.log("Handshake failed: " + reason);
    }, onsuccess = success || function(sock) {
      sock.onmessage = function(evt) {};
    }, sockopen_auth = function(evt) {
      if (4 === me.version) return !0;
      var client_hello = new Uint8Array(54);
      client_hello[16] = openCommand, client_hello[17] = channel;
      for (var i = 0; i < sessionKey.length; i++) client_hello[22 + i] = sessionKey.charCodeAt(i);
      socket.send(client_hello);
    }, sockrecv_auth = function(evt) {
      var server_hello = new IRC_SERVER_HELLO(new DataView(evt.data));
      if (4 === me.version) switch (server_hello.status) {
       case DVCNET.CMD_AUTHENTICATE:
        var name = channel === CMD_CHANNEL ? "CMD" : "KVM", client_hello = new Uint8Array(34);
        client_hello[0] = "CMD" === name ? 2 : 1, client_hello[1] = 32;
        for (var i = 0; i < sessionKey.length; i++) client_hello[2 + i] = sessionKey.charCodeAt(i);
        socket.send(client_hello);
        break;

       case DVCNET.CMD_AUTHENTICATED:
        socket.onmessage = null, socket.onopen = null, onsuccess(socket);
        break;

       case DVCNET.NEG_BUSY_NO_MURC:
        video.set_licensed_murc(!1);

       case DVCNET.NEG_BUSY:
        me.cmdsocket = socket;
        var msgKey = "IRC.message.ConsoleInUseNoShare", tmrMsgKey = "IRC.message.TimedAutoDisconnect", msgBox = {
          callback: "neg_seize",
          messageKey: msgKey,
          stayTime: 10,
          timerStringKey: tmrMsgKey,
          title: null,
          icon: null,
          buttons: [ {
            text: "Acquire",
            textKey: "IRC.control.Acquire",
            isDefault: !0,
            value: !0
          }, {
            text: "Cancel",
            textKey: "cancel",
            isDefault: !1,
            value: !1
          } ]
        };
        video.confirm(msgBox);
        break;

       case DVCNET.NEG_SEIZE:
       case DVCNET.NEG_SHARE:
        onfail(socket, server_hello.status);
        break;

       case DVCNET.CMD_DENIED:
       case DVCNET.NEG_DROP:
       case DVCNET.CMD_NO_SESSIONS:
       default:
        socket.onmessage = null, socket.onopen = null, onfail(socket, server_hello.status);
      } else switch (socket.onmessage = null, socket.onopen = null, server_hello.status) {
       case 0:
        onsuccess(socket);
        break;

       default:
        onfail(socket, server_hello.status);
      }
    };
    if ("function" == typeof MozWebSocket) socket = new MozWebSocket(wsuri); else {
      if ("function" != typeof WebSocket) throw "No WebSockets!";
      try {
        socket = new WebSocket(wsuri);
      } catch (e) {
        "SyntaxError" === e.message && this.ie && wsuri.match(/[^[\]]+(?=])/g).length ? onfail(null, "IE+IPv6") : onfail(null, e.message);
      }
    }
    socket && (socket.onerror = function(evt) {
      1 === socket.readyState ? console.log("WebSocket connection failed: " + evt.type) : console.log("WebSocket error: " + evt.type);
    }, socket.binaryType = "arraybuffer", socket.onmessage = sockrecv_auth, socket.onopen = sockopen_auth);
  }
  var me = this, KVM_CHANNEL = 0, CMD_CHANNEL = 1, DVCNET = {
    CMD_AUTHENTICATE: 80,
    CMD_DENIED: 81,
    CMD_AUTHENTICATED: 82,
    NEG_BUSY: 83,
    NEG_DROP: 84,
    NEG_SEIZE: 85,
    NEG_SHARE: 86,
    CMD_NO_SESSIONS: 88,
    NEG_BUSY_NO_MURC: 89
  }, SCSI_ERR = {
    APPLET_CONNECT: -2,
    NO_IMAGE: -3,
    OPTION_ERROR: -4,
    SCRIPT_CONNECT: -5,
    NO_LICENSE: -6,
    REJECTED: -7,
    CONNECT_FAILED: -8,
    OP_FAILED: -9,
    EXPECTED_ISO: -10,
    EXPECTED_IMG: -11,
    VIDEO_STARTUP: -12,
    FW_ACTIVE: -13,
    FLASH_FAILED: -14
  };
  this.onstatuschange = function() {}, this.version = options.version || 4, this.openCommand = options.openCommand || 0,
  this.sessionKey = options.sessionKey, this.sockaddr = "wss://" + options.host + "/wss/ircport",
  this.sockrecv = options.sockrecv, this.ie = options.ie, this.video = options.video,
  this.health_state = "", this.power_state = "", this.neg_seize = function(resp) {
    if (me.openCommand = resp ? 1 : 0, resp === !0) {
      var cmd = new Uint8Array(2);
      cmd[0] = DVCNET.NEG_SEIZE, cmd[1] = 0, me.cmdsocket.send(cmd);
    } else me.close(), me.onclose && me.onclose();
  }, this.handleSeize = function(irc_cmd) {
    var user = getStringFromData(irc_cmd.buf, 0, 64, "UNKNOWN"), addr = getStringFromData(irc_cmd.buf, 64, 64, "UNKNOWN"), dlgMsg = "IRC.message.RequestAcquire", tmrMsg = "IRC.message.AutoPermission", actMsg = "IRC.status.granted", messageInserts = [ {
      placeHolder: "{0}",
      value: user
    }, {
      placeHolder: "{1}",
      value: addr
    } ], msgBox = {
      callback: "send_bool",
      retvalOverride: !0,
      messageKey: dlgMsg,
      messageInserts: messageInserts,
      title: null,
      icon: null,
      stayTime: 10,
      timerStringKey: tmrMsg,
      actionKey: actMsg,
      buttons: [ {
        textKey: "IRC.control.Yes",
        isDefault: !0,
        value: !0
      }, {
        textKey: "IRC.control.No",
        value: !1
      } ]
    };
    video.confirm(msgBox);
  }, this.handleShare = function(irc_cmd) {
    var user = getStringFromData(irc_cmd.buf, 0, 64, "UNKNOWN"), addr = getStringFromData(irc_cmd.buf, 64, 64, "UNKNOWN");
    me.sendBool(!1), video.handle_share(user, addr);
  }, this.handlePostCode = function(irc_cmd) {
    if (2 === irc_cmd.size) {
      var code = getPostCodeFromData(irc_cmd.buf);
      video.post_code(code);
    }
  }, this.handleAckNotification = function(irc_cmd) {
    var dlgMsg = "";
    if (4 !== irc_cmd.size) return void $.log("IRC: rejecting Ack notification: size is not 4.");
    var dataView = new DataView(irc_cmd.buf.buffer, irc_cmd.buf.byteOffset), result = dataView.getInt8(0, !0), device = dataView.getInt8(1, !0), type = dataView.getInt8(2, !0), connected = 0 !== dataView.getInt8(3, !0) ? !0 : !1;
    if (0 === result) video.vm_change({
      isFloppy: 1 == device,
      isURL: 8 == type,
      isConnected: !!connected
    }); else if (0 > result && !connected) {
      switch (result) {
       case -1:
        dlgMsg = "IRC.message.CannotParse";
        break;

       case SCSI_ERR.APPLET_CONNECT:
       case SCSI_ERR.SCRIPT_CONNECT:
        dlgMsg = "IRC.message.DriveAlreadyConnected";
        break;

       case SCSI_ERR.NO_IMAGE:
        dlgMsg = "IRC.message.ImageNotFound";
        break;

       case SCSI_ERR.OPTION_ERROR:
        dlgMsg = "IRC.message.InternalConfigError";
        break;

       case SCSI_ERR.NO_LICENSE:
        dlgMsg = "IRC.message.VMNotLicensed";
        break;

       case SCSI_ERR.REJECTED:
        dlgMsg = "IRC.message.CannotConnect";
        break;

       default:
        dlgMsg = "IRC.message.UnknownError";
      }
      if (dlgMsg.length > 0) {
        var msgBox = {
          messageKey: dlgMsg,
          stayTime: 10,
          title: null,
          icon: null
        };
        video.alert(msgBox);
      }
    }
  }, this.handleFirmwareUpdate = function(irc_cmd) {
    var dlgMsg = "IRC.message.ClosedFwUpgrade", msgBox = {
      messageKey: dlgMsg,
      stayTime: 20,
      title: null,
      icon: null
    };
    video.alert(msgBox), me.close();
  }, this.handleUnauthorized = function(irc_cmd) {
    var priv = "";
    switch (irc_cmd.flags) {
     case 2:
      priv = "RemoteConsolePriv";
      break;

     case 3:
      priv = "VirtualMediaPriv";
      break;

     case 4:
      priv = "VirtualPowerAndResetPriv";
    }
    priv.length > 0 && video.unauthorized(priv);
  }, this.handleLicenseViolation = function(irc_cmd) {
    var dlgMsg = "IRC.message.IRCNotLicensed", msgBox = {
      titleKey: "IRC.title.LicenseRequired",
      type: "critical",
      messageKey: dlgMsg
    };
    video.alert(msgBox), me.close();
  }, this.handleReset = function(irc_cmd) {
    var dlgMsg = "IRC.message.iLOReset", msgBox = {
      messageKey: dlgMsg,
      stayTime: 20,
      title: null,
      icon: null
    };
    video.alert(msgBox), me.close();
  }, this.onhealthchange = function(health_state) {
    this.health_state !== health_state && (this.health_state = health_state, video.health_change(health_state));
  }, this.handleHealthState = function(health_state) {
    if (me.onhealthchange) switch (health_state) {
     case 0:
      me.onhealthchange("OP_STATUS_OK");
      break;

     case 1:
      me.onhealthchange("OP_STATUS_WARNING");
      break;

     case 2:
      me.onhealthchange("OP_STATUS_CRITICAL");
      break;

     default:
      me.onhealthchange("OP_STATUS_UNKNOWN");
    }
  }, this.handlePowerState = function(power_state) {
    if (me.onpowerchange) switch (power_state) {
     case 1:
      me.onpowerchange("ON");
      break;

     case 0:
     default:
      me.onpowerchange("OFF");
    }
  }, this.onpowerchange = function(power_state) {
    this.power_state !== power_state && (this.power_state = power_state, video.power_change(power_state));
  }, this.cmdsockrecv = function(evt) {
    var irc_cmd = new IRC_CMD(new DataView(evt.data));
    switch (irc_cmd.cmd) {
     case 2:
      video.status_change("Post Complete");
      break;

     case 3:
      me.handlePowerState(irc_cmd.buf[0]);
      break;

     case 4:
      me.handleHealthState(irc_cmd.buf[0]);
      break;

     case 5:
      me.handlePostCode(irc_cmd);
      break;

     case 6:
      me.handleSeize(irc_cmd);
      break;

     case 7:
      me.handleAckNotification(irc_cmd);
      break;

     case 9:
      me.handleShare(irc_cmd);
      break;

     case 10:
      console.log("IRC: received firmware update notification."), me.handleFirmwareUpdate(irc_cmd);
      break;

     case 11:
      console.log("IRC: received unauthorized notification."), me.handleUnauthorized(irc_cmd);
      break;

     case 13:
      break;

     case 14:
      me.handleLicenseViolation(irc_cmd);
      break;

     case 15:
      console.log("IRC: iLO reset detected."), me.handleReset(irc_cmd);
      break;

     case 16:
     case 17:
      break;

     default:
      console.log("IRC: received unhandled command: ", irc_cmd);
    }
  }, this.open_cmdsocket = function() {
    new Handshake(me.sockaddr, me.sessionKey, 1, 0, function(ws) {
      ws.onmessage = me.cmdsockrecv, ws.onclose = function() {
        me.onclose && me.onclose(), console.log("cmd socket closed");
      }, ws.onerror = function(evt) {
        console.log("cmd socket open error: " + evt);
      }, me.cmdsocket = ws, me.send = me.send_kvm, setTimeout(function() {
        me.all_keys_up();
      }, 2e3);
    }, null);
  }, this.send_kvm = function(arr) {
    this.socket && this.socket.readyState && this.socket.send(new Uint8Array(arr));
  }, this.send_cmd = function(arr) {
    this.cmdsocket && this.cmdsocket.readyState && this.cmdsocket.send(new Uint8Array(arr));
  }, this.send_null = function(arr) {}, this.send = this.send_null, this.sendBool = function(rsp) {
    var cmd = [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ];
    cmd[0] = rsp ? 4 : 3, this.send_cmd(cmd);
  }, this.kbdsend = function(hidrpt) {
    var cmd;
    cmd = me.version > 2 ? [ 1, 0 ] : [ 255, 214 ], cmd = cmd.concat(hidrpt), me.send(cmd);
  }, this.all_keys_up = function() {
    me.kbdsend([ 0, 0, 0, 0, 0, 0, 0, 0 ]);
  }, this.ctrl_alt_del = function() {
    me.kbdsend([ 5, 0, 76, 0, 0, 0, 0, 0 ]), setTimeout(function() {
      me.kbdsend([ 5, 0, 0, 0, 0, 0, 0, 0 ]), setTimeout(function() {
        me.all_keys_up();
      }, 5);
    }, 250);
  }, this.num_lock = function() {
    me.kbdsend([ 0, 0, 83, 0, 0, 0, 0, 0 ]), setTimeout(function() {
      me.all_keys_up();
    }, 50);
  }, this.caps_lock = function() {
    me.kbdsend([ 0, 0, 57, 0, 0, 0, 0, 0 ]), setTimeout(function() {
      me.all_keys_up();
    }, 50);
  }, this.refresh = function() {
    me.version > 2 ? me.send([ 5, 0 ]) : me.send([ 27, 91, 126 ]);
  }, this.send_ack = function() {
    me.send([ 12, 0 ]);
  }, this.keepalive = function() {
    me.send([ 27, 91, 40 ]);
  }, this.autoalive = function() {
    me.send([ 27, 91, 38 ]);
  }, this.mouse_send = function(rx, ry, ax, ay, button, wheel) {
    if (2 === this.version) return !1;
    var buf = [ 2, 0, 255 & ax, ax >> 8, 255 & ay, ay >> 8, 255 & rx, 255 & ry, button, wheel ];
    return this.send(buf), !0;
  }, this.no_op = function() {
    return !1;
  }, this.close = function() {
    this.socket && this.socket.close(), this.cmdsocket && this.cmdsocket.close();
  }, new Handshake(me.sockaddr, me.sessionKey, KVM_CHANNEL, me.openCommand, function(ws) {
    me.connected = !0, me.socket = ws, me.socket.onmessage = me.sockrecv, me.socket.onclose = function(evt) {
      console.log("WebSocket closed", evt), me.connected = !1, me.onclose && me.onclose();
    }, me.socket.onerror = function(evt) {
      console.log("WebSocket ERROR: ", evt);
    }, me.open_cmdsocket();
  }, function(ws, error) {
    var alertToast = function(key) {
      video.toast({
        textKey: key,
        type: "critical",
        sticky: !0
      });
    };
    switch (error) {
     case DVCNET.CMD_DENIED:
      switch (me.openCommand) {
       case 1:
        alertToast("IRC.message.AcquireDenied");
        break;

       case 2:
        alertToast("IRC.message.ShareDenied");
        break;

       case 0:
       default:
        alertToast("IRC.message.SessionLimitReached");
      }
      break;

     case 2:
      var msgKey = "IRC.message.ConsoleInUseNoShare", tmrMsgKey = "IRC.message.TimedAutoDisconnect", msgBox = {
        callback: "send_bool",
        messageKey: msgKey,
        stayTime: 10,
        timerStringKey: tmrMsgKey,
        title: null,
        icon: null,
        buttons: [ {
          textKey: "IRC.control.Acquire",
          isDefault: !0,
          value: !0
        }, {
          textKey: "cancel",
          isDefault: !1,
          value: !1
        } ]
      };
      return video.confirm(msgBox), void me.close();

     case 3:
      alertToast("status_labels.NOT_SUPPORTED");
      break;

     case 4:
      video.toast({
        text: "Bad Request",
        type: "critical",
        sticky: !0
      });
      break;

     case 5:
      video.toast({
        text: "Internal Error",
        type: "critical",
        sticky: !0
      });
      break;

     case 6:
     case DVCNET.CMD_NO_SESSIONS:
      alertToast("IRC.message.NoFreeSessions");
      break;

     case 7:
      alertToast("IRC.message.FeatureNotLicensed");
      break;

     case "SyntaxError":
      alertToast("IRC.message.WebSocketSyntaxError");
      break;

     case "IE+IPv6":
      alertToast("IRC.message.WebSocketIEIPv6");
      break;

     default:
      me.ie || isNaN(error) || video.toast({
        text: "Unexpected error: " + error,
        type: "critical",
        sticky: !0
      });
    }
    console.log("Handshake error: ", error), me.close(), me.onclose && me.onclose();
  });
}