content: import React, { Component } from 'react';
  import { Button, Intent } from '@blueprintjs/core';
  import { TagInput } from '@blueprintjs/labs';
  import localforage from 'localforage';
  import { times, DRAFTED_FLAGS, FILTER_TYPES, TAG_TYPES } from './utils';
  import processed from './data/processed';
  import Filters from './Filters';
  import KeyHints from './KeyHints';
  import MyTeam from './MyTeam';
  import PlayerTable from './PlayerTable';
  import Tiers from './Tiers';
  import '@blueprintjs/core/dist/blueprint.css';
  import '@blueprintjs/labs/dist/blueprint-labs.css';
  import '@blueprintjs/table/dist/table.css';
  import './normalize.css';
  import './App.css';
  
  const INTENTS = [Intent.PRIMARY, Intent.SUCCESS, Intent.DANGER, Intent.WARNING, Intent.NONE];
  const DIGIT_REGEX = /\d/g;
  const LETTERS_REGEX = /\D/g;
  const STORAGE_DB_NAME = 'fantasy-football';
  const STORAGE_DB_VERSION = 3.0;
  
  window.FF = {};
  
  class App extends Component {
  
    constructor(props) {
      super(props);
  
      const allPlayers = Object.keys(processed).map((playerId, index) => {
        return processed[playerId];
      });
      const filteredPlayers = allPlayers.slice(0);
  
      const tiers = {};
      allPlayers.forEach((player) => {
        if (!player.isDefense) {
          const pos = player.position;
          const tier = player.tier;
          tiers[pos] = tiers[pos] || {};
          tiers[pos][tier] = tiers[pos][tier] || [];
          tiers[pos][tier].push(player);
        }
      });
  
      this.state = {
        allPlayers,
        allPlayersById: processed,
        drafted: {},
        filters: {
          [FILTER_TYPES.DEPTH]: {},
          [FILTER_TYPES.POSITION]: {},
        },
        filteredPlayers,
        focusedCell: {
          col: 0,
          row: 0,
        },
        myTeam: {},
        previousSortSettings: null,
        sortedIndexMap: [],
        tags: [],
        tiers,
      };
  
      localforage.ready().then(() => {
        this.offlineStorage = localforage.createInstance({
           name: STORAGE_DB_NAME,
           version: STORAGE_DB_VERSION,
        });
  
        const drafted = {};
        const myTeam = {};
  
        this.offlineStorage.iterate((flag, playerId, iterationNumber) => {
          if (flag === DRAFTED_FLAGS.OTHER_TEAM) {
            drafted[playerId] = true;
          }
          if (flag === DRAFTED_FLAGS.MY_TEAM) {
            drafted[playerId] = true;
            myTeam[playerId] = true;
          }
        })
        .then(() => {
          window.FF.drafted = drafted;
          window.FF.myTeam = myTeam;
          console.info('LOADED FROM STORAGE', { drafted, myTeam });
          this.setState({
            drafted,
            myTeam,
          });
        });
      }).catch((e) => {
        console.error(e);
      });
  
      // TEST UTILS //////////////////////////////////////////////////////////////
      let testInterval = null;
      window.FF.test = () => {
        let row = 0;
        let col = 0;
        let isForMe = true;
        const isUndraft = false;
        testInterval = window.setInterval(() => {
          this.setState({
            focusedCell: {
              row,
              col,
            },
          }, () => {
            this.draftPlayer({ isForMe, isUndraft });
            row++;
            isForMe = !isForMe;
            if (row > allPlayers.length - 1) {
              window.FF.stopTest();
            }
          });
        }, 1);
      };
  
      window.FF.stopTest = () => {
        window.clearInterval(testInterval);
      };
      // END TEST UTILS //////////////////////////////////////////////////////////
  
    }
  
    componentWillMount() {
      window.addEventListener('keydown', this.handleKeyDown);
    }
  
    componentWillUnmount() {
      window.removeEventListener('keydown', this.handleKeyDown);
    }
  
    draftPlayer({ isForMe, isUndraft } = {}) {
      const { drafted, focusedCell, myTeam } = this.state;
      const rowIndex = focusedCell.row;
      const player = this.getPlayerByRowIndex(rowIndex);
      if (!player) {
        console.error('Could not get player from row. ROW = ', rowIndex);
        return;
      }
      const { playerId } = player;
      const newState = {
        drafted: {
          ...drafted,
          [playerId]: true,
        },
        myTeam: {
          ...myTeam,
        },
      };
      if (isForMe) {
        newState.myTeam[playerId] = true;
      }
      if (isUndraft || !isForMe) {
        delete newState.myTeam[playerId];
      }
      if (isUndraft) {
        delete newState.drafted[playerId];
      }
      window.FF.drafted = newState.drafted;
      window.FF.myTeam = newState.myTeam;
      this.setState(newState, this.saveToStorage({
        playerId,
        isForMe,
        isUndraft,
      }));
    }
  
    filterPlayers() {
      const {
        allPlayers,
        filters,
        previousSortSettings,
        tags,
      } = this.state;
      let filteredPlayers = null;
      const playersMatchingFilters = {};
      const playersMatchingTags = {};
  
      if (tags.length === 0) {
        filteredPlayers = allPlayers.slice(0);
        allPlayers.forEach((player) => {
          playersMatchingFilters[player.playerId] = true;
        });
      } else {
  
        // Whitelist players based on tags.
        allPlayers.forEach((player) => {
          tags.forEach((tag) => {
            if (tag.type === TAG_TYPES.NAME) {
              if (player.firstName.includes(tag.text) || player.lastName.includes(tag.text)) {
                playersMatchingTags[player.playerId] = true;
                playersMatchingFilters[player.playerId] = true;
              }
            } else if (tag.type === TAG_TYPES.TIER) {
              if (player.position === tag.position && player.tier === tag.tier) {
                playersMatchingTags[player.playerId] = true;
                playersMatchingFilters[player.playerId] = true;
              }
            }
          });
        });
        filteredPlayers = allPlayers.filter(player => playersMatchingTags[player.playerId]);
      }
  
      // Blacklist based on filters.
      const depthMustMatch = Object.keys(filters[FILTER_TYPES.DEPTH]).length > 0;
      const positionMustMatch = Object.keys(filters[FILTER_TYPES.POSITION]).length > 0;
  
      if (depthMustMatch || positionMustMatch) {
        filteredPlayers.forEach((player) => {
          let depthMatch = false;
          let positionMatch = false;
          if (depthMustMatch) {
            Object.keys(filters[FILTER_TYPES.DEPTH]).forEach((depthKey) => {
              const depthFilter = parseInt(depthKey, 10);
              if (player.depth === depthFilter) depthMatch = true;
            });
          }
          if (positionMustMatch) {
            Object.keys(filters[FILTER_TYPES.POSITION]).forEach((positionFilter) => {
              if (player.position === positionFilter) positionMatch = true;
            });
          }
          if (depthMustMatch && !depthMatch) delete playersMatchingFilters[player.playerId];
          if (positionMustMatch && !positionMatch) delete playersMatchingFilters[player.playerId];
        });
        filteredPlayers = filteredPlayers.filter((player) => {
          return playersMatchingFilters[player.playerId]
        });
      }
  
      let sortedIndexMap = [];
      if (previousSortSettings) {
        sortedIndexMap = this.generateSortedIndexMap({ players: filteredPlayers, sortSettings: previousSortSettings });
      }
  
      this.setState({
        filteredPlayers,
        sortedIndexMap,
      });
    }
  
    getPlayerByRowIndex = (rowIndex) => {
      const sortedRowIndex = this.state.sortedIndexMap[rowIndex];
      if (sortedRowIndex != null) {
        rowIndex = sortedRowIndex;
      }
      return this.state.filteredPlayers[rowIndex];
    };
  
    getRowClassName = ({ playerId, rowIndex }) => {
      let className = null;
      if (this.state.focusedCell && this.state.focusedCell.row === rowIndex) {
        // intent = Intent.PRIMARY;
        className = 'activeCell';
      }
      if (this.state.drafted[playerId]) className = 'draftedCell'; // intent = Intent.WARNING;
      if (this.state.myTeam[playerId]) className = 'myTeamCell'; // intent = Intent.SUCCESS;
      return className;
    };
  
    getTagProps = (tag, index) => ({
      intent: INTENTS[index % INTENTS.length],
    });
  
    handleFiltersChange = (filters) => {
      this.setState({
        filters,
      }, this.filterPlayers);
    };
  
    handleFocusedCellChange = ({ col, row }) => {
      this.setState({
        focusedCell: { col, row },
      });
    };
  
    handleKeyDown = (e) => {
      if (e.metaKey || e.ctrlKey) {
        if (e.key === 'Enter') {
          this.draftPlayer({ isForMe: false, isUndraft: false });
          e.preventDefault();
          e.stopPropagation();
        }
        if (e.key === 'm') {
          this.draftPlayer({ isForMe: true, isUndraft: false });
          e.preventDefault();
          e.stopPropagation();
        }
        if (e.key === 'u') {
          this.draftPlayer({ isForMe: false, isUndraft: true });
          e.preventDefault();
          e.stopPropagation();
        }
      }
      if (e.key === 'ArrowDown') {
        const { filteredPlayers, focusedCell } = this.state;
        const { col, row } = focusedCell;
        this.setState({
          focusedCell: {
            col,
            row: Math.min(row + 1, filteredPlayers.length - 1),
          },
        });
      }
      if (e.key === 'ArrowUp') {
        const { focusedCell } = this.state;
        const { col, row } = focusedCell;
        this.setState({
          focusedCell: {
            col,
            row: Math.max(row - 1, 0),
          },
        });
      }
    };
  
    handleReset = (e) => {
      const filteredPlayers = this.state.allPlayers.slice(0);
      this.setState({
        filters: {
          [FILTER_TYPES.DEPTH]: {},
          [FILTER_TYPES.POSITION]: {},
        },
        filteredPlayers,
        focusedCell: {
          col: 0,
          row: 0,
        },
        previousSortSettings: null,
        sortedIndexMap: [],
        tags: [],
      });
    };
  
    handleTagAdd = (tag) => {
      if (DIGIT_REGEX.test(tag)) {
        const position = tag.replace(DIGIT_REGEX, '').trim().toUpperCase();
        const tier = parseInt(tag.replace(LETTERS_REGEX, '').trim(), 10);
        tag = {
          type: TAG_TYPES.TIER,
          position,
          tier,
          text: `${ position }${ tier }`,
        };
      } else {
        tag = {
          type: TAG_TYPES.NAME,
          text: tag,
        };
      }
      this.setState({
        tags: [
          ...this.state.tags,
          tag,
        ],
      }, this.filterPlayers);
    };
  
    handleTagRemove = (tag, index) => {
      this.setState({
        tags: this.state.tags.filter((_, i) => i !== index),
      }, this.filterPlayers);
    };
  
    handleTierFilterClick = (e) => {
      const { position, tier } = e.currentTarget.dataset;
      this.handleTagAdd(`${ position }${ tier }`);
    };
  
    generateSortedIndexMap = ({ players, sortSettings }) => {
      const { comparator, field, isAsc } = sortSettings;
      const sortedIndexMap = times(players.length);
      sortedIndexMap.sort((a, b) => comparator(players[a][field], players[b][field]));
      if (isAsc) sortedIndexMap.reverse();
      return sortedIndexMap;
    };
  
    saveToStorage({ playerId, isForMe, isUndraft }) {
      if (isUndraft) {
        this.offlineStorage.removeItem(playerId)
        .then(() => {
          // NOOP
        })
        .catch((err) => {
          console.error('Error UN-drafting player.', { playerId, isForMe, isUndraft }, err);
          alert(`Error UN-drafting ${ playerId }.`);
        });
      } else {
        const flag = isForMe ? DRAFTED_FLAGS.MY_TEAM : DRAFTED_FLAGS.OTHER_TEAM;
        this.offlineStorage.setItem(playerId, flag)
        .then(() => {
          // NOOP
        })
        .catch((err) => {
          console.error('Error drafting player.', { playerId, isForMe, isUndraft }, err);
          alert(`Error drafting ${ playerId } ${ isForMe ? 'FOR ME' : ''}.`);
        });
      }
    }
  
    sortColumn = ({ comparator, field, isAsc }) => {
      const sortSettings = { comparator, field, isAsc };
      this.setState({
        previousSortSettings: sortSettings,
        sortedIndexMap: this.generateSortedIndexMap({ players: this.state.filteredPlayers, sortSettings }),
      });
    };
  
    refHandlers = {
      playerTable: el => this.playerTableRef = el,
    };
  
    render() {
      const {
        allPlayersById,
        drafted,
        filters,
        focusedCell,
        myTeam,
        tags,
        tiers,
      } = this.state;
      const tagsText = tags.map(tag => tag.text);
      return (
        <div className="App">
  
          {/* TIERS */}
          <Tiers
            drafted={ drafted }
            onTierFilterClick={ this.handleTierFilterClick }
            tiers={ tiers }
          />
  
          {/* FILTERS */}
          <Filters
            filters={ filters }
            onChange={ this.handleFiltersChange }
          />
  
          {/* SEARCH */}
          <div id="search" className="flextainer flexitemscenter">
            <div className="pt-tag pt-large instructionsTag">
              SHOW ME
            </div>
            <div className="flex1" style={{ padding: '5px 0' }}>
              <TagInput
                className="pt-large"
                onAdd={ this.handleTagAdd }
                onRemove={ this.handleTagRemove }
                tagProps={ this.getTagProps }
                values={ tagsText }
              />
            </div>
            <div
              className="pt-monospace-text flextainer flexcol flexitemscenter flexcenter"
            >
              <div style={{ padding: '15px 10px 20px', fontWeight: 900, fontSize: '24px', color: '#6aae6a' }}>
                { this.state.filteredPlayers.length }
              </div>
              <Button
                intent={ Intent.DANGER }
                onClick={ this.handleReset }
                iconName="repeat"
                text="Clear"
                style={{ marginBottom: -30 }}
              />
            </div>
          </div>
  
          {/* KEY HINTS */}
          <KeyHints />
  
          {/* PLAYER TABLE */}
          <PlayerTable
            ref={ this.refHandlers.playerTable }
            focusedCell={ focusedCell }
            getPlayerByRowIndex={ this.getPlayerByRowIndex }
            getRowClassName={ this.getRowClassName }
            numRows={ this.state.filteredPlayers.length }
            onFocusedCellChange={ this.handleFocusedCellChange }
            sortColumn={ this.sortColumn }
          />
  
          {/* MY TEAM */}
          <MyTeam
            allPlayersById={ allPlayersById }
            myTeam={ myTeam }
          />
  
        </div>
      );
    }
  }
  
  export default App;
  