import * as classNames from "classnames";
import * as React from "react";
import { Form, Input, InputProps } from "semantic-ui-react";

import { Duration } from "@common/Duration";

import "@client/styles/DurationView";

export interface DurationViewProps {
  label?: string;
  inline?: boolean;
  duration: Duration;
  onDurationChange?: (newDuration: Duration) => void;
  className?: string;
}

function roundOrString(val: number | string): number | string {
  if (typeof val === "number") {
    return Math.round(val);
  } else {
    return val;
  }
}

interface NumberInputProps {
  className?: string;
  label?: string;
  value: number;
  max?: number;
  onChange: (value: number) => void;
}

function NumberInput(props: NumberInputProps): React.ReactElement {
  const [valueState, setValueState] = React.useState<number | string>(props.value);
  const [elementId, setElementId] = React.useState(() => `NumberInput-${Math.round(Math.random() * 100000000)}`);
  const [isWheelChange, setIsWheelChange] = React.useState(false);

  const onChange: InputProps["onChange"] = (_e, data) => {
    setValueState(data.value);
    const newValue = parseFloat(data.value);
    if (!isNaN(newValue) && data.value.length > 0 && isWheelChange) {
      props.onChange(Math.round(newValue));
      setIsWheelChange(false);
    }
  };

  const onBlur: React.FocusEventHandler = () => {
    const newValue = (typeof valueState === "number") ? valueState : parseFloat(valueState);
    if (!props.onChange || isNaN(newValue)) {
      return;
    }
    if (props.value !== newValue) {
      props.onChange(Math.round(newValue));
    }
  };

  const onWheel = (e: Event) => {
    // do nothing
    setIsWheelChange(true);
  };

  React.useEffect(() => {
    const el = document.getElementById(elementId);
    if (el) {
      // Not passive events
      el.addEventListener("wheel", onWheel);
    }
  });

  React.useEffect(() => {
    if (props.value !== valueState) {
      setValueState(props.value);
    }
  }, [props.value]);

  return <Input
    id={elementId}
    type="number"
    pattern="[0-9\.]*" // for safari
    inputMode="numeric"
    value={roundOrString(valueState)}
    onChange={onChange}
    // onMouseOut={onBlur}
    onBlur={onBlur}
    className={props.className}
    label={props.label}
    max={props.max}
    labelPosition="right"
  />
}

export default class DurationView extends React.Component<DurationViewProps> {
  render() {
    const { duration, label, inline, onDurationChange, className } = this.props;
    const inputsClassName = classNames("durationInputs", { inline });
    if (onDurationChange) {
      return (
        <React.Fragment>
          <Form.Field inline={inline} className={className}>
            {label && <label>{label}</label>}
            <div className={inputsClassName}>
              <NumberInput
                className="durationInput minutes"
                value={this.props.duration.minutes}
                onChange={this.onMinutesChange}
                label="M"
              />
              <NumberInput
                className="durationInput seconds"
                value={this.props.duration.seconds}
                onChange={this.onSecondsChange}
                max={60}
                label="S"
              />
            </div>
          </Form.Field>
        </React.Fragment>
      );
    } else {
      return (
        <span className={className}>
          {label && <label>{label}</label>} {duration.minutes}M{" "}
          {duration.seconds}S
        </span>
      );
    }
  }

  componentWillReceiveProps(nextProps: Readonly<DurationViewProps>) {
    if (nextProps.duration.minutes !== this.props.duration.minutes ||
      nextProps.duration.seconds !== this.props.duration.seconds) {
      this.setState({
        minutes: nextProps.duration.minutes,
        seconds: nextProps.duration.seconds,
      });
    }
  }

  private onMinutesChange = (newMinutes: number) => {
    if (this.props.onDurationChange) {
      this.props.onDurationChange(this.props.duration.withMinutes(newMinutes));
    }
  };

  private onSecondsChange = (newSeconds: number) => {
    if (this.props.onDurationChange) {
      this.props.onDurationChange(this.props.duration.withSeconds(newSeconds));
    }
  };
}
