import * as React from "react";
import { ActiveMiddleProps, SequenceHeaderProps } from "./interfaces";
import { editCurrentSequence } from "./actions";
import { splice, move } from "./step_tiles";
import { push } from "../history";
import { BlurableInput, Row, Col, SaveBtn, ColorPicker, Help } from "../ui";
import { DropArea } from "../draggable/drop_area";
import { stepGet } from "../draggable/actions";
import { copySequence } from "./actions";
import { TaggedSequence, SyncStatus } from "farmbot";
import { save, edit, destroy } from "../api/crud";
import { TestButton } from "./test_button";
import { AllSteps } from "./all_steps";
import { LocalsList, localListCallback } from "./locals_list/locals_list";
import { betterCompact } from "../util";
import { AllowedVariableNodes } from "./locals_list/locals_list_support";
import { ResourceIndex } from "../resources/interfaces";
import { ShouldDisplay } from "../devices/interfaces";
import { isScopeDeclarationBodyItem } from "./locals_list/handle_select";
import { t } from "../i18next_wrapper";
import { Actions } from "../constants";
import { Popover, Position } from "@blueprintjs/core";
import { ToggleButton } from "../controls/toggle_button";
import { Content } from "../constants";
import {
  setWebAppConfigValue,
  GetWebAppConfigValue,
} from "../config_storage/actions";
import { BooleanSetting } from "../session_keys";
import { BooleanConfigKey } from "farmbot/dist/resources/configs/web_app";
import { isUndefined } from "lodash";
import { NO_GROUPS } from "./locals_list/default_value_form";
import { ErrorBoundary } from "../error_boundary";

export const onDrop =
  (dispatch1: Function, sequence: TaggedSequence) =>
    (index: number, key: string) => {
      if (key.length > 0) {
        dispatch1(function (dispatch2: Function) {
          const dataXferObj = dispatch2(stepGet(key));
          const step = dataXferObj.value;
          switch (dataXferObj.intent) {
            case "step_splice":
              return dispatch2(splice({ step, sequence, index }));
            case "step_move":
              const action =
                move({ step, sequence, to: index, from: dataXferObj.draggerId });
              return dispatch2(action);
            default:
              throw new Error("Got unexpected data transfer object.");
          }
        });
      }
    };

export interface SequenceSettingsMenuProps {
  dispatch: Function;
  getWebAppConfigValue: GetWebAppConfigValue;
}

export interface SequenceSettingProps {
  label: string;
  description: string;
  dispatch: Function;
  setting: BooleanConfigKey;
  getWebAppConfigValue: GetWebAppConfigValue;
  confirmation?: string;
  defaultOn?: boolean;
}

export const SequenceSetting = (props: SequenceSettingProps) => {
  const raw_value = props.getWebAppConfigValue(props.setting);
  const value = (props.defaultOn && isUndefined(raw_value)) ? true : !!raw_value;
  const proceed = () =>
    (props.confirmation && !value) ? confirm(t(props.confirmation)) : true;
  return <fieldset>
    <label>
      {t(props.label)}
    </label>
    <Help text={t(props.description)} requireClick={true} />
    <ToggleButton
      toggleValue={value}
      toggleAction={() => proceed() &&
        props.dispatch(setWebAppConfigValue(props.setting, !value))} />
  </fieldset>;
};

export const SequenceSettingsMenu =
  ({ dispatch, getWebAppConfigValue }: SequenceSettingsMenuProps) => {
    const commonProps = { dispatch, getWebAppConfigValue };
    return <div className="sequence-settings-menu">
      <SequenceSetting {...commonProps}
        setting={BooleanSetting.confirm_step_deletion}
        label={t("Confirm step deletion")}
        description={Content.CONFIRM_STEP_DELETION} />
      <SequenceSetting {...commonProps}
        setting={BooleanSetting.confirm_sequence_deletion}
        defaultOn={true}
        label={t("Confirm sequence deletion")}
        description={Content.CONFIRM_SEQUENCE_DELETION} />
      <SequenceSetting {...commonProps}
        setting={BooleanSetting.show_pins}
        label={t("Show pins")}
        description={Content.SHOW_PINS} />
      <SequenceSetting {...commonProps}
        setting={BooleanSetting.expand_step_options}
        label={t("Open options by default")}
        description={Content.EXPAND_STEP_OPTIONS} />
      <SequenceSetting {...commonProps}
        setting={BooleanSetting.discard_unsaved_sequences}
        confirmation={Content.DISCARD_UNSAVED_SEQUENCE_CHANGES_CONFIRM}
        label={t("Discard unsaved sequence changes")}
        description={Content.DISCARD_UNSAVED_SEQUENCE_CHANGES} />
    </div>;
  };

interface SequenceBtnGroupProps {
  dispatch: Function;
  sequence: TaggedSequence;
  syncStatus: SyncStatus;
  resources: ResourceIndex;
  shouldDisplay: ShouldDisplay;
  menuOpen: boolean;
  getWebAppConfigValue: GetWebAppConfigValue;
}

const SequenceBtnGroup = ({
  dispatch,
  sequence,
  syncStatus,
  resources,
  shouldDisplay,
  menuOpen,
  getWebAppConfigValue
}: SequenceBtnGroupProps) =>
  <div className="button-group">
    <SaveBtn status={sequence.specialStatus}
      onClick={() => dispatch(save(sequence.uuid))} />
    <TestButton
      syncStatus={syncStatus}
      sequence={sequence}
      resources={resources}
      shouldDisplay={shouldDisplay}
      menuOpen={menuOpen}
      dispatch={dispatch} />
    <button
      className="fb-button red"
      onClick={() => {
        const confirm = getWebAppConfigValue(
          BooleanSetting.confirm_sequence_deletion);
        const force = !(confirm ?? true);
        dispatch(destroy(sequence.uuid, force))
          .then(() => push("/app/sequences/"));
      }}>
      {t("Delete")}
    </button>
    <button
      className="fb-button yellow"
      onClick={() => dispatch(copySequence(sequence))}>
      {t("Copy")}
    </button>
    <div className={"settings-menu-button"}>
      <Popover position={Position.BOTTOM_RIGHT}>
        <i className="fa fa-gear" />
        <SequenceSettingsMenu
          dispatch={dispatch}
          getWebAppConfigValue={getWebAppConfigValue} />
      </Popover>
    </div>
  </div>;

export const SequenceNameAndColor = ({ dispatch, sequence }: {
  dispatch: Function, sequence: TaggedSequence
}) =>
  <Row>
    <Col xs={11}>
      <BlurableInput value={sequence.body.name}
        placeholder={t("Sequence Name")}
        onCommit={e =>
          dispatch(edit(sequence, { name: e.currentTarget.value }))} />
    </Col>
    <Col xs={1} className="color-picker-col">
      <ColorPicker
        current={sequence.body.color}
        onChange={color =>
          editCurrentSequence(dispatch, sequence, { color })} />
    </Col>
  </Row>;

const SequenceHeader = (props: SequenceHeaderProps) => {
  const { sequence, dispatch } = props;
  const sequenceAndDispatch = { sequence, dispatch };
  const variableData = props.resources.sequenceMetas[sequence.uuid] || {};
  const declarations = betterCompact(Object.values(variableData)
    .map(v => v &&
      isScopeDeclarationBodyItem(v.celeryNode) ? v.celeryNode : undefined));
  return <div id="sequence-editor-tools" className="sequence-editor-tools">
    <SequenceBtnGroup {...sequenceAndDispatch}
      syncStatus={props.syncStatus}
      resources={props.resources}
      shouldDisplay={props.shouldDisplay}
      getWebAppConfigValue={props.getWebAppConfigValue}
      menuOpen={props.menuOpen} />
    <SequenceNameAndColor {...sequenceAndDispatch} />
    <ErrorBoundary>
      <LocalsList
        variableData={variableData}
        sequenceUuid={sequence.uuid}
        resources={props.resources}
        onChange={localListCallback(props)(declarations)}
        locationDropdownKey={JSON.stringify(sequence)}
        allowedVariableNodes={AllowedVariableNodes.parameter}
        collapsible={true}
        collapsed={props.variablesCollapsed}
        toggleVarShow={props.toggleVarShow}
        shouldDisplay={props.shouldDisplay}
        hideGroups={true}
        customFilterRule={NO_GROUPS} />
    </ErrorBoundary>
  </div>;
};

interface ActiveMiddleState {
  variablesCollapsed: boolean;
}

export class SequenceEditorMiddleActive extends
  React.Component<ActiveMiddleProps, ActiveMiddleState> {
  state: ActiveMiddleState = { variablesCollapsed: false };

  /** Make room for the sequence header variable form when necessary. */
  get stepSectionHeight() {
    const { resources, sequence } = this.props;
    let subHeight = 200;
    const variables =
      Object.keys(resources.sequenceMetas[sequence.uuid] || {}).length > 0;
    if (variables) { subHeight = 500; }
    if (this.state.variablesCollapsed) { subHeight = 300; }
    const variablesDiv = document.getElementById("sequence-editor-tools");
    if (variablesDiv) { subHeight = 200 + variablesDiv.offsetHeight; }
    return `calc(100vh - ${subHeight}px)`;
  }

  get stepProps() {
    const getConfig = this.props.getWebAppConfigValue;
    return {
      sequence: this.props.sequence,
      onDrop: onDrop(this.props.dispatch, this.props.sequence),
      dispatch: this.props.dispatch,
      resources: this.props.resources,
      hardwareFlags: this.props.hardwareFlags,
      farmwareData: this.props.farmwareData,
      shouldDisplay: this.props.shouldDisplay,
      confirmStepDeletion: !!getConfig(BooleanSetting.confirm_step_deletion),
      showPins: !!getConfig(BooleanSetting.show_pins),
      expandStepOptions: !!getConfig(BooleanSetting.expand_step_options),
    };
  }

  render() {
    const { dispatch, sequence } = this.props;
    return <div className="sequence-editor-content">
      <SequenceHeader
        dispatch={this.props.dispatch}
        sequence={sequence}
        resources={this.props.resources}
        syncStatus={this.props.syncStatus}
        shouldDisplay={this.props.shouldDisplay}
        variablesCollapsed={this.state.variablesCollapsed}
        toggleVarShow={() =>
          this.setState({ variablesCollapsed: !this.state.variablesCollapsed })}
        getWebAppConfigValue={this.props.getWebAppConfigValue}
        menuOpen={this.props.menuOpen} />
      <hr />
      <div className="sequence" id="sequenceDiv"
        style={{ height: this.stepSectionHeight }}>
        <ErrorBoundary>
          <AllSteps {...this.stepProps} />
        </ErrorBoundary>
        <Row>
          <Col xs={12}>
            <DropArea isLocked={true}
              callback={key => onDrop(dispatch, sequence)(Infinity, key)}>
              {t("DRAG COMMAND HERE")}
            </DropArea>
            <AddCommandButton dispatch={dispatch} index={99999999} />
          </Col>
        </Row>
      </div>
    </div>;
  }
}

export const AddCommandButton = (props: { dispatch: Function, index: number }) =>
  <div className="add-command-button-container">
    <button
      className="add-command fb-button gray"
      onClick={() => props.dispatch({
        type: Actions.SET_SEQUENCE_STEP_POSITION,
        payload: props.index,
      })}>
      {t("Add command")}
    </button>
  </div>;
