import _ from 'lodash'
import React from 'react'
import autoBind from 'auto-bind'
import { withStyles } from '@material-ui/core/styles'
import Loading from '@src/js/components/common/loading/Loading.jsx'
import Table from '@material-ui/core/Table'
import TableHead from '@material-ui/core/TableHead'
import TableBody from '@material-ui/core/TableBody'
import Header from '@src/js/components/common/form/Header.jsx'
import GridController from '@src/js/components/common/grid/GridController.js'
import GridFilters from '@src/js/components/common/grid/GridFilters.jsx'
import GridHeaders from '@src/js/components/common/grid/GridHeaders.jsx'
import GridSelectionInfo from '@src/js/components/common/grid/GridSelectionInfo.jsx'
import GridRow from '@src/js/components/common/grid/GridRow.jsx'
import GridRowFullWidth from '@src/js/components/common/grid/GridRowFullWidth.jsx'
import GridExports from '@src/js/components/common/grid/GridExports.jsx'
import GridExportLoading from '@src/js/components/common/grid/GridExportLoading.jsx'
import GridExportWarnings from '@src/js/components/common/grid/GridExportWarnings.jsx'
import GridExportError from '@src/js/components/common/grid/GridExportError.jsx'
import GridPaging from '@src/js/components/common/grid/GridPaging.jsx'
import GridColumnsConfig from '@src/js/components/common/grid/GridColumnsConfig.jsx'
import GridFiltersConfig from '@src/js/components/common/grid/GridFiltersConfig.jsx'
import ComponentContext from '@src/js/components/common/ComponentContext.js'
import logger from '@src/js/common/logger.js'

const styles = theme => ({
  container: {
    minWidth: '800px',
    height: '100%'
  },
  loadingContainer: {
    flex: '1 1 auto'
  },
  loading: {
    display: 'inline-block'
  },
  tableContainer: {
    display: 'inline-block',
    minWidth: '100%',
    height: '100%'
  },
  table: {
    borderCollapse: 'unset'
  },
  tableHead: {
    position: 'sticky',
    top: 0,
    zIndex: '200',
    backgroundColor: theme.palette.background.paper
  },
  titleCell: {
    border: 0
  },
  titleContent: {
    paddingLeft: theme.spacing(2)
  },
  title: {
    paddingTop: theme.spacing(1),
    paddingBottom: 0
  },
  pagingAndConfigsAndExportsContent: {
    display: 'flex'
  }
})

class Grid extends React.PureComponent {
  static defaultProps = {
    id: 'grid'
  }

  constructor(props) {
    super(props)
    autoBind(this)

    this.state = {}

    if (this.props.controller) {
      this.controller = this.props.controller
    } else {
      this.controller = new GridController()
    }

    this.controller.init(new ComponentContext(this))

    if (this.props.controllerRef) {
      this.props.controllerRef(this.controller)
    }
  }

  componentDidMount() {
    this.controller.load()
  }

  handleClickContainer() {
    this.controller.handleRowSelect(null)
  }

  handleClickTable(event) {
    event.stopPropagation()
  }

  render() {
    logger.log(logger.DEBUG, 'Grid.render')

    if (!this.state.loaded) {
      return <Loading loading={true}></Loading>
    }

    const { id, classes } = this.props
    const { loading, rows } = this.state

    return (
      <div
        id={id}
        onClick={this.handleClickContainer}
        className={classes.container}
      >
        <div className={classes.loadingContainer}>
          <Loading loading={loading} styles={{ root: classes.loading }}>
            <div className={classes.tableContainer}>
              <Table
                classes={{ root: classes.table }}
                onClick={this.handleClickTable}
              >
                <TableHead classes={{ root: classes.tableHead }}>
                  {this.renderTitle()}
                  {this.renderPagingAndConfigsAndExports()}
                  {this.renderHeaders()}
                  {this.renderFilters()}
                  {this.renderSelectionInfo()}
                </TableHead>
                <TableBody>
                  {rows.map(row => {
                    return this.renderRow(row)
                  })}
                </TableBody>
              </Table>
            </div>
            {this.renderExportState()}
          </Loading>
        </div>
      </div>
    )
  }

  renderTitle() {
    const { header, multiselectable, classes } = this.props

    if (header === null || header === undefined) {
      return null
    }

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridRowFullWidth
        multiselectable={multiselectable}
        columns={visibleColumns}
        styles={{ cell: classes.titleCell, content: classes.titleContent }}
      >
        <div onClick={this.handleClickContainer}>
          <Header styles={{ root: classes.title }}>{header}</Header>
        </div>
      </GridRowFullWidth>
    )
  }

  renderPagingAndConfigsAndExports() {
    const { multiselectable, classes } = this.props

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridRowFullWidth
        multiselectable={multiselectable}
        columns={visibleColumns}
        styles={{
          content: classes.pagingAndConfigsAndExportsContent
        }}
      >
        {this.renderPaging()}
        {this.renderConfigs()}
        {this.renderExports()}
      </GridRowFullWidth>
    )
  }

  renderPaging() {
    const { id } = this.props
    const { page, pageSize, totalCount } = this.state

    return (
      <GridPaging
        id={id}
        count={totalCount}
        page={page}
        pageSize={pageSize}
        onPageChange={this.controller.handlePageChange}
        onPageSizeChange={this.controller.handlePageSizeChange}
      />
    )
  }

  renderConfigs() {
    const { id, filterModes } = this.props
    const { loading, filterMode, columnsVisibility } = this.state

    const allColumns = this.controller.getAllColumns()

    return (
      <React.Fragment>
        <GridColumnsConfig
          id={id}
          columns={allColumns}
          columnsVisibility={columnsVisibility}
          loading={loading}
          onVisibleChange={this.controller.handleColumnVisibleChange}
          onOrderChange={this.controller.handleColumnOrderChange}
        />
        <GridFiltersConfig
          id={id}
          filterModes={filterModes}
          filterMode={filterMode}
          loading={loading}
          onFilterModeChange={this.controller.handleFilterModeChange}
        />
      </React.Fragment>
    )
  }

  renderExports() {
    const { id, multiselectable } = this.props
    const { rows, multiselectedRows, exportOptions } = this.state

    const exportable = this.controller.getExportable()

    if (!exportable) {
      return null
    }

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridExports
        id={id}
        disabled={rows.length === 0}
        exportable={exportable}
        exportOptions={exportOptions}
        multiselectable={multiselectable}
        multiselectedRows={multiselectedRows}
        visibleColumns={visibleColumns}
        onExport={this.controller.handleExport}
        onExportOptionsChange={this.controller.handleExportOptionsChange}
      />
    )
  }

  renderExportState() {
    const { exportState } = this.state

    if (!exportState) {
      return null
    }

    return (
      <React.Fragment>
        <GridExportLoading loading={!!exportState.loading} />
        <GridExportError
          open={!_.isEmpty(exportState.error)}
          error={exportState.error}
          onClose={this.controller.handleExportCancel}
        />
        <GridExportWarnings
          open={!_.isEmpty(exportState.warnings)}
          warnings={exportState.warnings}
          onDownload={() =>
            this.controller.handleExportDownload(
              exportState.fileName,
              exportState.fileUrl
            )
          }
          onCancel={this.controller.handleExportCancel}
        />
      </React.Fragment>
    )
  }

  renderHeaders() {
    const { multiselectable } = this.props
    const { sortings, rows, multiselectedRows } = this.state

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridHeaders
        columns={visibleColumns}
        rows={rows}
        sortings={sortings}
        onSortChange={this.controller.handleSortChange}
        onMultiselectAllRowsChange={
          this.controller.handleMultiselectAllRowsChange
        }
        multiselectable={multiselectable}
        multiselectedRows={multiselectedRows}
      />
    )
  }

  renderFilters() {
    const { id, filterModes, multiselectable } = this.props
    const { filterMode, filters, globalFilter } = this.state

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridFilters
        id={id}
        columns={visibleColumns}
        filterModes={filterModes}
        filterMode={filterMode}
        filters={filters}
        onFilterChange={this.controller.handleFilterChange}
        onFilterModeChange={this.controller.handleFilterModeChange}
        globalFilter={globalFilter}
        onGlobalFilterChange={this.controller.handleGlobalFilterChange}
        multiselectable={multiselectable}
      />
    )
  }

  renderSelectionInfo() {
    const { multiselectable, actions } = this.props
    const { rows, multiselectedRows } = this.state

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridSelectionInfo
        columns={visibleColumns}
        rows={rows}
        actions={actions}
        onExecuteAction={this.controller.handleExecuteAction}
        onMultiselectionClear={this.controller.handleMultiselectionClear}
        multiselectable={multiselectable}
        multiselectedRows={multiselectedRows}
      />
    )
  }

  renderRow(row) {
    const { selectable, multiselectable, onRowClick } = this.props
    const { selectedRow, multiselectedRows, heights } = this.state

    const visibleColumns = this.controller.getVisibleColumns()

    return (
      <GridRow
        key={row.id}
        columns={visibleColumns}
        row={row}
        heights={heights[row.id]}
        clickable={onRowClick ? true : false}
        selectable={selectable}
        selected={selectedRow ? selectedRow.id === row.id : false}
        multiselectable={multiselectable}
        multiselected={multiselectedRows && multiselectedRows[row.id]}
        onClick={this.controller.handleRowClick}
        onSelect={this.controller.handleRowSelect}
        onMultiselect={this.controller.handleRowMultiselect}
        onMeasured={this.controller.handleMeasured}
      />
    )
  }
}

export default withStyles(styles)(Grid)