/**
 * @author Martin Karkowski
 * @email m.karkowski@zema.de
 * @create date 2020-12-03 11:57:29
 * @modify date 2020-12-03 11:57:29
 * @desc [description]
 */

import React from "react";
import { Badge, Card, Col, ProgressBar, Row, Table } from "react-bootstrap";
import {
  ENopeDispatcherStatus,
  INopeDispatcher
} from "../../lib/types/nope/nopeDispatcher.interface";
import { INopeObserver } from "../../lib/types/nope/nopeObservable.interface";

/**
 * Component used to render the Status of the Memory of a Host.
 */
class MemoryStatusOfHostComponent extends React.Component<
  {
    value: number;
  },
  {}
> {
  render() {
    // Determine the Now Value.
    const now = Math.round(1000 - this.props.value * 1000) / 10;
    const colorRange = {
      "0": "success",
      "30": "success",
      "75": "warning",
      "90": "danger",
      "101": "danger"
    };

    // Determine the Color, Therefore filter the "keys" based on the now Value.
    const color =
      colorRange[
        Object.getOwnPropertyNames(colorRange).filter(
          (value) => now < parseFloat(value)
        )[0]
      ];

    // Render
    return (
      <>
        <ProgressBar now={now} label={`${now} %`} variant={color} />
      </>
    );
  }
}

class HostStatusComponent extends React.Component<
  {
    status: {
      cpu: string;
      cores: number;
      os: string;
      ram: number;
      name: string;
      pids: {
        pid: number;
        dispatchers: {
          id: string;
          status: ENopeDispatcherStatus;
          timestamp: number;
        }[];
      }[];
      status: ENopeDispatcherStatus;
      timestamp: number;
    };
  },
  {
    lastUpdate: string;
    variant: "success" | "warning" | "danger";
    badge: boolean;
    badgeText: string;
  }
> {
  protected _intervall: any = null;

  get _state() {
    const dict = {
      0: "success",
      1: "info",
      2: "warning",
      3: "danger"
    };

    return {
      lastUpdate: new Date(this.props.status.timestamp).toISOString(),
      variant: dict[this.props.status.status],
      badge: this.props.status.status !== ENopeDispatcherStatus.HEALTHY,
      badgeText: ENopeDispatcherStatus[this.props.status.status]
    };
  }

  constructor(props) {
    super(props);
    this.state = this._state;
  }

  componentDidMount() {
    const _this = this;
    this._intervall = setInterval(() => {
      _this.setState(this._state);
    }, 200);
  }

  componentWillUnmount() {
    if (this._intervall) {
      clearInterval(this._intervall);
    }
  }

  render() {
    return (
      <Card border={this.state.variant}>
        <Card.Body>
          <Card.Title>
            {" "}
            {this.state.badge ? (
              <>
                {this.props.status.name}{" "}
                <Badge variant={this.state.variant}>
                  ELEMENT {this.state.badgeText} !
                </Badge>{" "}
              </>
            ) : (
              <>{this.props.status.name}</>
            )}{" "}
          </Card.Title>
          <Table bordered>
            <tbody>
              {this.props.status.ram > 0 ? (
                <tr>
                  <td>RAM</td>
                  <td>
                    {" "}
                    {/* Rendert the Memory */}
                    <MemoryStatusOfHostComponent
                      value={this.props.status.ram}
                    ></MemoryStatusOfHostComponent>
                  </td>
                </tr>
              ) : (
                <></>
              )}
              <tr>
                <td>CPU</td>
                <td>{this.props.status.cpu}</td>
              </tr>
              <tr>
                <td>Cores</td>
                <td>{this.props.status.cores}</td>
              </tr>
              <tr>
                <td>Platform</td>
                <td>{this.props.status.os}</td>
              </tr>
              <tr>
                <td>Nope-Processes</td>
                <td>{this.props.status.pids.length}</td>
              </tr>
            </tbody>
          </Table>
        </Card.Body>
        <Card.Footer>
          <small className="text-muted">
            Last updated <b>{this.state.lastUpdate}</b>
          </small>
        </Card.Footer>
      </Card>
    );
  }
}

class AvailableDispatchers extends React.Component<
  { dispatcher: INopeDispatcher },
  {
    dispatchers: {
      cpu: string;
      cores: number;
      os: string;
      ram: number;
      name: string;
      pids: {
        pid: number;
        dispatchers: {
          id: string;
          status: ENopeDispatcherStatus;
          timestamp: number;
        }[];
      }[];
    }[][];
    connected: boolean;
  }
> {
  private _observer: INopeObserver[] = [];

  private __sort() {
    const sorted: {
      [index: string]: {
        cpu: string;
        cores: number;
        os: string;
        ram: number;
        name: string;
        pids: {
          [index: string]: {
            id: string;
            status: ENopeDispatcherStatus;
            timestamp: number;
          }[];
        };
        status: ENopeDispatcherStatus;
        timestamp: number;
      };
    } = {};
    const ret: {
      cpu: string;
      cores: number;
      os: string;
      ram: number;
      name: string;
      pids: {
        pid: number;
        dispatchers: {
          id: string;
          status: ENopeDispatcherStatus;
          timestamp: number;
        }[];
      }[];
      status: ENopeDispatcherStatus;
      timestamp: number;
    }[] = [];

    for (const dispatcher of this.props.dispatcher.externalDispatchers.getContent()) {
      if (sorted[dispatcher.host.name] === undefined) {
        sorted[dispatcher.host.name] = {
          cores: dispatcher.host.cores,
          cpu: dispatcher.host.cpu,
          name: dispatcher.host.name,
          os: dispatcher.host.os,
          pids: {},
          ram: dispatcher.host.ram,
          status: ENopeDispatcherStatus.HEALTHY,
          timestamp: dispatcher.timestamp
        };
      }
      if (sorted[dispatcher.host.name].pids[dispatcher.pid] === undefined) {
        sorted[dispatcher.host.name].pids[dispatcher.pid] = [];
      }

      sorted[dispatcher.host.name].status = Math.max(
        sorted[dispatcher.host.name].status,
        dispatcher.status
      );
      sorted[dispatcher.host.name].timestamp = Math.min(
        sorted[dispatcher.host.name].timestamp,
        dispatcher.timestamp
      );

      // Store the Dispatcher Status
      sorted[dispatcher.host.name].pids[dispatcher.pid].push({
        id: dispatcher.id,
        status: dispatcher.status,
        timestamp: dispatcher.timestamp
      });
    }

    for (const hostname in sorted) {
      const _toAdd: any = sorted[hostname];
      _toAdd.pids = Object.getOwnPropertyNames(sorted[hostname].pids).map(
        (pid) => {
          return {
            pid,
            dispatchers: sorted[hostname].pids[pid]
          };
        }
      );
      ret.push(_toAdd);
    }

    // console.log(this.props.dispatcher.externalDispatchers.getContent().length,ret.map(item => `${item.name}:${item.status}`).toString());

    return ret;
  }

  private __refresh__() {
    const connected = this.props.dispatcher.communicator.connected.getContent();
    const dispatchers = [];
    const maxItems = 3;
    const sorted = this.__sort();

    // Function to Split the Dispatcher Array into an array containing max 3 elements.
    for (let i = 0; i < sorted.length; i = i + maxItems) {
      dispatchers.push(sorted.slice(i, i + maxItems));
    }

    this.setState({
      dispatchers: connected ? dispatchers : [],
      connected
    });
  }

  /**
   * Function will be called if the Item has been rendered sucessfully.
   */
  componentDidMount() {
    if (this.props.dispatcher) {
      // Subscribe to the Instances.
      const _this = this;

      this._observer.push(
        this.props.dispatcher.externalDispatchers.subscribe(() => {
          _this.__refresh__();
        })
      );

      this._observer.push(
        this.props.dispatcher.communicator.connected.subscribe(() => {
          _this.__refresh__();
        })
      );

      // Render the Elements
      this.__refresh__();
    }
  }

  /**
   * Function, that will be called before the network fails.
   */
  componentWillUnmount() {
    // Call the unmount
    for (const _observer of this._observer) {
      _observer.unsubscribe();
    }
  }

  constructor(props) {
    super(props);
    this.state = {
      dispatchers: [],
      connected: false
    };
  }

  public render() {
    return (
      <>
        {this.state.dispatchers.map((row, idx_01) => {
          return (
            <Row
              key={idx_01}
              style={{ paddingTop: "0.5rem", paddingBottom: "0.5rem" }}
            >
              {row.map((item, idx_02) => {
                return (
                  <Col key={`${idx_01}_${idx_02}`} sm={4}>
                    <HostStatusComponent status={item}></HostStatusComponent>
                  </Col>
                );
              })}
            </Row>
          );
        })}
      </>
    );
  }
}

export default AvailableDispatchers;
