import * as React from 'react';
import * as classNames from 'classnames';
import {Component, HTMLProps, SFC, ReactElement, ReactNode, Ref} from 'react';
import {DatagridCards} from './DatagridCards';
import {DatagridFilters} from './DatagridFilters';
import {DatagridHeader} from './DatagridHeader';
import {DatagridPagination} from './DatagridPagination';
import {DatagridTable, TableProps} from './DatagridTable';
import {ColumnHeaderProperty, Query, SelectOption, ViewMode} from './models';
import {Scrollbars} from './../';

export interface DatagridProps {
  defaultFilterDisabled?: boolean;
  filtersActive?: boolean;
  progress?: number;
  loading?: boolean;
  limitOptions?: number[];
  mainButton?: HTMLProps<HTMLAnchorElement>;
  noDefaultFilter?: boolean;
  noFilters?: boolean;
  noFiltersHeader?: boolean;
  noHeader?: boolean;
  noPagination?: boolean;
  noSearchBarCollapse?: boolean;
  noSearchBy?: boolean;
  noTotalInHeader?: boolean;
  noViewModeButton?: boolean;
  properties?: ColumnHeaderProperty[];
  query?: Query;
  searchInputPlaceholder?: string;
  searchPropertyOptions?: SelectOption[];
  searchPropertyValue?: string;
  searchTerm?: string;
  total?: number;
  tableMinWidth?: number;
  viewMode?: ViewMode;
  viewModeButtons?: ViewMode[];
  viewModeCustomElement?: ReactNode;
  onChangeQuery?: (updatedQuery: Query) => void;
  onChangeSearchProperty?: (searchProperty: string) => void;
  onChangeViewMode?: (viewMode: ViewMode) => void;
  onResetAllFilters?: () => void;
  onSearchTerm?: (term: string) => void;
  onToggleFilters?: (active: boolean) => void;
}

export interface DatagridState {
  filtersActive: boolean;
  viewMode: ViewMode;
}

const style = {
  progress: (progress: number) => ({width: `${progress}%` }),
};

export class Datagrid extends Component<DatagridProps, DatagridState> {
  static propTypes: any = {
    children: (props, propName, componentName) => {
      const children: any[] = props[propName] && (Array.isArray(props[propName])
        ? props[propName]
        : [props[propName]]);

      if (!children) { return; }

      const childrenHasNotAllowedElements = children
        .some(c => typeof c.type === 'string' ||
          (c.type.displayName !== 'DatagridTableView' &&
          c.type.displayName !== 'DatagridCardsView' &&
          c.type.displayName !== 'DatagridMapView' &&
          c.type.displayName !== 'DatagridFiltersSlot'));

      if (childrenHasNotAllowedElements) {
        console.warn('Prop `' + propName + '` supplied to `' + componentName +
          '` has invalid elements.' + ' `' + componentName +
          '` only accepts children of type `DatagridTableView`, `DatagridCardsView`, `DatagridMapView` and `DatagridFiltersSlot`.' +
          ' Others components will be ignored.');
        return;
      }
    },
  };

  static defaultProps: Partial<DatagridProps> = {
    defaultFilterDisabled: false,
    filtersActive: true,
    loading: false,
    progress: 100,
    limitOptions: [50, 100, 200],
    mainButton: undefined,
    noDefaultFilter: false,
    noFilters: false,
    noFiltersHeader: false,
    noHeader: false,
    noPagination: false,
    noSearchBarCollapse: false,
    noSearchBy: false,
    noTotalInHeader: false,
    noViewModeButton: false,
    properties: [],
    query: {
      sort: '',
      limit: 50,
      skip: 0,
    },
    searchInputPlaceholder: 'Search',
    searchPropertyOptions: [],
    searchPropertyValue: '',
    searchTerm: '',
    tableMinWidth: 1000,
    total: 0,
    viewMode: ViewMode.Table,
    viewModeButtons: [
      ViewMode.Table,
      ViewMode.Cards,
      ViewMode.Map,
    ],
    viewModeCustomElement: null,
    onChangeQuery: () => { return; },
    onChangeSearchProperty: () => { return; },
    onChangeViewMode: () => { return; },
    onResetAllFilters: () => { return; },
    onSearchTerm: () => { return; },
    onToggleFilters: () => { return; },
  };

  constructor(props: DatagridProps) {
    super(props);

    this.state = {
      filtersActive: props.filtersActive,
      viewMode: props.viewMode,
    };
  }

  componentWillReceiveProps(nextProps: DatagridProps) {
    if (this.props.viewMode !== nextProps.viewMode) {
      this.setState({viewMode: nextProps.viewMode});
    }

    if (this.props.filtersActive !== nextProps.filtersActive) {
      this.setState({filtersActive: nextProps.filtersActive});
    }
  }

  render() {
    const {
      loading,
      progress,
      defaultFilterDisabled,
      noDefaultFilter,
      noFilters,
      noFiltersHeader,
      noHeader,
      noPagination,
      noSearchBarCollapse,
      noSearchBy,
      noTotalInHeader,
      noViewModeButton,
      limitOptions,
      mainButton,
      properties,
      query,
      searchInputPlaceholder,
      searchPropertyOptions,
      searchPropertyValue,
      searchTerm,
      tableMinWidth,
      total,
      viewModeButtons,
      viewModeCustomElement,
      onChangeSearchProperty,
      onResetAllFilters,
      onSearchTerm} = this.props;

    const {filtersActive, viewMode} = this.state;

    if (!this.props.children) { return null; }

    const children = this.getChildrenAsArray();

    const filters = children.filter(node => (node.type as SFC<HTMLDivElement>).displayName === 'DatagridFiltersSlot');

    return (
      <div className="bkn-datagrid">
        <div className={classNames('bkn-datagrid-inner', {'filters-active': !noFilters && filtersActive})}>
          <div className="bkn-datagrid-data-column">
            {!noHeader && (
              <DatagridHeader
                hideSearchByField={noSearchBy}
                hideToggleFilterButton={noFilters}
                hideViewModeButton={noViewModeButton}
                hideTotalInfo={noTotalInHeader}
                mainButton={mainButton}
                noSearchBarCollapse={noSearchBarCollapse}
                searchInputPlaceholder={searchInputPlaceholder}
                searchPropertyOptions={searchPropertyOptions}
                searchPropertyValue={searchPropertyValue}
                searchTerm={searchTerm}
                total={total}
                viewMode={viewMode}
                viewModeButtons={viewModeButtons}
                viewModeCustomElement={viewModeCustomElement}
                onTypeSearchTerm={onSearchTerm}
                onChangeSearchProperty={(e) => onChangeSearchProperty(e.target.value)}
                onChangeViewMode={this.handleChangeViewMode}
                onClickToggleFiltersButton={this.handleToggleFilters}
              />
            )}

            <div
              className={classNames(
                'progress',
                {
                  'progress-active': loading,
                  'no-top': noHeader,
                },
              )}
            >
              <div
                className="progress-bar progress-bar-striped progress-bar-animated"
                role="progressbar"
                aria-valuenow="100"
                aria-valuemin="0"
                aria-valuemax="100"
                style={style.progress(progress)}
              />
            </div>

            {this.renderTableView(children)}
            {this.renderCardsView(children)}
            {this.renderMapView(children)}

            {!noPagination && (
              <DatagridPagination
                limitOptions={limitOptions}
                limit={query.limit}
                skip={query.skip}
                total={total}
                onChangeLimit={this.handleQueryChange('limit')}
                onChangeSkip={this.handleQueryChange('skip')}
              />
            )}
          </div>

          {!noFilters && (
            <div className="bkn-datagrid-filter-column">
              <DatagridFilters
                defaultFilterDisabled={defaultFilterDisabled}
                noHeader={noFiltersHeader}
                noDefaultFilter={noDefaultFilter}
                properties={properties}
                sort={query.sort}
                onClickClose={this.handleToggleFilters}
                onClickResetAll={onResetAllFilters}
                onSort={this.handleQueryChange('sort')}
              >
                {filters}
              </DatagridFilters>
            </div>
          )}
        </div>
      </div>
    );
  }

  renderTableView = (children: ReactElement<any>[]) => {
    const {properties, query, tableMinWidth} = this.props;
    const {viewMode} = this.state;
    const tableView = children.filter(node => (node.type as SFC<HTMLDivElement>).displayName === 'DatagridTableView');

    const visible = viewMode === ViewMode.Table && tableView && tableView.length > 0;

    return visible && (
      <DatagridTable
        properties={properties}
        sort={query.sort}
        tableMinWidth={tableMinWidth}
        onSort={this.handleQueryChange('sort')}
      >
        {tableView}
      </DatagridTable>
    );
  }

  renderCardsView = (children: ReactElement<any>[]) => {
    const {viewMode} = this.state;
    const cardsView = children.filter(node => (node.type as SFC<HTMLDivElement>).displayName === 'DatagridCardsView');
    const visible = viewMode === ViewMode.Cards && cardsView && cardsView.length > 0;

    return visible && (
      <DatagridCards>{cardsView}</DatagridCards>
    );
  }

  renderMapView(children: ReactElement<any>[]) {
    const {viewMode} = this.state;
    const mapView = children.find(node => (node.type as SFC<HTMLDivElement>).displayName === 'DatagridMapView');
    const visible = viewMode === ViewMode.Map && mapView;

    const mapViewClone = React.cloneElement(mapView, {
      ...mapView.props,
      visible,
    });

    return mapViewClone;
  }

  handleToggleFilters = () => {
    this.setState({filtersActive: !this.state.filtersActive}, () => {
      this.props.onToggleFilters(this.state.filtersActive);
    });
  }

  handleChangeViewMode = (viewMode: ViewMode) => {
    this.setState({viewMode});
    this.props.onChangeViewMode(viewMode);
  }

  handleQueryChange = (queryProp: string) => (value: any) => {
    const {query, onChangeQuery} = this.props;

    const updatedQuery = {...query, [queryProp]: value};
    this.props.onChangeQuery(updatedQuery);
  }

  getChildrenAsArray() {
    return React.Children.toArray(this.props.children) as ReactElement<any>[];
  }
}
