import { ECHO_FAILED } from "./socket.js";
import * as history from "./history.js";

export function build(ctx) {
  const connectionStatusNotConnected = "Sshwifty is not connected";
  const connectionStatusConnecting =
    "Sshwifty is connecting to it's backend server, it should only take " +
    "less than a second or two";
  const connectionStatusDisconnected =
    "Sshwifty has disconnected from it's backend server";
  const connectionStatusConnected =
    "Sshwifty has connected to it's backend server, user interface " +
    "is operational";
  const connectionStatusMeasurable =
    "Unable to measure connection delay. The connection maybe very " +
    "busy or already lost";

  const connectionDelayGood =
    "Connection delay is low, operation should be very responsive";
  const connectionDelayFair =
    "Experiencing minor connection delay, operation should be responded " +
    "within reasonable time";
  const connectionDelayMedian =
    "Experiencing median connection delay, consider slow down your input " +
    "to avoid misoperate";
  const connectionDelayHeavy =
    "Experiencing long connection delay, operation may appear to be sticked";

  const buildEmptyHistory = () => {
    let r = [];

    for (let i = 0; i < 32; i++) {
      r.push(0);
    }

    return r;
  };

  let inboundPerSecond = 0,
    outboundPerSecond = 0,
    trafficPreSecondNextUpdate = new Date(),
    inboundPre10Seconds = 0,
    outboundPre10Seconds = 0,
    trafficPre10sNextUpdate = new Date(),
    inboundHistory = new history.Records(buildEmptyHistory()),
    outboundHistory = new history.Records(buildEmptyHistory()),
    trafficSamples = 1;

  let delayHistory = new history.Records(buildEmptyHistory()),
    delaySamples = 0,
    delayPerInterval = 0;

  return {
    update(time) {
      if (time >= trafficPreSecondNextUpdate) {
        trafficPreSecondNextUpdate = new Date(time.getTime() + 1000);
        inboundPre10Seconds += inboundPerSecond;
        outboundPre10Seconds += outboundPerSecond;

        this.status.inbound = inboundPerSecond;
        this.status.outbound = outboundPerSecond;

        inboundPerSecond = 0;
        outboundPerSecond = 0;

        trafficSamples++;
      }

      if (time >= trafficPre10sNextUpdate) {
        trafficPre10sNextUpdate = new Date(time.getTime() + 10000);

        inboundHistory.update(inboundPre10Seconds / trafficSamples);
        outboundHistory.update(outboundPre10Seconds / trafficSamples);

        inboundPre10Seconds = 0;
        outboundPre10Seconds = 0;
        trafficSamples = 1;

        if (delaySamples > 0) {
          delayHistory.update(delayPerInterval / delaySamples);

          delaySamples = 0;
          delayPerInterval = 0;
        }
      }
    },
    classStyle: "",
    windowClass: "",
    message: "",
    status: {
      description: connectionStatusNotConnected,
      delay: 0,
      delayHistory: delayHistory.get(),
      inbound: 0,
      inboundHistory: inboundHistory.get(),
      outbound: 0,
      outboundHistory: outboundHistory.get()
    },
    connecting() {
      this.message = "--";
      this.classStyle = "working";
      this.windowClass = "";
      this.status.description = connectionStatusConnecting;
    },
    connected() {
      this.message = "??";
      this.classStyle = "working";
      this.windowClass = "";
      this.status.description = connectionStatusConnected;
    },
    traffic(inb, outb) {
      inboundPerSecond += inb;
      outboundPerSecond += outb;
    },
    echo(delay) {
      delayPerInterval += delay > 0 ? delay : 0;
      delaySamples++;

      if (delay == ECHO_FAILED) {
        this.message = "";
        this.classStyle = "red flash";
        this.windowClass = "red";
        this.status.description = connectionStatusMeasurable;

        return;
      }

      let avgDelay = Math.round(delayPerInterval / delaySamples);

      this.message = Number(avgDelay).toLocaleString() + "ms";
      this.status.delay = avgDelay;

      if (avgDelay < 30) {
        this.classStyle = "green";
        this.windowClass = "green";
        this.status.description =
          connectionStatusConnected + ". " + connectionDelayGood;
      } else if (avgDelay < 100) {
        this.classStyle = "yellow";
        this.windowClass = "yellow";
        this.status.description =
          connectionStatusConnected + ". " + connectionDelayFair;
      } else if (avgDelay < 300) {
        this.classStyle = "orange";
        this.windowClass = "orange";
        this.status.description =
          connectionStatusConnected + ". " + connectionDelayMedian;
      } else {
        this.classStyle = "red";
        this.windowClass = "red";
        this.status.description =
          connectionStatusConnected + ". " + connectionDelayHeavy;
      }
    },
    close(e) {
      ctx.connector.inputting = false;

      if (e === null) {
        this.message = "";
        this.classStyle = "";
        this.status.description = connectionStatusDisconnected;

        return;
      }

      this.message = "ERR";
      this.classStyle = "red flash";
      this.windowClass = "red";
      this.status.description = connectionStatusDisconnected + ". Error: " + e;
    },
    failed(e) {
      ctx.connector.inputting = false;

      if (e.code) {
        this.message = "E" + e.code;
      } else {
        this.message = "E????";
      }

      this.classStyle = "red flash";
      this.status.description = connectionStatusDisconnected + ". Error: " + e;
    }
  };
}
