import { assign } from "lodash";
import { observer } from "mobx-react";
import * as qs from "query-string";
import * as React from "react";
import { RouteComponentProps, withRouter } from "react-router";
import {
  Button,
  CheckboxProps,
  Form,
  Icon,
  Input,
  InputOnChangeData,
  Menu,
  Modal
} from "semantic-ui-react";

import { ProgramSequenceView, ScheduleView } from "@client/components";
import * as route from "@client/routePaths";
import { AppState, injectState } from "@client/state";
import { ISprinklersDevice } from "@common/httpApi";
import log from "@common/logger";
import { Program, SprinklersDevice } from "@common/sprinklersRpc";
import { action } from "mobx";

interface ProgramPageProps
  extends RouteComponentProps<{ deviceId: string; programId: string }> {
  appState: AppState;
}

@observer
class ProgramPage extends React.Component<ProgramPageProps> {
  get isEditing(): boolean {
    return qs.parse(this.props.location.search).editing != null;
  }

  deviceInfo: ISprinklersDevice | null = null;
  device: SprinklersDevice | null = null;
  program: Program | null = null;
  programView: Program | null = null;

  componentWillUnmount() {
    if (this.device) {
      this.device.release();
    }
  }

  updateProgram() {
    const { userStore, sprinklersRpc } = this.props.appState;
    const devId = Number(this.props.match.params.deviceId);
    const programId = Number(this.props.match.params.programId);
    // tslint:disable-next-line:prefer-conditional-expression
    if (this.deviceInfo == null || this.deviceInfo.id !== devId) {
      this.deviceInfo = userStore.findDevice(devId);
    }
    if (!this.deviceInfo || !this.deviceInfo.deviceId) {
      if (this.device) {
        this.device.release();
        this.device = null;
      }
      return;
    } else {
      if (this.device == null || this.device.id !== this.deviceInfo.deviceId) {
        if (this.device) {
          this.device.release();
        }
        this.device = sprinklersRpc.acquireDevice(this.deviceInfo.deviceId);
      }
    }
    if (!this.program || this.program.id !== programId) {
      if (this.device.programs.length > programId && programId >= 0) {
        this.program = this.device.programs[programId];
      } else {
        return;
      }
    }
    if (this.isEditing) {
      if (this.programView == null && this.program) {
        // this.programView = createViewModel(this.program);
        // this.programView = observable(toJS(this.program));
        this.programView = this.program.clone();
      }
    } else {
      if (this.programView != null) {
        // this.programView.reset();
        this.programView = null;
      }
    }
  }

  renderName(program: Program) {
    const { name } = program;
    if (this.isEditing) {
      return (
        <Menu.Item header>
          <Form>
            <Form.Group inline>
              <Form.Field inline>
                <label>
                  <h4>Program</h4>
                </label>
                <Input
                  placeholder="Program Name"
                  type="text"
                  value={name}
                  onChange={this.onNameChange}
                />
              </Form.Field>
              ({program.id})
            </Form.Group>
          </Form>
        </Menu.Item>
      );
    } else {
      return (
        <Menu.Item header as="h4">
          Program {name} ({program.id})
        </Menu.Item>
      );
    }
  }

  renderActions(program: Program) {
    const { running } = program;
    const editing = this.isEditing;
    let editButtons;
    if (editing) {
      editButtons = (
        <React.Fragment>
          <Button primary onClick={this.save}>
            <Icon name="save" />
            Save
          </Button>
          <Button negative onClick={this.stopEditing}>
            <Icon name="cancel" />
            Cancel
          </Button>
        </React.Fragment>
      );
    } else {
      editButtons = (
        <Button primary onClick={this.startEditing}>
          <Icon name="edit" />
          Edit
        </Button>
      );
    }
    const stopStartButton = (
      <Button onClick={this.cancelOrRun} positive={!running} negative={running}>
        <Icon name={running ? "stop" : "play"} />
        {running ? "Stop" : "Run"}
      </Button>
    );
    return (
      <Modal.Actions>
        {stopStartButton}
        {editButtons}
        <Button onClick={this.close}>
          <Icon name="arrow left" />
          Close
        </Button>
      </Modal.Actions>
    );
  }

  render() {
    this.updateProgram();

    const program = this.programView || this.program;
    if (!this.device || !program) {
      return null;
    }
    const editing = this.isEditing;

    const { running, enabled, schedule, sequence } = program;

    return (
      <Modal open onClose={this.close} className="programEditor">
        <Modal.Header>{this.renderName(program)}</Modal.Header>
        <Modal.Content>
          <Form>
            <Form.Group>
              <Form.Checkbox
                toggle
                label="Enabled"
                checked={enabled}
                readOnly={!editing}
                onChange={this.onEnabledChange}
              />
              <Form.Checkbox
                toggle
                label="Running"
                checked={running}
                readOnly={!editing}
              />
            </Form.Group>
            <Form.Field>
              <label>
                <h4>Sequence</h4>
              </label>
              <ProgramSequenceView
                sequence={sequence}
                sections={this.device.sections}
                editing={editing}
              />
            </Form.Field>
            <ScheduleView
              schedule={schedule}
              editing={editing}
              label={<h4>Schedule</h4>}
            />
          </Form>
        </Modal.Content>
        {this.renderActions(program)}
      </Modal>
    );
  }

  @action.bound
  private cancelOrRun() {
    if (!this.program) {
      return;
    }
    this.program.running ? this.program.cancel() : this.program.run();
  }

  @action.bound
  private startEditing() {
    this.props.history.push({ search: qs.stringify({ editing: true }) });
  }

  @action.bound
  private save() {
    if (!this.programView || !this.program) {
      return;
    }
    assign(this.program, this.programView);
    this.program.update().then(
      data => {
        log.info({ data }, "Program updated");
      },
      err => {
        log.error({ err }, "error updating Program");
      }
    );
    this.stopEditing();
  }

  @action.bound
  private stopEditing() {
    this.props.history.push({ search: "" });
  }

  @action.bound
  private close() {
    // this.props.history.goBack();
    this.props.appState.history.goBack();
  }

  @action.bound
  private onNameChange(e: any, p: InputOnChangeData) {
    if (this.programView) {
      this.programView.name = p.value;
    }
  }

  @action.bound
  private onEnabledChange(e: any, p: CheckboxProps) {
    if (this.programView) {
      this.programView.enabled = p.checked!;
    }
  }
}

const DecoratedProgramPage = injectState(withRouter(observer(ProgramPage)));
export default DecoratedProgramPage;
