import {
  blue,
  brown,
  deepOrange,
  deepPurple,
  green,
  grey,
  lightGreen,
  orange,
  purple,
  red,
  teal,
  yellow
} from "@mui/material/colors";
import { GraphType } from "common/Graph";
import { IsCardController } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { getTextSecondary } from "theme/text";
// import { getTextSecondary } from "theme";
import { getDistanceUnit, getPressureUnit, getTemperatureUnit, roundTo } from "utils";
import { extract } from "utils/types";

interface Enumeration {
  label: string;
  colour: any;
}

interface NodeDetails {
  labels: string[];
  colours: string[];
}

interface MeasurementDetails {
  label: string;
  unit: string;
  factor: number;
  complexConversion: ((value: number, mode: "toStored" | "toDisplay") => number) | null;
  multiply: boolean;
  spacer: boolean;
  enumerations: Enumeration[];
  colour: any;
  decimals: number;
  path: string;
  graph: GraphType;
  min: number;
  max: number;
  step: number;
  errorValue?: number;
  nodeDetails?: NodeDetails;
}

function enumeration(label: string, colour: any) {
  return { label, colour };
}
/**
 * This is used to determine the measurements that will be shown on the y axis of the graph as well as the measurements table
 */
export class MeasurementDescriber {
  private measurementType: quack.MeasurementType;
  private details: MeasurementDetails;

  constructor(
    measurementType: quack.MeasurementType,
    componentType: quack.ComponentType,
    componentSubtype: number,
    product?: pond.DeviceProduct
  ) {
    this.measurementType = measurementType;
    this.details = {
      label: "Unknown",
      unit: "",
      factor: 1.0,
      complexConversion: null,
      multiply: false,
      spacer: false,
      enumerations: [],
      colour: "",
      decimals: 0,
      path: "",
      graph: GraphType.LINEAR,
      min: 0,
      max: 100,
      step: 1
    };
    switch (measurementType) {
      case quack.MeasurementType.MEASUREMENT_TYPE_INVALID:
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE:
        let temperatureUnit = getTemperatureUnit();
        let isFahrenheit = temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT;
        this.details.label = "Temperature";
        this.details.unit = isFahrenheit ? "°F" : "°C";
        this.details.factor = 10.0;
        this.details.complexConversion = isFahrenheit
          ? (value: number, mode: "toStored" | "toDisplay"): number => {
              if (mode === "toStored") {
                return ((value - 32.0) / (9.0 / 5.0)) * this.details.factor;
              }

              return (value / this.details.factor) * (9.0 / 5.0) + 32.0;
            }
          : null;
        this.details.colour = orange["500"];
        this.details.decimals = 2;
        this.details.path = "temperature.celciusTimes10";
        this.details.errorValue = -127;
        if (componentType === quack.ComponentType.COMPONENT_TYPE_DHT) {
          this.details.path = "humidityAndTemperature.celciusTimes10";
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) {
          this.details.graph = GraphType.AREA;
          if(componentSubtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG){
            this.details.label = "Cable ID"
            this.details.unit = ""
          }
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT) {
          this.details.path = "vpd.celciusTimes10";
          if (componentSubtype === quack.DHTSubtype.DHT_SUBTYPE_22) {
            this.details.decimals = 1;
          }
        }
        if (IsCardController(product)) {
          this.details.min = isFahrenheit ? 32 : 0;
          this.details.max = isFahrenheit ? 113 : 45;
        } else {
          this.details.min = isFahrenheit ? -40 : -40;
          this.details.max = isFahrenheit ? 212 : 100;
        }
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN:
        this.details.label = "State";
        this.details.enumerations = [enumeration("Off", "#f9a825"), enumeration("On", "#f9a825")];
        this.details.path = "booleanOutput.value";
        this.details.colour = "#f9a825";
        this.details.min = 0;
        this.details.max = 1;
        if (componentType === quack.ComponentType.COMPONENT_TYPE_BOOLEAN_INPUT) {
          this.details.path = "booleanInput.value";
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_EDGE_TRIGGERED) {
          this.details.path = "edgeTriggered.state";
          this.details.graph = GraphType.SCATTER;
          if (componentSubtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_DOOR) {
            this.details.enumerations = [
              enumeration("Closed", "var(--status-ok)"),
              enumeration("Open", "var(--status-alert)")
            ];
          } else if (componentSubtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_WATER) {
            this.details.colour = "#2196f3";
            this.details.enumerations = [
              enumeration("Stopped", getTextSecondary()),
              enumeration("Flowing", "#2196f3")
            ];
          } else {
            this.details.enumerations = [
              enumeration("OK", "var(--status-ok)"),
              enumeration("Triggered", "var(--status-alert)")
            ];
          }
        }
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_VOLTAGE:
        this.details.label = "Voltage";
        this.details.unit = "V";
        this.details.factor = 10.0;
        this.details.colour = deepOrange["700"];
        this.details.path = "voltage.microvolts";
        this.details.decimals = 3;
        if (componentType === quack.ComponentType.COMPONENT_TYPE_POWER) {
          this.details.path = "power.inputVoltageTimes10";
          this.details.decimals = 1;
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_DRAGER_GAS_DONGLE) {
          this.details.unit = "mV";
          this.details.graph = GraphType.MULTILINE;
          this.details.unit = "mV";
          // this.details.nodeDetails = {
          //   colours: ["white", "black", "red", "blue"],
          //   labels: ["millivolt1", "millivolt2", "millivolt3", "millivolt4"]
          // };
        }
        this.details.max = 30;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_PERCENT:
        this.details.label = "Percent";
        this.details.unit = "%";
        this.details.errorValue = 200;
        this.details.factor = 100.0;
        this.details.colour = teal["500"];
        this.details.decimals = 2;
        if (componentType === quack.ComponentType.COMPONENT_TYPE_DHT) {
          this.details.label = "Humidity";
          this.details.path = "humidityAndTemperature.relativeHumidityTimes100";
          this.details.decimals = 1;
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) {
          this.details.label = "Humidity";
          this.details.graph = GraphType.AREA;
          if(componentSubtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG){
            this.details.label = "Cable Type"
            this.details.unit = ""
          }
          // can be used for testing node splitting on a cable if you dont have a component that uses it
          // this.details.nodeDetails = {
          //   colours: ["white", "black"],
          //   labels: ["hum1", "hum2"]
          // };
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_MODEM) {
          this.details.label = "Quality";
          this.details.factor = 2.0;
          this.details.path = "modemSignal.quality";
          this.details.colour = orange["500"];
          this.details.multiply = true;
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_POWER) {
          this.details.label = "Charged";
          this.details.path = "power.percentChargedTimes100";
          this.details.colour = lightGreen["600"];
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT) {
          this.details.label = "Humidity";
          this.details.path = "vpd.relativeHumidityTimes100";
          if (componentSubtype === quack.DHTSubtype.DHT_SUBTYPE_22) {
            this.details.decimals = 1;
          }
        }
        if (componentType === quack.ComponentType.COMPONENT_TYPE_SEN5X) {
          this.details.label = "Humidity";
        }
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE:
        let pressureUnit = getPressureUnit();
        let isIWG = pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER;
        this.details.label = "Pressure";
        this.details.unit = isIWG ? "iwg" : "kPa";
        this.details.factor = isIWG ? 248.84 : 1000.0;
        this.details.spacer = true;
        this.details.colour = deepPurple["300"];
        this.details.path = "pressure.pascals";
        this.details.decimals = 2;
        if (componentType === quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT) {
          this.details.path = "vpd.pascals";
          if (componentSubtype === quack.DHTSubtype.DHT_SUBTYPE_22) {
            this.details.decimals = 1;
          }
        } else if (componentType === quack.ComponentType.COMPONENT_TYPE_PRESSURE_CABLE) {
          this.details.path = "pressureCable.pascals";
          this.details.graph = GraphType.AREA;
        }
        this.details.max = isIWG ? 20 : 5;
        this.details.step = 0.1;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_RSSI:
        this.details.label = "Strength";
        this.details.unit = "dBm";
        this.details.spacer = true;
        this.details.colour = yellow["500"];
        this.details.path = "modemSignal.rssi";
        this.details.min = -150;
        this.details.max = 0;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_LIGHT:
        this.details.label = "Illuminance";
        this.details.unit = "lux";
        this.details.factor = 100.0;
        this.details.spacer = true;
        this.details.colour = orange["500"];
        this.details.path = "light.luxTimes100";
        this.details.decimals = 2;
        this.details.max = 20000;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_PPB:
        this.details.label = "Concentration";
        this.details.spacer = true;
        this.details.unit = "ppb";
        this.details.colour = green["500"];
        this.details.decimals = 3;
        this.details.max = 300;
        this.details.path = "concentration.ppb";
        switch (componentType) {
          case quack.ComponentType.COMPONENT_TYPE_CALCIUM:
            this.details.path = "concentration.ppb";
            this.details.colour = teal["500"];
            break;
          case quack.ComponentType.COMPONENT_TYPE_NITRATE:
            this.details.path = "concentration.ppb";
            this.details.colour = green["500"];
            break;
          case quack.ComponentType.COMPONENT_TYPE_POTASSIUM:
            this.details.path = "concentration.ppb";
            this.details.colour = yellow["500"];
            break;
          case quack.ComponentType.COMPONENT_TYPE_ETHYLENE:
            this.details.colour = red["300"];
            this.details.max = 500000;
            this.details.path = "gas.ppb";
            break;
          case quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY:
            this.details.path = "gas.ppb";
            this.details.label = "TVOC";
            this.details.max = 60000;
            this.details.decimals = 1;
            this.details.colour = green["300"];
            break;
          default:
            break;
        }
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_PPM:
        this.details.label = "Gas";
        this.details.spacer = true;
        this.details.unit = "ppm";
        this.details.path = "gas.ppm";
        this.details.colour = brown["300"];
        switch (componentType) {
          case quack.ComponentType.COMPONENT_TYPE_CO2:
            this.details.max = 5000;
            break;
          case quack.ComponentType.COMPONENT_TYPE_O2:
            this.details.max = 500000;
            break;
          case quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY:
            this.details.label = "eCO2";
            this.details.max = 60000;
            break;
          case quack.ComponentType.COMPONENT_TYPE_DRAGER_GAS_DONGLE:
            this.details.graph = GraphType.MULTILINE;
            switch (componentSubtype) {
              case quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_DEBUG_VOLTAGE:
                this.details.label = "Voltage";
                this.details.unit = "mV";
                break;
              case quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_O2:
              case quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_LEL:
                this.details.colour = blue["400"];
                this.details.decimals = 1;
                this.details.unit = "%";
                this.details.complexConversion = (
                  value: number,
                  mode: "toStored" | "toDisplay"
                ): number => {
                  if (mode === "toStored") {
                    //for O2/LEL dragers the display is in percentage so convert the percent to ppm
                    return value * 10000;
                  }
                  return value / 10000;
                };
                break;
              case quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_NO2:
                this.details.colour = teal["500"];
                break;
              case quack.DragerGasDongleSubtype.DRAGER_GAS_DONGLE_SUBTYPE_CO:
                this.details.colour = grey["500"];
            }
            break;
          default:
            break;
        }
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_ANALOG:
        this.details.label = "Value";
        this.details.colour = orange["500"];
        this.details.path = "analogInput.value";
        this.details.max = 50;
        switch (componentType) {
          case quack.ComponentType.COMPONENT_TYPE_ANALOG_INPUT:
            if (componentSubtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL) {
              this.details.factor = 10.0;
              this.details.max = 100;
              this.details.min = 0;
              this.details.decimals = 1;
              this.details.label = "Fuel";
              this.details.unit = "%";
              this.details.colour = "#CC9933";
            } else if (
              componentSubtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_WIND_DIRECTION
            ) {
              this.details.label = "Wind Direction";
              this.details.colour = green["500"];
              this.details.graph = GraphType.RADAR;
            }
        }
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_ORP:
        this.details.label = "ORP";
        this.details.unit = "mV";
        this.details.factor = 1000.0;
        this.details.spacer = true;
        this.details.colour = blue["200"];
        this.details.path = "orp.microvolts";
        this.details.decimals = 3;
        this.details.max = 1000;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_PH:
        this.details.label = "pH";
        this.details.unit = "pH";
        this.details.factor = 100000.0;
        this.details.spacer = true;
        this.details.colour = purple["500"];
        this.details.path = "ph.phTimes100000";
        this.details.decimals = 3;
        this.details.min = 0;
        this.details.max = 14;
        this.details.step = 0.1;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_CONDUCTIVITY:
        this.details.label = "Conductivity";
        this.details.unit = "μS/cm";
        this.details.spacer = true;
        this.details.colour = orange["500"];
        this.details.path = "conductivity.microsiemensPerCm";
        this.details.decimals = 3;
        this.details.max = 5000;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_CAPACITANCE:
        this.details.label = "Capacitance";
        this.details.unit = "nF";
        this.details.path = "capacitance.picofarads";
        if (componentType === quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE) {
          this.details.unit = "fF";
        }
        this.details.factor = 1000.0;
        this.details.spacer = true;
        this.details.colour = orange["500"];
        this.details.decimals = 3;
        this.details.max = 100000;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_WEIGHT:
        this.details.label = "Weight";
        this.details.unit = "g";
        this.details.colour = green["500"];
        this.details.path = "weight.grams";
        this.details.max = 100000;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_DISTANCE_CM:
        let distanceUnit = getDistanceUnit();
        this.details.label = "Distance";
        this.details.unit = "cm";
        if (distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET) {
          this.details.unit = "ft";
        }
        if (distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_METERS) {
          this.details.unit = "m";
        }
        this.details.colour = blue["400"];
        this.details.path = "distance.cm";
        this.details.max = 100000;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_SPEED:
        let speedUnit = getDistanceUnit()
        this.details.label = "Speed";
        this.details.unit = speedUnit === pond.DistanceUnit.DISTANCE_UNIT_METERS ? "m/s" : "ft/m";
        this.details.colour = green["500"];
        this.details.path = "edgeTriggered.rises";
        this.details.max = 500;
        this.details.min = 0;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_RAIN:
        this.details.label = "Rain";
        this.details.unit = "mm";
        this.details.colour = blue["500"];
        this.details.path = "edgeTriggered.rises";
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_GRAIN_EMC:
        this.details.label = "Grain Moisture";
        this.details.unit = "% WB";
        this.details.decimals = 2;
        this.details.colour = yellow["500"];
        this.details.graph = GraphType.AREA;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_CFM:
        this.details.label = "Airflow";
        this.details.unit = "CFM";
        this.details.colour = blue["300"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_CAPACITANCE_GND_FF:
        this.details.label = "Capacitance(Ground Ref)";
        this.details.unit = "fF";
        this.details.colour = orange["500"];
        this.details.graph = GraphType.AREA;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_CAPACITANCE_SELF_FF:
        this.details.label = "Capacitance(Self Ref)";
        this.details.unit = "fF";
        this.details.colour = orange["500"];
        this.details.graph = GraphType.AREA;
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_DUST_1UG:
        this.details.label = "Dust <1um";
        this.details.unit = "ug/m3";
        this.details.factor = 10;
        this.details.decimals = 2;
        this.details.multiply = false;
        this.details.colour = lightGreen["500"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_DUST_2UG:
        this.details.label = "Dust <2.5um";
        this.details.unit = "ug/m3";
        this.details.factor = 10;
        this.details.decimals = 2;
        this.details.multiply = false;
        this.details.colour = lightGreen["400"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_DUST_4UG:
        this.details.label = "Dust <4um";
        this.details.unit = "ug/m3";
        this.details.factor = 10;
        this.details.decimals = 2;
        this.details.multiply = false;
        this.details.colour = lightGreen["300"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_DUST_10UG:
        this.details.label = "Dust <10um";
        this.details.unit = "ug/m3";
        this.details.factor = 10;
        this.details.decimals = 2;
        this.details.multiply = false;
        this.details.colour = lightGreen["200"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_VOC_INDEX:
        this.details.label = "VOC percent";
        this.details.unit = "%";
        this.details.factor = 10;
        this.details.multiply = false;
        this.details.colour = teal["400"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_NOX_INDEX:
        this.details.label = "NOX percent";
        this.details.unit = "%";
        this.details.factor = 10;
        this.details.multiply = false;
        this.details.colour = teal["300"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_FREQUENCY:
        this.details.label = "Frequency";
        this.details.unit = "Hz";
        this.details.colour = green["300"];
        break;
      case quack.MeasurementType.MEASUREMENT_TYPE_SINGLE_ACCELERATION_RMS:
      case quack.MeasurementType.MEASUREMENT_TYPE_TOTAL_ACCELERATION_RMS:
        this.details.label = "Acceleration";
        this.details.unit = "m/s²";
        this.details.colour = yellow["300"];
        break;
    }
  }

  isErrorMeasurement(measurement?: quack.Measurement | null): boolean {
    if (this.details.errorValue === undefined || !measurement || this.details.path === "")
      return false;
    let value = extract<number>(measurement, this.details.path, 0);
    return this.details.errorValue === value;
  }

  GetErrorValue() {
    return this.details.errorValue;
  }

  GetUnit() {
    return this.details.unit;
  }

  toDisplay(value: number): number {
    let converted;
    if (this.details.complexConversion !== null) {
      converted = this.details.complexConversion(value, "toDisplay");
    } else {
      converted = this.details.multiply ? value * this.details.factor : value / this.details.factor;
    }

    return roundTo(converted, this.details.decimals);
  }

  toStored(value: number): number {
    let converted;
    if (this.details.complexConversion !== null) {
      converted = this.details.complexConversion(value, "toStored");
    } else {
      converted = this.details.multiply ? value / this.details.factor : value * this.details.factor;
    }

    return converted;
  }

  forDisplay(measurement?: quack.Measurement | null): number {
    if (!measurement) return 0;
    if (this.details.path === "") return 0;
    let storedValue = extract<number>(measurement, this.details.path, 0);
    return this.toDisplay(storedValue);
  }

  convertWithUnits(value: number, unitMeasurement?: boolean): string {
    let converted = unitMeasurement ? value : this.toDisplay(value);
    if (
      this.details.enumerations.length > 0 &&
      converted >= 0 &&
      this.details.enumerations.length > converted
    ) {
      return this.details.enumerations[converted].label;
    }
    return (
      converted.toFixed(this.details.decimals) +
      (this.details.spacer ? " " : "") +
      this.details.unit
    );
  }

  convertWithoutUnits(value: number, unitMeasurement?: boolean): string {
    let converted = unitMeasurement ? value : this.toDisplay(value);
    if (
      this.details.enumerations.length > 0 &&
      converted >= 0 &&
      this.details.enumerations.length > converted
    ) {
      return this.details.enumerations[converted].label;
    }
    return converted.toFixed(this.details.decimals);
  }

  withUnits(measurement: quack.Measurement): string {
    return this.convertWithUnits(extract(measurement, this.details.path, 0));
  }

  withoutUnits(measurement: quack.Measurement): string {
    return this.convertWithoutUnits(extract(measurement, this.details.path, 0));
  }

  type(): quack.MeasurementType {
    return this.measurementType;
  }

  label(): string {
    return this.details.label;
  }

  unit(): string {
    return this.details.unit;
  }

  colour(measurement?: quack.Measurement): any {
    if (measurement) {
      let value = this.forDisplay(measurement);
      if (
        this.details.enumerations.length > 0 &&
        value >= 0 &&
        this.details.enumerations.length > value
      ) {
        return this.details.enumerations[value].colour;
      }
    }
    return this.details.colour;
  }

  nodeDetails(): NodeDetails | undefined {
    return this.details.nodeDetails;
  }

  graph(): GraphType {
    return this.details.graph;
  }

  enumerations(): string[] {
    return this.details.enumerations.map(e => e.label);
  }

  min(): number {
    return this.details.min;
  }

  max(): number {
    return this.details.max;
  }

  step(): number {
    return this.details.step;
  }

  decimals(): number {
    return this.details.decimals;
  }
}

export function describeMeasurement(
  measurementType: quack.MeasurementType | null | undefined,
  componentType: quack.ComponentType | null | undefined = quack.ComponentType
    .COMPONENT_TYPE_INVALID,
  componentSubtype: number = 0,
  product?: pond.DeviceProduct
): MeasurementDescriber {
  measurementType = measurementType
    ? measurementType
    : quack.MeasurementType.MEASUREMENT_TYPE_INVALID;
  componentType = componentType ? componentType : quack.ComponentType.COMPONENT_TYPE_INVALID;
  return new MeasurementDescriber(measurementType, componentType, componentSubtype, product);
}
