import React from 'react'
import PropTypes from 'prop-types'

import Button from '../Button'
import SpecField from './SpecField'
import NumberInput from '../inputs/NumberInput'
import InputBlock from '../inputs/InputBlock'

import DeleteStopButton from './_DeleteStopButton'
import labelFromFieldName from './_labelFromFieldName'

import docUid from '../../libs/document-uid'
import sortNumerically from '../../libs/sort-numerically'


export default class ZoomProperty extends React.Component {
  static propTypes = {
    onChange: PropTypes.func,
    onDeleteStop: PropTypes.func,
    onAddStop: PropTypes.func,
    fieldName: PropTypes.string,
    fieldSpec: PropTypes.object,
    value: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.string,
      PropTypes.number,
      PropTypes.bool,
      PropTypes.array
    ]),
  }


  constructor() {
    super()
    this.state = {
      refs: {}
    }
  }

  componentDidMount() {
    this.setState({
      refs: this.setStopRefs(this.props)
    })
  }

  /**
   * We cache a reference for each stop by its index.
   *
   * When the stops are reordered the references are also updated (see this.orderStops) this allows React to use the same key for the element and keep keyboard focus.
   */
  setStopRefs(props) {
    // This is initialsed below only if required to improved performance.
    let newRefs;

    if(props.value && props.value.stops) {
      props.value.stops.forEach((val, idx) => {
        if(!this.state.refs.hasOwnProperty(idx)) {
          if(!newRefs) {
            newRefs = {...this.state.refs};
          }
          newRefs[idx] = docUid("stop-");
        }
      })
    }

    return newRefs;
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    const newRefs = this.setStopRefs(nextProps);
    if(newRefs) {
      this.setState({
        refs: newRefs
      })
    }
  }

  // Order the stops altering the refs to reflect their new position.
  orderStopsByZoom(stops) {
    const mappedWithRef = stops
      .map((stop, idx) => {
        return {
          ref: this.state.refs[idx],
          data: stop
        }
      })
    // Sort by zoom
      .sort((a, b) => sortNumerically(a.data[0], b.data[0]));

    // Fetch the new position of the stops
    const newRefs = {};
    mappedWithRef
      .forEach((stop, idx) =>{
        newRefs[idx] = stop.ref;
      })

    this.setState({
      refs: newRefs
    });

    return mappedWithRef.map((item) => item.data);
  }

  changeZoomStop(changeIdx, stopData, value) {
    const stops = this.props.value.stops.slice(0);
    stops[changeIdx] = [stopData, value];

    const orderedStops = this.orderStopsByZoom(stops);

    const changedValue = {
      ...this.props.value,
      stops: orderedStops
    }
    this.props.onChange(this.props.fieldName, changedValue)
  }

  render() {
    const zoomFields = this.props.value.stops.map((stop, idx) => {
      const zoomLevel = stop[0]
      const key  = this.state.refs[idx];
      const value = stop[1]
      const deleteStopBtn= <DeleteStopButton onClick={this.props.onDeleteStop.bind(this, idx)} />

      return <InputBlock
        key={key}
        doc={this.props.fieldSpec.doc}
        label={labelFromFieldName(this.props.fieldName)}
        action={deleteStopBtn}
      >
        <div>
          <div className="maputnik-zoom-spec-property-stop-edit">
            <NumberInput
              value={zoomLevel}
              onChange={changedStop => this.changeZoomStop(idx, changedStop, value)}
              min={0}
              max={22}
            />
          </div>
          <div className="maputnik-zoom-spec-property-stop-value">
            <SpecField
              fieldName={this.props.fieldName}
              fieldSpec={this.props.fieldSpec}
              value={value}
              onChange={(_, newValue) => this.changeZoomStop(idx, zoomLevel, newValue)}
            />
          </div>
        </div>
      </InputBlock>
    });

    return <div className="maputnik-zoom-spec-property">
      {zoomFields}
      <Button
        className="maputnik-add-stop"
        onClick={this.props.onAddStop.bind(this)}
      >
        Add stop
      </Button>
    </div>
  }
}