/**
 * @author Martin Karkowski
 * @email m.karkowski@zema.de
 * @create date 2021-02-11 08:02:28
 * @modify date 2021-02-11 13:45:57
 * @desc [description]
 */

import React from "react";
import { Alert, Col, Container, Row, Table } from "react-bootstrap";
import {
  nopeDispatcherManager
} from "../../lib/dispatcher/nopeDispatcherManager";
import { ENopeDispatcherStatus } from "../../lib/types/nope/nopeDispatcher.interface";
import { INopeObserver } from "../../lib/types/nope/nopeObservable.interface";
import { StatusBadgeComponent } from "./generic/StatusBadge";
import { InstanceDetailsComponent, InstanceDetailsState } from "./instance/InstanceDetails";

export class InstanceOverviewComponent extends React.Component<
  { dispatcher: nopeDispatcherManager; maxItemsInRow?: 2 | 3 | 4, renderDetails?: boolean },
  { connected: boolean; instances: InstanceDetailsState[][], maxItemsInRow: number }
> {
  /**
   * List containing Observables
   *
   * @private
   * @type {INopeObserver[]}
   * @memberof InstanceOverviewComponent
   */
  private _observer: INopeObserver[] = [];

  /**
   * Function, which will be used to refresh the Nodes.
   */
  private _refresh() {
    if (this.props.dispatcher?.getHostInfos) {
      // Get the connected Flag
      const connected = this.props.dispatcher.communicator.connected.getContent();
      const computingNodes = this.props.dispatcher.getHostInfos();
      const computingNodesAsList = Object.getOwnPropertyNames(computingNodes).map(key => computingNodes[key]);
      const allInstances = this.props.dispatcher.availableInstances.getContent();
      console.log(allInstances.length);
      const mappingIndexToName = new Map<string,number>();
      const instances:  InstanceDetailsState[] = allInstances.map((item,idx) => {

        mappingIndexToName.set(item.identifier,idx);

        return Object.assign({
          status: ENopeDispatcherStatus.HEALTHY,
          dispatchers: [],
          redundant: false,
        }, item);
      });

      for (const node of computingNodesAsList){
        for (const instance of node.instances){
          if (mappingIndexToName.has(instance.identifier)){
            // Add the Dispatcher, update the Status of the Component            
            instances[mappingIndexToName.get(instance.identifier)].status = Math.max(instances[mappingIndexToName.get(instance.identifier)].status, instance.status);
            instances[mappingIndexToName.get(instance.identifier)].dispatchers.push(instance.dispatcher);
            instances[mappingIndexToName.get(instance.identifier)].redundant = instances[mappingIndexToName.get(instance.identifier)].dispatchers.length > 1;
          }
        }
      }  

      const renderingInstances: InstanceDetailsState[][] = [];

      const maxItems = this.props.renderDetails
        ? this.props.maxItemsInRow || 2
        : instances.length * 2 ;

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

      this.setState({
        instances: renderingInstances,
        connected,
        maxItemsInRow: maxItems
      });
    }
  }

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

      this._observer.push(
        this.props.dispatcher.availableInstances.subscribe(() => {
          console.log("HERE");
          _this._refresh();
        })
      );

      this._observer.push(
        this.props.dispatcher.externalDispatchers.subscribe(() => {
          console.log("HERE");
          _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.
   */
  public componentWillUnmount(): void {
    // Call the unmount
    for (const _observer of this._observer) {
      _observer.unsubscribe();
    }
  }

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

  public render() {
    if (this.state.connected) {
      
      if (this.props.renderDetails) {
        const sm = Math.round(12 / this.state.maxItemsInRow);
        return (
          <Container>
            {this.state.instances.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={sm}>
                        <InstanceDetailsComponent description={item} renderDetails={true}></InstanceDetailsComponent>
                      </Col>
                    );
                  })}
                </Row>
              );
            })}
          </Container>
        );
      }

      if (this.state.instances.length > 0) {
        return (
          <Table striped bordered hover>
            <thead>
              <tr>
                <th>Name</th>
                <th>Type</th>
                <th>Status</th>
                <th>Redundant</th>
              </tr>
            </thead>
            <tbody>
              {this.state.instances[0].map((item, idx) => (
                <tr key={idx}>
                  <td>{item.identifier}</td>
                  <td><code>{item.type}</code></td>
                  <td>
                    <StatusBadgeComponent
                      status={item.status}
                    ></StatusBadgeComponent>
                  </td>
                  <td>
                    {item.redundant.toString()}
                  </td>
                </tr>
              ))}
            </tbody>
          </Table>
        );
      }
      
      return (
        <Alert variant={"warning"}>
            No Instance Found <b>:'(</b>
        </Alert>
      );



      
    } else {
      return (
        <>
          <Alert variant={"danger"}>
            Not able to connect to the Backend <b>:'(</b>
          </Alert>
          <p>
            Please start a Nope-Backend with the following command:{" "}
            <code>nope run -c io-server</code>
          </p>
        </>
      );
    }
  }
}
