/**
 * @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 { faHashtag, faHome } from "@fortawesome/free-solid-svg-icons";
import React from "react";
import {
  Alert,
  ButtonGroup,
  Card,
  Col,
  Container,
  Row,
  Spinner,
  Table,
  ToggleButton
} from "react-bootstrap";
import { INopeDispatcher } from "../../lib/types/nope/nopeDispatcher.interface";
import { INopeModule } from "../../lib/types/nope/nopeModule.interface";
import { INopeObserver } from "../../lib/types/nope/nopeObservable.interface";
import { IReadableIos } from "../../modules/generic-plc/type/interfaces";
import { IBeckhoffPLC } from "../../modules/mod-Beckhoff-PLC-Interface/type/interfaces";
import Toolbar from "../../resources/ui/layout/toolbar";

class BeckhoffComponent extends React.Component<
  { instance: IBeckhoffPLC & INopeModule },
  {
    intialized: boolean;
    connected: boolean;
    remote;
    inputs: IReadableIos[];
    outputs: IReadableIos[];
  }
> {
  constructor(props) {
    super(props);
    this.state = {
      connected: false,
      intialized: false,
      remote: "unkown",
      inputs: [],
      outputs: []
    };
  }

  observers: INopeObserver[] = [];

  componentDidMount() {
    if (this.props.instance) {
      const _this = this;

      // Subscribe to the Oberservers
      // which will influence the visuals.
      this.observers.push(
        this.props.instance.connected.subscribe((connected) => {
          _this.setState({ connected });
        })
      );
      this.observers.push(
        // Wait for the System to Initialize.
        // If done, define the Remote-String.
        this.props.instance.initialized.subscribe((intialized) => {
          _this.setState({
            intialized,
            remote:
              _this.props.instance.adsOptions.getContent()?.hostUri +
              ":" +
              _this.props.instance.adsOptions.getContent()?.port
          });
        })
      );

      // Subscribe to the IO-Elements of the
      // Module.
      this.observers.push(
        this.props.instance.readableIos.subscribe((ios) => {
          _this.setState({
            // Assign the Inputs
            inputs: ios.filter((io) => io.type === "input"),
            // Assign the Outputs
            outputs: ios.filter((io) => io.type === "output")
          });
        })
      );

      // Initially define the Remote.
      this.setState({
        remote:
          this.props.instance.adsOptions.getContent()?.hostUri +
          ":" +
          this.props.instance.adsOptions.getContent()?.port
      });
    }
  }

  render() {
    return (
      <Card border={this.state.connected ? "success" : "danger"}>
        <Card.Header>
          <b>PLC: </b>
          <code>{this.props.instance.identifier}</code>{" "}
        </Card.Header>
        <Card.Body>
          {!this.state.intialized ? (
            <Alert variant="warning">
              <Alert.Heading>
                <Spinner animation="border" /> Grabbing Configuration
              </Alert.Heading>
              <p>Trying to extract the Configuration online.</p>
            </Alert>
          ) : (
            <Table striped bordered hover>
              <thead>
                <tr>
                  <th>Module</th>
                  <th>Port</th>
                  <th>Value</th>
                </tr>
              </thead>
              <tbody>
                {/* Currently Render the Inputs Only. */}
                {this.state.inputs.map((io, idx) => (
                  <tr key={"input" + idx}>
                    <td>{io.orginalName}</td>
                    <td>{io.dataType}</td>
                    <td>{io.currentValue}</td>
                  </tr>
                ))}
                {this.state.outputs
                  .filter((io) => io.dataType === "boolean")
                  .map((io, idx) => {
                    <tr key={"output" + idx}>
                      <td>{io.orginalName}</td>
                      <td>{io.dataType}</td>
                      <td>
                        <ButtonGroup>
                          <ToggleButton
                            key={idx}
                            type="radio"
                            variant="danger"
                            value={false}
                            checked={io.currentValue === false}
                            // onChange={(e) => setRadioValue(e.currentTarget.value)}
                          >
                            Off
                          </ToggleButton>
                          <ToggleButton
                            key={idx}
                            type="radio"
                            variant="sucess"
                            value={true}
                            checked={io.currentValue === true}
                            // onChange={(e) => setRadioValue(e.currentTarget.value)}
                          >
                            On
                          </ToggleButton>
                        </ButtonGroup>
                      </td>
                    </tr>;
                  })}
              </tbody>
            </Table>
          )}
        </Card.Body>
        {this.state.connected ? (
          ""
        ) : (
          <Card.Footer>
            Waiting for the plc to accept the connection. Host of the PLC is{" "}
            <code>{this.state.remote}</code>
          </Card.Footer>
        )}
      </Card>
    );
  }
}

class BeckhoffOverviewComponent extends React.Component<
  { dispatcher: INopeDispatcher },
  { instances: (IBeckhoffPLC & INopeModule)[]; connected: boolean }
> {
  private _observer: INopeObserver[] = [];

  private async __refresh() {
    const connected = this.props.dispatcher.communicator.connected.getContent();
    const modules = this.props.dispatcher.availableInstances
      .getContent()
      .filter((mod) => mod.type == "BeckhoffPlc");

    const plcs: (IBeckhoffPLC & INopeModule)[] = [];
    for (const plc of modules) {
      const mod = await this.props.dispatcher.generateInstance<
        IBeckhoffPLC & INopeModule
      >({
        identifier: plc.identifier,
        type: plc.type
      });

      plcs.push(mod);
    }

    // Dispose the old instances
    for (const plc of this.state.instances || []) {
      await plc.dispose();
    }

    this.setState({
      instances: plcs,
      connected
    });
  }

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

      this.__refresh();

      const _this = this;

      this._observer.push(
        this.props.dispatcher.availableInstances.subscribe(() => {
          _this.__refresh().catch(console.error);
        })
      );

      this._observer.push(
        this.props.dispatcher.communicator.connected.subscribe(() => {
          _this.__refresh().catch(console.error);
        })
      );
    }
  }

  /**
   * 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 = {
      instances: [],
      connected: false
    };
  }

  public render() {
    const idx = 0;
    return (
      <Container>
        <Toolbar<undefined>
          toolbar={{
            items: [
              {
                icon: faHome,
                label: "root",
                ref: "/",
                type: "link"
              },
              {
                type: "link",
                ref: "/docs",
                label: "docs",
                icon: faHashtag
              }
            ]
          }}
          generateData={() => undefined}
          brand={{
            ref: "",
            type: "link",
            icon:
              "https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Beckhoff_Logo.svg/1024px-Beckhoff_Logo.svg.png",
            label: ""
          }}
        ></Toolbar>
        <Row>
          <Col>
            {this.state.connected ? (
              <Alert variant={"success"}>Backend online</Alert>
            ) : (
              <>
                <Alert variant={"danger"}>
                  Not able to connect to the Backend <b>:'(</b>
                </Alert>
                <p>
                  Please start a Nope-Backend with the following command:{" "}
                  <code>
                    node .\dist\lib\cli\runNopeBackend.js -c io-server
                  </code>
                </p>
              </>
            )}
          </Col>
        </Row>
        <Row>
          <Col>
            {/* Available PLCs */}
            <Table striped bordered hover>
              <thead>
                <tr>
                  <th>PLC-Module-Name</th>
                  <th>Type</th>
                </tr>
              </thead>
              <tbody>
                {/* Create the PLC instances. */}
                {this.state.instances.map((instance, idx) => {
                  return (
                    <tr key={idx++}>
                      <td>{instance.identifier}</td>
                      <td>
                        <code>{instance.type}</code>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </Table>
          </Col>
        </Row>
        {this.state.instances.map((instance, idx) => {
          return (
            <Row key={idx} style={{ paddingTop: "1rem" }}>
              <Col>
                <BeckhoffComponent instance={instance}></BeckhoffComponent>
              </Col>
            </Row>
          );
        })}
      </Container>
    );
  }
}

export default BeckhoffOverviewComponent;
