import React from 'react';
import { Table, Column } from 'fixed-data-table';
import CalendarCell from './CalendarCell';
import CalendarCellHeader from './CalendarCellHeader';
import Dimensions from 'utils/Dimensions';
import {
  loadMoreCalendarEvents,
  changeCalendarSort,
  freezeCalendarColumns,
  changeCalendarWidths
  } from 'actions/calendars';

export const SortTypes = {
  ASC: 'ASC',
  DESC: 'DESC',
};

const ROW_HEIGHT = 21;
class CalendarTable extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      hoveredRow: -1,
    };
  }

  getAdjustedEvents() {
    let events = this.props.events;
    events = this.sortEvents(events);
    return events;
  }

  handleSortChange(columnKey) {
    const { displayOptions, parameters, id, dispatch } = this.props;
    const { colSortDirs = {} } = displayOptions;
    const { calendarType } = parameters;

    const currentDir = colSortDirs[calendarType][columnKey];
    let sortDir = SortTypes.ASC;
    if (currentDir && currentDir === SortTypes.ASC) {
      sortDir = SortTypes.DESC;
    }
    colSortDirs[calendarType] = {[columnKey]: sortDir};
    dispatch(
      changeCalendarSort(id, displayOptions, colSortDirs)
    );
  }

  sortEvents(events) {
    const { displayOptions, parameters, fields } = this.props;
    const { colSortDirs = {} } = displayOptions;
    const { calendarType } = parameters;

    const columnKeys = Object.keys(colSortDirs[calendarType]);
    const sortDirs = Object.values(colSortDirs[calendarType]);

    // Clean up a value before sorting with it.
    // Ensure strings are lower case
    // Turn number-strings into numbers
    // Turn percentages into numbers
    const cleanValue = (oldValue) => {
      let value = oldValue;
      if (typeof value === 'string') {
        value = value.toLowerCase();

        let maybeNum = value;
        if (maybeNum.charAt(maybeNum.length - 1) === '%') {
          maybeNum = maybeNum.substring(0, maybeNum.length - 1);
        }

        if (!isNaN(maybeNum)) {
          value = parseFloat(maybeNum);
        }
      }

      return value;
    };

    const compareColumns = (colA, colB, compareIndex) => {
      const sortDir = sortDirs[compareIndex];
      const columnKey = columnKeys[compareIndex];
      let valA = colA[columnKey];
      let valB = colB[columnKey];
      if (columnKey in fields && 'computeValue' in fields[columnKey]) {
        valA = fields[columnKey].computeValue(colA);
        valB = fields[columnKey].computeValue(colB);
      }

      valA = cleanValue(valA);

      valB = cleanValue(valB);

      const nextCompareIndex = compareIndex + 1;
      if (valA === valB) {
        if ( nextCompareIndex < sortDirs.length) {
          return compareColumns(colA, colB, nextCompareIndex);
        }

        return 0;
      }

      if (valA === undefined || valA === null || valA === ('∞%') || valA === ''
          || typeof valA === 'number' && (isNaN(valA) || !isFinite(valA))) {
        return 1;
      } else if (valB === undefined || valB === null || valB === ('∞%') || valB === ''
          || typeof valB === 'number' && (isNaN(valB) || !isFinite(valB))) {
        return -1;
      }

      if (sortDir === SortTypes.ASC) {
        return valA > valB ? 1 : -1;
      }

      return valA < valB ? 1 : -1;
    };

    if (columnKeys && sortDirs) {
      return events.slice().sort(function(a, b) {
        return compareColumns(a, b, 0);
      });
    }
    return events;
  }

  handleColumnResizeEnd(newColumnWidth, columnKey) {
    const { displayOptions, parameters, id, dispatch } = this.props;
    const { calendarType } = parameters;
    const colWidths = Object.assign({}, displayOptions.colWidths);
    colWidths[calendarType][columnKey] = newColumnWidth;
    dispatch(changeCalendarWidths(id, displayOptions, colWidths));
  }

  handleScroll(x, y) {
    const { isLoading, canLoadMore, containerHeight, id, dispatch } = this.props;
    const dataList = this.getAdjustedEvents();

    // How many pixels are used by what we are shown (and thus cannot scroll)
    // Subtract ROW_HEIGHT to handle the header
    const onSreenY = containerHeight - ROW_HEIGHT;
    // Load more when you are within 6 rows of the end of the calendar
    const scrollThreshold = (dataList.length - 6) * ROW_HEIGHT - onSreenY;

    const loadingMore = (isLoading && (dataList && dataList.length !== 0));

    if (y > scrollThreshold && !loadingMore && canLoadMore) {
      dispatch(loadMoreCalendarEvents(id));
    }
  }

  freezeColumn(columnKey) {
    const { displayOptions, parameters, id, dispatch } = this.props;
    const { colFrozen = {} } = displayOptions;
    const{ calendarType} = parameters;
    colFrozen[calendarType][columnKey] = !colFrozen[calendarType][columnKey];
    dispatch(
      freezeCalendarColumns(id, displayOptions, colFrozen)
    );
  }

  columns(dataList) {
    const { hoveredRow } = this.state;
    const { displayOptions, parameters, fields } = this.props;
    const { colWidths = {}, colHidden = {}, colFrozen = {}, colSortDirs = {} } = displayOptions;
    const { calendarType } = parameters;
    const columnWidths = colWidths[calendarType];

    const shownFields = Object.keys(fields).filter((columnKey) => !colHidden[calendarType][columnKey]);

    return shownFields.map((columnKey, colKeyIndex) => {
      const minimumWidth = 50;
      const colWidth = columnWidths[columnKey] || minimumWidth;

      const sortDir = colSortDirs[calendarType][columnKey];
      const isColFrozen = colFrozen[calendarType][columnKey];
      return (
        <Column
          key={columnKey}
          columnKey={columnKey}
          isResizable
          flexGrow={1}
          fixed={false}
          width={colWidth}
          minWidth={minimumWidth}
          header={
            <CalendarCellHeader
              sortDir={sortDir}
              isColFrozen={false}
              listName={fields[columnKey].listName}
              changeSort={() => this.handleSortChange(columnKey)}
              freezeColumn={() => this.freezeColumn(columnKey)}
            />
          }
          cell={
            <CalendarCell
              data={dataList}
              parameters={parameters}
              field={columnKey}
              fieldData={fields[columnKey]}
              hoveredRow={hoveredRow}
            />
          }
        />
      );
    });
  }

  render() {
    const { isLoading, events, containerWidth, containerHeight } = this.props;
    const dataList = this.getAdjustedEvents();
    // If loading initial events
    if (isLoading && !(events && events.length !== 0)) {
      return (
        <div>
          Loading...
        </div>
      );
    }
    if (events.length <= 0 ) {
      return (
        <div>
          No calendar data available for the applied filters
        </div>
      );
    }

    // Note that isColumnResizing needs to be explicitly set to false
    return (
      <div className="Calendar-data">
        <Table
          rowHeight={ROW_HEIGHT}
          onColumnResizeEndCallback={(colWidth, colKey) => this.handleColumnResizeEnd(colWidth, colKey)}
          isColumnResizing={false}
          headerHeight={ROW_HEIGHT}
          overflowX="auto"
          overflowY="auto"
          rowsCount={dataList.length}
          width={containerWidth}
          height={containerHeight}
          onScrollEnd={(x, y) => this.handleScroll(x, y)}
          onRowMouseEnter={(e, index) => this.setState({hoveredRow: index})}
          onRowMouseLeave={(e, index) => this.setState({hoveredRow: -1})}
        >
          {this.columns(dataList)}
        </Table>
      </div>
    );
  }
}

CalendarTable.propTypes = {
  id: React.PropTypes.number,
  isLoading: React.PropTypes.bool.isRequired,
  canLoadMore: React.PropTypes.bool.isRequired,
  fields: React.PropTypes.object.isRequired,
  parameters: React.PropTypes.object.isRequired,
  displayOptions: React.PropTypes.object.isRequired,
  events: React.PropTypes.array,
  dispatch: React.PropTypes.func.isRequired,

  // Given by Dimensions
  containerWidth: React.PropTypes.any,
  containerHeight: React.PropTypes.any,
};

const dimensionsOptions = {
  // The default of '100%' doesn't work due to flexbox stuff
  containerStyle: {
    width: '100%',
    padding: 0,
    border: 0,
  },
  // The parent element can resize itself for reasons other than the window being
  // resized (due to widgets being added/removed)
  elementResize: true,
};
export default new Dimensions(dimensionsOptions)(CalendarTable);
