import { Component, Device } from "models";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { or, notNull } from "utils/types";
import { capitalize } from "utils/strings";

export function sameComponentID(
  id1: quack.IComponentID | null | undefined,
  id2: quack.IComponentID | null | undefined
): boolean {
  if (!id1 || !id2) {
    return false;
  }

  const sameType: boolean = id1.type === id2.type;
  const sameAddressType: boolean = id1.addressType === id2.addressType;
  const sameAddress: boolean = (!id1.address && !id2.address) || id1.address === id2.address;
  return sameType && sameAddressType && sameAddress;
}

export function getComponentIDString(component?: Component): string {
  return component
    ? componentIDToString({
        type: component.settings.type,
        addressType: component.settings.addressType,
        address: component.settings.address,
        expansionLine: component.settings.expansionLine,
        muxLine: component.settings.muxLine
      })
    : "0-0-0";
}

export function getComponentTypeString(component: pond.ComponentSettings): string {
  let componentTypeString: string = "";
  if (notNull(component) && notNull(component.type)) {
    componentTypeString = quack.ComponentType[or(component, 0).type];
  }
  return componentTypeString;
}

export function componentIDToString(componentID?: quack.IComponentID | null): string {
  if (!componentID) {
    return "0-0-0";
  }
  let compositeLocation = or(componentID.type, 0).toString() +
    "-" +
    or(componentID.addressType, 0).toString() +
    "-" +
    or(componentID.address, 0).toString()
  if(componentID.expansionLine){
    compositeLocation = compositeLocation + "-" + componentID.expansionLine
  }
  if(componentID.muxLine){
    compositeLocation = compositeLocation + ":" + componentID.muxLine
  }
  return compositeLocation;
}

export function stringToComponentId(componentIDString: string): quack.ComponentID {
  let componentID = quack.ComponentID.create();

  if (!notNull(componentID) || componentIDString === "") {
    return componentID;
  }

  const componentIDFragments = componentIDString.split("-", 3);
  let type: string = quack.ComponentType[Number(componentIDFragments[0])];
  let addressType: string = quack.AddressType[Number(componentIDFragments[1])];
  let address: number = Number(componentIDFragments[2]);
  componentID.type = quack.ComponentType[type as keyof typeof quack.ComponentType];
  componentID.addressType = quack.AddressType[addressType as keyof typeof quack.AddressType];
  componentID.address = address;
  return componentID;
}

export function emptyComponentId(): quack.ComponentID {
  let componentID = {} as quack.ComponentID;
  componentID.type = quack.ComponentType.COMPONENT_TYPE_INVALID;
  componentID.addressType = quack.AddressType.ADDRESS_TYPE_INVALID;
  componentID.address = 0;
  return componentID;
}

export function deviceComponentID(device: number | string, component: string): string {
  return device.toString() + "-" + component;
}

export function sortComponents(a: Component, b: Component, componentOrder: string[]): number {
  const aIndex = componentOrder.indexOf(a.locationString());
  const bIndex = componentOrder.indexOf(b.locationString());
  if (aIndex === bIndex) return 0;
  if (aIndex < 0) return 1;
  if (bIndex < 0) return -1;
  return bIndex < aIndex ? 1 : -1;
}

export function findComponent(componentID: string, components: Component[]): Component | undefined {
  for (let i = 0; i < components.length; i++) {
    let c = components[i];
    if (c && getComponentIDString(c) === componentID) {
      return c;
    }
  }
  return undefined;
}

export function controllerModeLabel(mode?: number | null) {
  if (typeof mode !== "number" || mode < 0 || mode > 2) return "unknown";

  return mode === 2 ? "off" : mode === 1 ? "on" : "auto";
}

//DeviceComponentKey returns a key to represent a device-component
//  Format: DeviceID:ComponentKey
export function DeviceComponentKey(device: Device, component: Component): string {
  return device.id().toString() + ":" + component.key();
}

const keyTranslator = new Map<keyof pond.ComponentSettings, string>([
  ["key", "Key"],
  ["type", "Type"],
  ["subtype", "Subtype"],
  ["name", "Name"],
  ["reportPeriodMs", "Report Period"],
  ["measurementPeriodMs", "Measurement Period"],
  ["defaultOutputState", "Default Output State"],
  ["minCycleTimeMs", "Min Cycle Times"],
  ["removed", "Removed"],
  ["addressType", "Address Type"],
  ["address", "Address"],
  ["calibrate", "Calibrate"],
  ["calibrationCoefficient", "Calibration Coefficient"],
  ["calibrationOffset", "Calibration Offset"],
  ["grainType", "Grain Type"],
  ["grainFilledTo", "Grain Filled To"]
]);

// calibration_offset
// grain_type
// grain_filled_to

// Keys will be stringified by default if not found in the keyTranslator
export function TranslateKey(key: keyof pond.ComponentSettings): string {
  let translatedKey = keyTranslator.get(key);
  return translatedKey ? translatedKey : capitalize(key.toString());
}

// might not need this
const valueTranslator = new Map<
  keyof pond.ComponentSettings,
  (device: pond.ComponentSettings) => string
>([]);

// Values will be stringified by default if its key is not found in the valueTranslator
export function TranslateValue(
  key: keyof pond.ComponentSettings,
  component: pond.ComponentSettings
) {
  let translatorFunc = valueTranslator.get(key);
  let value: any = or(component[key], "");
  let d: string;
  d = or(value.toString(), "");
  return translatorFunc ? translatorFunc(component) : component[key] ? capitalize(d) : "";
}
