import React from "react";
import FileSelector from "./file-selector.js";
import SimpleMDE from "react-simplemde-editor";
import style from "react-simplemde-editor/dist/simplemde.min.css";

let formFieldCounter = 0,
  formFieldFileSelectorTypes = ["file", "directory"],
  formFieldNestedLabelTypes = ["radio", "checkbox"];

function formFieldId() {
  return "form-field-" + ++formFieldCounter;
}

export class FormField extends React.PureComponent {
  static propTypes = {
    type: React.PropTypes.string.isRequired,
    prefix: React.PropTypes.string,
    postfix: React.PropTypes.string,
    hasError: React.PropTypes.bool,
  };

  constructor(props) {
    super(props);

    this._fieldRequiredText = __("This field is required");
    this._type = null;
    this._element = null;
    this._extraElementProps = {};

    this.state = {
      isError: null,
      errorMessage: null,
    };
  }

  componentWillMount() {
    if (["text", "number", "radio", "checkbox"].includes(this.props.type)) {
      this._element = "input";
      this._type = this.props.type;
    } else if (this.props.type == "text-number") {
      this._element = "input";
      this._type = "text";
    } else if (this.props.type == "SimpleMDE") {
      this._element = SimpleMDE;
      this._type = "textarea";
      this._extraElementProps.options = {
        hideIcons: ["guide", "heading", "image", "fullscreen", "side-by-side"],
      };
    } else if (formFieldFileSelectorTypes.includes(this.props.type)) {
      this._element = "input";
      this._type = "hidden";
    } else {
      // Non <input> field, e.g. <select>, <textarea>
      this._element = this.props.type;
    }
  }

  componentDidMount() {
    /**
     * We have to add the webkitdirectory attribute here because React doesn't allow it in JSX
     * https://github.com/facebook/react/issues/3468
     */
    if (this.props.type == "directory") {
      this.refs.field.webkitdirectory = true;
    }
  }

  handleFileChosen(path) {
    this.refs.field.value = path;
    if (this.props.onChange) {
      // Updating inputs programmatically doesn't generate an event, so we have to make our own
      const event = new Event("change", { bubbles: true });
      this.refs.field.dispatchEvent(event); // This alone won't generate a React event, but we use it to attach the field as a target
      this.props.onChange(event);
    }
  }

  showError(text) {
    this.setState({
      isError: true,
      errorMessage: text,
    });
  }

  focus() {
    this.refs.field.focus();
  }

  getValue() {
    if (this.props.type == "checkbox") {
      return this.refs.field.checked;
    } else if (this.props.type == "SimpleMDE") {
      return this.refs.field.simplemde.value();
    } else {
      return this.refs.field.value;
    }
  }

  getSelectedElement() {
    return this.refs.field.options[this.refs.field.selectedIndex];
  }

  getOptions() {
    return this.refs.field.options;
  }

  render() {
    // Pass all unhandled props to the field element
    const otherProps = Object.assign({}, this.props),
      isError = this.state.isError !== null
        ? this.state.isError
        : this.props.hasError,
      elementId = this.props.id ? this.props.id : formFieldId(),
      renderElementInsideLabel =
        this.props.label && formFieldNestedLabelTypes.includes(this.props.type);

    delete otherProps.type;
    delete otherProps.label;
    delete otherProps.hasError;
    delete otherProps.className;
    delete otherProps.postfix;
    delete otherProps.prefix;
    const element = (
      <this._element
        id={elementId}
        type={this._type}
        name={this.props.name}
        ref="field"
        placeholder={this.props.placeholder}
        className={
          "form-field__input form-field__input-" +
          this.props.type +
          " " +
          (this.props.className || "") +
          (isError ? "form-field__input--error" : "")
        }
        {...otherProps}
        {...this._extraElementProps}
      >
        {this.props.children}
      </this._element>
    );

    return (
      <div className={"form-field form-field--" + this.props.type}>
        {this.props.prefix
          ? <span className="form-field__prefix">{this.props.prefix}</span>
          : ""}
        {renderElementInsideLabel
          ? <label
              htmlFor={elementId}
              className={
                "form-field__label " +
                (isError ? "form-field__label--error" : "")
              }
            >
              {element}
              {this.props.label}
            </label>
          : element}
        {formFieldFileSelectorTypes.includes(this.props.type)
          ? <FileSelector
              type={this.props.type}
              onFileChosen={this.handleFileChosen.bind(this)}
              {...(this.props.defaultValue
                ? { initPath: this.props.defaultValue }
                : {})}
            />
          : null}
        {this.props.postfix
          ? <span className="form-field__postfix">{this.props.postfix}</span>
          : ""}
        {isError && this.state.errorMessage
          ? <div className="form-field__error">{this.state.errorMessage}</div>
          : ""}
      </div>
    );
  }
}

export class FormRow extends React.PureComponent {
  static propTypes = {
    label: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.element,
    ]),
    errorMessage: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.object,
    ]),
    // helper: React.PropTypes.html,
  };

  constructor(props) {
    super(props);

    this._fieldRequiredText = __("This field is required");

    this.state = this.getStateFromProps(props);
  }

  componentWillReceiveProps(nextProps) {
    this.setState(this.getStateFromProps(nextProps));
  }

  getStateFromProps(props) {
    return {
      isError: !!props.errorMessage,
      errorMessage: typeof props.errorMessage === "string"
        ? props.errorMessage
        : props.errorMessage instanceof Error
          ? props.errorMessage.toString()
          : "",
    };
  }

  showError(text) {
    this.setState({
      isError: true,
      errorMessage: text,
    });
  }

  showRequiredError() {
    this.showError(this._fieldRequiredText);
  }

  clearError(text) {
    this.setState({
      isError: false,
      errorMessage: "",
    });
  }

  getValue() {
    return this.refs.field.getValue();
  }

  getSelectedElement() {
    return this.refs.field.getSelectedElement();
  }

  getOptions() {
    return this.refs.field.getOptions();
  }

  focus() {
    this.refs.field.focus();
  }

  render() {
    const fieldProps = Object.assign({}, this.props),
      elementId = formFieldId(),
      renderLabelInFormField = formFieldNestedLabelTypes.includes(
        this.props.type
      );

    if (!renderLabelInFormField) {
      delete fieldProps.label;
    }
    delete fieldProps.helper;
    delete fieldProps.errorMessage;

    return (
      <div className="form-row">
        {this.props.label && !renderLabelInFormField
          ? <div
              className={
                "form-row__label-row " +
                (this.props.labelPrefix ? "form-row__label-row--prefix" : "")
              }
            >
              <label
                htmlFor={elementId}
                className={
                  "form-field__label " +
                  (this.state.isError ? "form-field__label--error" : "")
                }
              >
                {this.props.label}
              </label>
            </div>
          : ""}
        <FormField ref="field" hasError={this.state.isError} {...fieldProps} />
        {!this.state.isError && this.props.helper
          ? <div className="form-field__helper">{this.props.helper}</div>
          : ""}
        {this.state.isError
          ? <div className="form-field__error">{this.state.errorMessage}</div>
          : ""}
      </div>
    );
  }
}