import * as React from 'react';

export interface QueryPanelProps {
  elmtId: string;
  data: any;
}

export interface QueryPanelState {
  elmtId: string;
  dimensions;
  dimensionInputs;
  dimensionLevelInputs;
  dimensionPivotInputs;
}

/**
 * Parse Query Panel html, with provided id attr, into Query Panel data object
 * @param id
 */
export function getQueryPanelData(id: string): any {
  let data = {
      dimensions: [],
      dimensionInputs: [],
      dimensionLevelInputs: [],
      dimensionPivotInputs: [],
    },
    elmt = document.getElementById(id),
    name,
    dimension,
    dimensionElmt,
    dimensionInputElmt,
    dimensionEmts = elmt.querySelectorAll('.mmui-dimension'),
    dimensionInputElmts = elmt.querySelectorAll('.mmui-dimension-input');
  // parse the available dimensions html
  for (let i = 0; i < dimensionEmts.length; i++) {
    dimensionElmt = dimensionEmts[i];
    dimension = {
      value: dimensionElmt.dataset.value,
      label: dimensionElmt.dataset.label,
    };

    data.dimensions.push(dimension);
  }

  // parse the input field dimensions
  for (let i = 0; i < dimensionInputElmts.length; i++) {
    dimensionInputElmt = dimensionInputElmts[i];
    name = dimensionInputElmt.name;
    dimension = {
      label: dimensionInputElmt.dataset.label,
      value: dimensionInputElmt.value,
    };

    if (/_pivot$/.test(name)) {
      data.dimensionPivotInputs.push(dimension);
    } else if (/_level$/.test(name)) {
      data.dimensionLevelInputs.push(dimension);
    } else if (/^dim/.test(name)) {
      data.dimensionInputs.push(dimension);
    }
  }

  return data;
}

const DIMENSION_ARRAY_NAMES = [
  'dimensions',
  'dimensionInputs',
  'dimensionLevelInputs',
  'dimensionPivotInputs',
];

/**
 * Query Panel Component
 * Provides the behavior for the Query Crafter query builder panel.
 */
export class MmuiQueryPanelComponent extends React.Component<
  QueryPanelProps,
  QueryPanelState
> {
  constructor(props) {
    super(props);

    // init state
    const state = {
      elmtId: props.elmtId,
      dimensions: [],
      dimensionInputs: [],
      dimensionLevelInputs: [],
      dimensionPivotInputs: [],
    };

    for (const daName of DIMENSION_ARRAY_NAMES) {
      state[daName] = props.data[daName];
    }

    this.state = state;
  }

  componentDidUpdate(prevProps, prevState) {
    const daName = 'dimensions';
    if (this.state[daName].length > prevState[daName].length) {
      this.orderDimensions();
    }
  }

  /**
   * Default dimension order compare function.
   *  Override in inheriting class to change.
   * @param a
   * @param b
   */
  compareDimensionOrder = (a, b) => {
    return a.label.localeCompare(b.label);
  };

  /**
   * Order the each of the dimension arrays according .
   */
  orderDimensions() {
    const newState = {},
      daName = 'dimensions',
      dimensionArray = [...this.state[daName]];
    dimensionArray.sort(this.compareDimensionOrder);
    newState[daName] = dimensionArray;

    this.setState(newState);
  }

  /**
   * Find the name of the dimension array that contains the dimension with the provided value
   * @param dimensionValue - value of dimension to locate
   */
  findDimensionArrayName(dimensionValue) {
    let dimension, dimensionArray, dimensionArrayName;

    for (const daName of DIMENSION_ARRAY_NAMES) {
      dimensionArray = this.state[daName];
      for (dimension of dimensionArray) {
        if (dimension.value === dimensionValue) {
          dimensionArrayName = daName;
          break;
        }
      }
    }

    return dimensionArrayName;
  }

  /**
   * Move the dimension, with the provided dimensionValue, from its current array
   *  to the dimension array with name specified by daNameTarget.
   * @param dimensionValue - value of dimension to be moved
   * @param daNameTarget - name of target dimension array
   */
  moveDimension(dimensionValue, daNameTarget) {
    let daNameSrc,
      dimension,
      dimensionArraySrc,
      dimensionArraySrcNew = [],
      dimensionArrayTarget,
      dimensionArrayTargetNew = [];
    daNameSrc = this.findDimensionArrayName(dimensionValue);

    if (daNameSrc && daNameSrc !== daNameTarget) {
      dimensionArraySrc = this.state[daNameSrc];
      for (const d of dimensionArraySrc) {
        if (d.value === dimensionValue) {
          dimension = d;
        } else {
          dimensionArraySrcNew.push(d);
        }
      }

      dimensionArrayTarget = this.state[daNameTarget];
      for (const d of dimensionArrayTarget) {
        dimensionArrayTargetNew.push(d);
      }

      dimensionArrayTargetNew.push(dimension);

      const newState = {};
      newState[daNameSrc] = dimensionArraySrcNew;
      newState[daNameTarget] = dimensionArrayTargetNew;
      this.setState(newState);
    }
  }

  /**
   * User has started to drag a dimension
   * @param evt
   */
  onDimensionDragStart = (evt) => {
    const dimensionValue = evt.target.dataset.value;
    evt.dataTransfer.setData('dimensionValue', dimensionValue);
  };

  /**
   * Dimension is dragged over target
   * @param evt
   */
  onDimensionDragOver = (evt) => {
    evt.preventDefault();

    /*
        * TODO: Finish below code to add list sorting. 

        let elmtBox, elmtHalfBox,
            dimensionElmts, dimensionElmt, daName,
            dimensionValue = evt.dataTransfer.getData("dimensionValue"),
            index = -1
        ;

        // console.log("dimensionValue", dimensionValue);

        let elmt = evt.target;

        while(elmt){

            daName = elmt.dataset.name;
            if(daName){
                break;
            }

            elmt = elmt.parentElement;
        }

        // console.log("daName", daName);

        if(daName){

            dimensionElmts = elmt.querySelectorAll(".mmui-dimension");
            // console.log(dimensionElmts);

            for (let i = 0; i < dimensionElmts.length; i++){

                dimensionElmt = dimensionElmts[i];
                elmtBox = dimensionElmt.getBoundingClientRect();

                let elmtBoxTopQuarter = elmtBox.top + (elmtBox.height * 0.25);
                let elmtBoxBottomQuarter = elmtBox.bottom - (elmtBox.height * 0.25);
                elmtHalfBox = elmtBox.top + (elmtBox.height * 0.5);

                // console.log(evt.clientY, elmtBox.top);
                // if (elmtBox.top <= evt.clientY && evt.clientY < elmtBox.bottom){
                //     index = i;
                //     break;
                // }

                if (elmtBox.top <= evt.clientY && evt.clientY < elmtBoxTopQuarter){
                    index = i;
                }
                else if (elmtBoxBottomQuarter <= evt.clientY && evt.clientY < elmtBox.bottom) {
                    index = i + 1;
                }
            }

            let dimension,
                dimensionArray = this.state[daName],
                dimensionArrayNew = []
            ;

            for(let i = 0; i < dimensionArray.length; i++){

                dimension = dimensionArray[i];
                if(dimension.isPlaceholder){
                    continue;
                }

                if (i === index) {
                    dimensionArrayNew.push({
                        isPlaceholder: true
                    });
                }

                if(dimension.value !== dimensionValue){
                    dimensionArrayNew.push(dimension);
                }
            }

            if (index < 0){
                dimensionArrayNew.push({
                    isPlaceholder: true
                });
            }

            let newState = {};
            newState[daName] = dimensionArrayNew;
            this.setState(newState);
        }
        */
  };

  /**
   * Dimension is dropped into target area
   * @param evt
   */
  onDimensionDrop = (evt) => {
    evt.preventDefault();

    const dimensionValue = evt.dataTransfer.getData('dimensionValue'),
      daNameSrc = evt.currentTarget.dataset.name;

    if (dimensionValue && daNameSrc) {
      this.moveDimension(dimensionValue, daNameSrc);
    }
  };

  /**
   * Move dimension back to available dimentions
   * @param evt
   */
  onDimensionReset = (evt) => {
    evt.preventDefault();

    const dimensionValue = evt.target.dataset.value;
    this.moveDimension(dimensionValue, 'dimensions');
  };

  getEmptyState(msg) {
    return (
      <div className="mmui-empty-dim-field">
        <p>Drag items here to add {msg}.</p>
      </div>
    );
  }

  /**
   * Render the Component
   */
  render() {
    let dimensionElmts,
      dimensionInputElmts,
      dimensionLevelInputElmts,
      dimensionPivotInputElmts,
      dimensionFieldElmt,
      dimensionInputFieldElmt,
      dimensionLevelInputFieldElmt,
      dimensionPivotInputFieldElmt,
      hasDimensions = this.state.dimensions.length > 0,
      hasDimensionInputs = this.state.dimensionInputs.length > 0,
      hasDimensionLevelInputs = this.state.dimensionLevelInputs.length > 0,
      hasDimensionPivotInputs = this.state.dimensionPivotInputs.length > 0;
    // render dimension elements
    if (hasDimensions) {
      dimensionElmts = this.state.dimensions.map((dimension, index) => {
        const key = `dimension-${index}`;
        if (dimension.isPlaceholder) {
          return (
            <li key={key} className="mmui-dimension">
              <span>&nbsp;</span>
            </li>
          );
        } else {
          return (
            // @ts-ignore
            <li
              key={key}
              className="mmui-dimension"
              data-value={dimension.value}
              data-label={dimension.label}
              draggable={true}
              onDragStart={this.onDimensionDragStart}
            >
              <i className="fas fa-grip-vertical"></i>
              <span>{dimension.label}</span>
            </li>
          );
        }
      });

      dimensionFieldElmt = (
        <ul className="mmui-dimensions mmui-text-list">{dimensionElmts}</ul>
      );
    } else {
      dimensionFieldElmt = this.getEmptyState('available dimensions');
    }

    // render dimension element and hidden input
    if (hasDimensionInputs) {
      dimensionInputElmts = this.state.dimensionInputs.map(
        (dimension, index) => {
          const key = `dimension-input-${index}`;

          return (
            // @ts-ignore
            <li
              key={key}
              className="mmui-dimension-input"
              data-value={dimension.value}
              data-label={dimension.label}
              draggable={true}
              onDragStart={this.onDimensionDragStart}
            >
              <i className="fas fa-grip-vertical"></i>
              <span>{dimension.label}</span>
              <a
                href="#"
                className="mmui-dimension-reset"
                data-value={dimension.value}
                onClick={this.onDimensionReset}
              >
                <i className="fa fa-times-circle"></i>
              </a>
              <input
                type="hidden"
                id="id_dim"
                name="dim"
                value={dimension.value}
                data-label={dimension.label}
              />
            </li>
          );
        }
      );

      dimensionInputFieldElmt = (
        <ul className="mmui-dimensions mmui-dimension-inputs mmui-text-list">
          {dimensionInputElmts}
        </ul>
      );
    } else {
      dimensionInputFieldElmt = this.getEmptyState('dimensions');
    }

    // render dimension level element and hidden input
    if (hasDimensionLevelInputs) {
      dimensionLevelInputElmts = this.state.dimensionLevelInputs.map(
        (dimension, index) => {
          const key = `dimension-level-input-${index}`;

          return (
            // @ts-ignore
            <li
              key={key}
              className="mmui-dimension-input"
              data-value={dimension.value}
              data-label={dimension.label}
              draggable={true}
              onDragStart={this.onDimensionDragStart}
            >
              <i className="fas fa-grip-vertical"></i>
              <span>{dimension.label}</span>
              <a
                href="#"
                className="mmui-dimension-reset"
                data-value={dimension.value}
                onClick={this.onDimensionReset}
              >
                <i className="fa fa-times-circle"></i>
              </a>
              <input
                type="hidden"
                id="id_dim_level"
                name="dim_level"
                value={dimension.value}
                data-label={dimension.label}
              />
            </li>
          );
        }
      );

      dimensionLevelInputFieldElmt = (
        <ul className="mmui-dimensions mmui-dimension-level-inputs mmui-text-list">
          {dimensionLevelInputElmts}
        </ul>
      );
    } else {
      dimensionLevelInputFieldElmt = this.getEmptyState('dimension levels');
    }

    // render dimension pivot element and hidden input
    if (hasDimensionPivotInputs) {
      dimensionPivotInputElmts = this.state.dimensionPivotInputs.map(
        (dimension, index) => {
          const key = `dimension-pivot-input-${index}`;

          return (
            // @ts-ignore
            <li
              key={key}
              className="mmui-dimension-input"
              data-value={dimension.value}
              data-label={dimension.label}
              draggable={true}
              onDragStart={this.onDimensionDragStart}
            >
              <i className="fas fa-grip-vertical"></i>
              <span>{dimension.label}</span>
              <a
                href="#"
                className="mmui-dimension-reset"
                data-value={dimension.value}
                onClick={this.onDimensionReset}
              >
                <i className="fa fa-times-circle"></i>
              </a>
              <input
                type="hidden"
                id="id_dim_pivot"
                name="dim_pivot"
                value={dimension.value}
                data-label={dimension.label}
              />
            </li>
          );
        }
      );

      dimensionPivotInputFieldElmt = (
        <ul className="mmui-dimensions mmui-dimension-pivot-inputs mmui-text-list">
          {dimensionPivotInputElmts}
        </ul>
      );
    } else {
      dimensionPivotInputFieldElmt = this.getEmptyState('pivot columns');
    }

    return (
      <div>
        <h4 className="mmui-mb-2">Available Dimensions</h4>
        <div
          className={`mmui-dimension-drop ${hasDimensions ? '' : 'empty'}`}
          data-name="dimensions"
          onDragOver={this.onDimensionDragOver}
          onDrop={this.onDimensionDrop}
        >
          {dimensionFieldElmt}
        </div>
        <h4 className="mmui-mb-2">Dimensions</h4>
        <div
          className={`mmui-dimension-drop ${hasDimensionInputs ? '' : 'empty'}`}
          data-name="dimensionInputs"
          onDragOver={this.onDimensionDragOver}
          onDrop={this.onDimensionDrop}
        >
          {dimensionInputFieldElmt}
        </div>
        <h4 className="mmui-mb-2">Dimension Levels</h4>
        <div
          className={`mmui-dimension-drop ${
            hasDimensionLevelInputs ? '' : 'empty'
          }`}
          data-name="dimensionLevelInputs"
          onDragOver={this.onDimensionDragOver}
          onDrop={this.onDimensionDrop}
        >
          {dimensionLevelInputFieldElmt}
        </div>
        <h4 className="mmui-mb-2">Pivot Columns</h4>
        <div
          className={`mmui-dimension-drop ${
            hasDimensionPivotInputs ? '' : 'empty'
          }`}
          data-name="dimensionPivotInputs"
          onDragOver={this.onDimensionDragOver}
          onDrop={this.onDimensionDrop}
        >
          {dimensionPivotInputFieldElmt}
        </div>
      </div>
    );
  }
}
