import * as React from "react";
import { store } from "../../redux/store";
import { ControlPanelState } from "../interfaces";
import { toggleControlPanel, bulkToggleControlPanel } from "../actions";
import { urlFriendly } from "../../util";
import { DeviceSetting } from "../../constants";
import { trim } from "lodash";

const HOMING_PANEL = [
  DeviceSetting.homingAndCalibration,
  DeviceSetting.homing,
  DeviceSetting.calibration,
  DeviceSetting.setZeroPosition,
  DeviceSetting.findHomeOnBoot,
  DeviceSetting.stopAtHome,
  DeviceSetting.stopAtMax,
  DeviceSetting.negativeCoordinatesOnly,
  DeviceSetting.axisLength,
];
const MOTORS_PANEL = [
  DeviceSetting.motors,
  DeviceSetting.maxSpeed,
  DeviceSetting.homingSpeed,
  DeviceSetting.minimumSpeed,
  DeviceSetting.accelerateFor,
  DeviceSetting.stepsPerMm,
  DeviceSetting.microstepsPerStep,
  DeviceSetting.alwaysPowerMotors,
  DeviceSetting.invertMotors,
  DeviceSetting.motorCurrent,
  DeviceSetting.enable2ndXMotor,
  DeviceSetting.invert2ndXMotor,
];
const ENCODERS_PANEL = [
  DeviceSetting.encoders,
  DeviceSetting.stallDetection,
  DeviceSetting.enableEncoders,
  DeviceSetting.enableStallDetection,
  DeviceSetting.stallSensitivity,
  DeviceSetting.useEncodersForPositioning,
  DeviceSetting.invertEncoders,
  DeviceSetting.maxMissedSteps,
  DeviceSetting.missedStepDecay,
  DeviceSetting.encoderScaling,
];
const ENDSTOPS_PANEL = [
  DeviceSetting.endstops,
  DeviceSetting.enableEndstops,
  DeviceSetting.swapEndstops,
  DeviceSetting.invertEndstops,
];
const ERROR_HANDLING_PANEL = [
  DeviceSetting.errorHandling,
  DeviceSetting.timeoutAfter,
  DeviceSetting.maxRetries,
  DeviceSetting.estopOnMovementError,
];
const PIN_GUARD_PANEL = [
  DeviceSetting.pinGuard,
  DeviceSetting.pinGuard1,
  DeviceSetting.pinGuard2,
  DeviceSetting.pinGuard3,
  DeviceSetting.pinGuard4,
  DeviceSetting.pinGuard5,
];
const DANGER_ZONE_PANEL = [
  DeviceSetting.dangerZone,
  DeviceSetting.resetHardwareParams,
];
const PIN_BINDINGS_PANEL = [
  DeviceSetting.pinBindings,
  DeviceSetting.savedPinBindings,
  DeviceSetting.addNewPinBinding,
];
const POWER_AND_RESET_PANEL = [
  DeviceSetting.powerAndReset,
  DeviceSetting.restartFarmbot,
  DeviceSetting.shutdownFarmbot,
  DeviceSetting.restartFirmware,
  DeviceSetting.factoryReset,
  DeviceSetting.autoFactoryReset,
  DeviceSetting.connectionAttemptPeriod,
  DeviceSetting.changeOwnership,
];

const FARM_DESIGNER_PANEL = [
  DeviceSetting.farmDesigner,
  DeviceSetting.animations,
  DeviceSetting.trail,
  DeviceSetting.dynamicMap,
  DeviceSetting.mapSize,
  DeviceSetting.rotateMap,
  DeviceSetting.mapOrigin,
  DeviceSetting.confirmPlantDeletion,
];

const FIRMWARE_PANEL = [
  DeviceSetting.firmwareSection,
  DeviceSetting.firmware,
  DeviceSetting.flashFirmware,
  DeviceSetting.restartFirmware,
];

const FARMBOT_PANEL = [
  DeviceSetting.farmbot,
  DeviceSetting.name,
  DeviceSetting.timezone,
  DeviceSetting.camera,
  DeviceSetting.applySoftwareUpdates,
  DeviceSetting.farmbotOSAutoUpdate,
  DeviceSetting.farmbotOS,
  DeviceSetting.autoSync,
  DeviceSetting.bootSequence,
];

/** Look up parent panels for settings. */
const SETTING_PANEL_LOOKUP = {} as Record<DeviceSetting, keyof ControlPanelState>;
HOMING_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "homing_and_calibration");
MOTORS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "motors");
ENCODERS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "encoders");
ENDSTOPS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "endstops");
ERROR_HANDLING_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "error_handling");
PIN_GUARD_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "pin_guard");
DANGER_ZONE_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "danger_zone");
PIN_BINDINGS_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "pin_bindings");
POWER_AND_RESET_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "power_and_reset");
FARM_DESIGNER_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "farm_designer");
FIRMWARE_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "firmware");
FARMBOT_PANEL.map(s => SETTING_PANEL_LOOKUP[s] = "farmbot_os");

/** Keep string up until first `(` character (trailing whitespace removed). */
const stripUnits = (settingName: string) => trim(settingName.split("(")[0]);

/** Look up parent panels for settings using URL-friendly names. */
const URL_FRIENDLY_LOOKUP: Record<string, keyof ControlPanelState> = {};
Object.entries(SETTING_PANEL_LOOKUP).map(([setting, panel]) => {
  URL_FRIENDLY_LOOKUP[urlFriendly(setting)] = panel;
  URL_FRIENDLY_LOOKUP[urlFriendly(stripUnits(setting))] = panel;
});

/** Look up all relevant names for the same setting. */
const ALTERNATE_NAMES =
  Object.values(DeviceSetting).reduce((acc, s) => { acc[s] = [s]; return acc; },
    {} as Record<DeviceSetting, DeviceSetting[]>);
ALTERNATE_NAMES[DeviceSetting.encoders].push(DeviceSetting.stallDetection);
ALTERNATE_NAMES[DeviceSetting.stallDetection].push(DeviceSetting.encoders);

/** Generate array of names for the same setting. Most only have one. */
const compareValues = (settingName: DeviceSetting) =>
  (ALTERNATE_NAMES[settingName] as string[])
    .concat(stripUnits(settingName))
    .map(s => urlFriendly(s));

/** Retrieve a highlight search term. */
const getHighlightName = () => location.search.split("?highlight=").pop();

/** Only open panel and highlight once per app load. Exported for tests. */
export const highlight = { opened: false, highlighted: false };

/** Open a panel if a setting in that panel is highlighted. */
export const maybeOpenPanel = (
  panelState: ControlPanelState,
  closeOthers = false,
) =>
  (dispatch: Function) => {
    if (highlight.opened) { return; }
    const urlFriendlySettingName = urlFriendly(getHighlightName() || "");
    if (!urlFriendlySettingName) { return; }
    const panel = URL_FRIENDLY_LOOKUP[urlFriendlySettingName];
    closeOthers && dispatch(bulkToggleControlPanel(false, closeOthers));
    const panelIsOpen = panelState[panel];
    if (panelIsOpen) { return; }
    dispatch(toggleControlPanel(panel));
    highlight.opened = true;
  };

/** Highlight a setting if provided as a search term. */
export const maybeHighlight = (settingName: DeviceSetting) => {
  const item = getHighlightName();
  if (highlight.highlighted || !item) { return ""; }
  const isCurrentSetting = compareValues(settingName).includes(item);
  if (!isCurrentSetting) { return ""; }
  highlight.highlighted = true;
  return "highlight";
};

export interface HighlightProps {
  settingName: DeviceSetting;
  children: React.ReactChild
  | React.ReactChild[]
  | (React.ReactChild | false)[]
  | (React.ReactChild | React.ReactChild[])[];
  className?: string;
  searchTerm?: string;
}

interface HighlightState {
  className: string;
}

/** Wrap highlight-able settings. */
export class Highlight extends React.Component<HighlightProps, HighlightState> {
  state: HighlightState = { className: maybeHighlight(this.props.settingName) };

  componentDidMount = () => {
    if (this.state.className == "highlight") {
      /** Slowly fades highlight. */
      this.setState({ className: "unhighlight" });
    }
  }

  get searchTerm() {
    const { resources } = store.getState();
    return resources.consumers.farm_designer.settingsSearchTerm;
  }

  render() {
    const show = !this.searchTerm ||
      this.props.settingName.toLowerCase().includes(this.searchTerm);
    return <div className={[
      this.props.className,
      this.state.className,
    ].join(" ")}
      hidden={!show}>
      {this.props.children}
    </div>;
  }
}
