import CableDarkIcon from "assets/components/grainCableDark.png";
import CableLightIcon from "assets/components/grainCableLight.png";
import { GraphPoint, GraphType } from "common/Graph";
import { convertedUnitMeasurement } from "models/UnitMeasurement";
import moment from "moment";
import {
  AreaChartData,
  ComponentMeasurement,
  ComponentTypeExtension,
  GraphFilters,
  LineChartData,
  simpleAreaChartData,
  simpleLineChartData,
  Summary,
  unitMeasurementSummaries
} from "pbHelpers/ComponentType";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { or } from "utils/types";

interface Node {
  groundRef: number;
  selfRef: number;
}

function extractNodes(measurement: quack.IMeasurement | null | undefined): Node[] {
  let measurements: Node[] = [];
  if (measurement && measurement.capacitorCable) {
    for (let i = 0; i < measurement.capacitorCable.groundRefFf.length; i++) {
      measurements.push({
        groundRef: measurement.capacitorCable.groundRefFf[i],
        selfRef: measurement.capacitorCable.selfRefFf[i]
      });
    }
  }

  return measurements;
}

export function CapacitorCable(subtype: number = 0): ComponentTypeExtension {
  let capacitance = describeMeasurement(
    quack.MeasurementType.MEASUREMENT_TYPE_CAPACITANCE,
    quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE,
    subtype
  );
  let addressTypes = [quack.AddressType.ADDRESS_TYPE_I2C];
  if (subtype === quack.CapacitorCableSubtype.CAPACITOR_CABLE_SUBTYPE_FROG) {
    addressTypes = [quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY];
  }
  return {
    type: quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE,
    subtypes: [
      {
        key: quack.CapacitorCableSubtype.CAPACITOR_CABLE_SUBTYPE_NONE,
        value: "CAPACITOR_CABLE_SUBTYPE_NONE",
        friendlyName: "Capacitor Cable"
      },
      {
        key: quack.CapacitorCableSubtype.CAPACITOR_CABLE_SUBTYPE_FROG,
        value: "CAPACITOR_CABLE_SUBTYPE_FROG",
        friendlyName: "Capacitor Cable FDC"
      }
    ],
    friendlyName: "Capacitor Cable",
    description: "Measures Capacitance",
    isController: false,
    isSource: true,
    isArray: true,
    isCalibratable: false,
    addressTypes: addressTypes,
    interactionResultTypes: [],
    states: [],
    measurements: [
      {
        measurementType: quack.MeasurementType.MEASUREMENT_TYPE_CAPACITANCE,
        label: capacitance.label() + " ground Ref",
        graphType: GraphType.AREA,
        colour: capacitance.colour(),
        extract: function(measurement: quack.Measurement, filters?: GraphFilters): any {
          let groundRefs = extractNodes(measurement).map(n => n.groundRef);

          let groundMeasurements: any = {
            low: Math.min(...groundRefs),
            high: Math.max(...groundRefs)
          };

          groundRefs.forEach((measurement, i) => {
            groundMeasurements["node" + i] = measurement;
          });

          return groundMeasurements;
        },
        isErrorMeasurement: (measurement: quack.Measurement): boolean => {
          //TODO determine what the error value would be
          return false;
        }
      } as ComponentMeasurement,
      {
        measurementType: quack.MeasurementType.MEASUREMENT_TYPE_CAPACITANCE,
        label: capacitance.label() + " self Ref",
        graphType: GraphType.AREA,
        colour: capacitance.colour(),
        extract: function(measurement: quack.Measurement, filters?: GraphFilters): any {
          let selfRefs = extractNodes(measurement).map(n => n.selfRef);
          let selfMeasurements: any = {
            low: Math.min(...selfRefs),
            high: Math.max(...selfRefs)
          };

          selfRefs.forEach((measurement, i) => {
            selfMeasurements["node" + i] = measurement;
          });

          return selfMeasurements;
        },
        isErrorMeasurement: (measurement: quack.Measurement): boolean => {
          //TODO determine what the error value would be
          return false;
        }
      } as ComponentMeasurement
    ],
    measurementSummary: async function(
      measurement: quack.Measurement,
      filters: GraphFilters
    ): Promise<Array<Summary>> {
      if (!measurement || !measurement.capacitorCable || !filters) {
        Promise.reject();
      }
      let summary: Array<Summary> = [];
      let nodes = extractNodes(measurement);
      let formattedGround = "";
      let formattedSelf = "";

      formattedGround = "[" + nodes.map(n => n.groundRef.toString()).join(", ") + "]";
      formattedSelf = "[" + nodes.map(n => n.selfRef.toString()).join(", ") + "]";

      summary.push({
        label: "Capacitance(GroundRef)",
        value: formattedGround,
        colour: "rgb(149, 117, 205)"
      } as Summary);
      summary.push({
        label: "Capacitance(SelfRef)",
        value: formattedSelf,
        colour: "rgb(149, 117, 205)"
      } as Summary);
      return Promise.resolve(summary);
    },
    unitMeasurementSummary: (
      measurements: convertedUnitMeasurement
    ): Summary[] => {
      return unitMeasurementSummaries(
        measurements,
        quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE,
        subtype
      );
    },
    areaChartData: (
      measurement: pond.UnitMeasurementsForComponent,
      smoothingAverages?: number,
      filters?: GraphFilters
    ): AreaChartData => {
      return simpleAreaChartData(measurement, smoothingAverages, filters);
    },
    lineChartData: (
      measurement: pond.UnitMeasurementsForComponent,
      smoothingAverages?: number,
      filters?: GraphFilters
    ): LineChartData => {
      return simpleLineChartData(
        quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE,
        measurement,
        smoothingAverages,
        filters
      );
    },
    minMeasurementPeriodMs: 1000,
    icon: (theme?: "light" | "dark"): string | undefined => {
      return theme === "light" ? CableDarkIcon : CableLightIcon;
    }
  };
}

//TODO: deprecated function with new measurements
export function multilineCapCableData(
  measurements: Array<pond.Measurement>,
  filters?: GraphFilters
) {
  let groundRefs: Array<Array<GraphPoint>> = [];
  let selfRefs: Array<Array<GraphPoint>> = [];
  measurements.forEach((measurement: pond.Measurement, i) => {
    let nodes = extractNodes(measurement.measurement);
    let selectedNodes = filters ? or(filters.selectedNodes, []) : [];
    for (let j = 0; j < selectedNodes.length; j++) {
      if (i === 0) {
        groundRefs[j] = [];
        selfRefs[j] = [];
      }
      let nodeIndex = selectedNodes[j];
      if (nodeIndex < nodes.length) {
        let node = nodes[nodeIndex];
        let ts = moment(measurement.timestamp);
        groundRefs[j].push({ x: ts, y: node.groundRef });
        selfRefs[j].push({ x: ts, y: node.selfRef });
      }
    }
  });
  return { data1: groundRefs, data2: selfRefs };
}
