import 'phaser';
import React, { KeyboardEvent, useEffect, useState, version } from 'react';
import MainScene, { Frame, FrameTileData } from '../scenes/MainScene';
import { createGame } from '../game';
import {
  Button,
  Switch,
  FormControlLabel,
  createMuiTheme,
  ThemeProvider,
  FormGroup,
} from '@material-ui/core';
import './styles.css';
import { LuxMatchConfigs, Game } from '@lux-ai/2021-challenge/lib/es6';
import TileStats from './TileStats';
import {
  hashMapCoords,
  hashToMapPosition,
  mapCoordsToIsometricPixels,
} from '../scenes/utils';
import GlobalStats from './GlobalStats';
import Controller from './Controller';
import ZoomInOut from './ZoomInOut';
import UploadSVG from '../icons/upload.svg';
import { parseReplayData } from '../utils/replays';
import clientConfigs from './configs.json';
import WarningsPanel from './WarningsPanel';
// import debug_replay from './replay.json';
export type GameComponentProps = {
  // replayData?: any;
};

const theme = createMuiTheme({
  palette: {
    primary: {
      main: '#fea201',
    },
    secondary: {
      main: '#3686FF',
    },
  },
});

export const GameComponent = () => {
  const [notifWindowOpen, setNotifWindowOpen] = useState(false);
  const [replayData, setReplayData] = useState(null);
  const [notifMsg, setNotifMsg] = useState('');
  const [running, setRunning] = useState(false);
  const [useKaggleReplay, setUseKaggleReplay] = useState(true);
  const [playbackSpeed, _setPlaybackSpeed] = useState(1);
  const [replayVersion, setReplayVersion] = useState('');
  const [warningMessage, setWarningMessage] = useState('');
  const setPlaybackSpeed = (speed: number) => {
    if (speed >= 0.5 && speed <= 32) {
      _setPlaybackSpeed(speed);
      main.speed = speed;
    }
  };
  const url = new URL(window.location.href);
  const searchlist = url.search.slice(1).split('&');
  let scale =
    searchlist.length > 0 && searchlist[0].split('=')[0] === 'scale'
      ? parseFloat(searchlist[0].split('=')[1])
      : 1.5;
  if (isNaN(scale)) {
    scale = 1.5;
  }
  let zoom = 1 / scale;
  let scaleSize = scale / 10;
  const [visualScale, _setVisualScale] = useState(scale / 4);
  const setVisualScale = (scale: number) => {
    if (scale >= scaleSize && scale <= 2) {
      _setVisualScale(scale);
    }
  };
  const [isReady, setReady] = useState(false);
  const [warningsPanelOpen, setWarningsPanelOpen] = useState(false);
  const [selectedTileData, setTileData] = useState<FrameTileData>(null);
  const [trackedUnitID, setTrackedUnitID] = useState<string>(null);
  const [game, setGame] = useState<Phaser.Game>(null);
  const [main, setMain] = useState<MainScene>(null);
  const [configs, setConfigs] = useState<LuxMatchConfigs>(null);
  const [sliderConfigs, setSliderConfigs] = useState({
    step: 1,
    min: 0,
    max: 1000,
  });

  const [turn, setTurn] = useState(0);
  const [currentFrame, setFrame] = useState<Frame>(null);
  const [uploading, setUploading] = useState(false);
  const fileInput = React.createRef<HTMLInputElement>();

  // If the game changes, put a setup callback to set up controller configs
  useEffect(() => {
    if (game) {
      game.events.on('setup', () => {
        // @ts-ignore
        const main: MainScene = game.scene.scenes[0];
        setMain(main);
        const configs = main.luxgame.configs;
        setConfigs(configs as LuxMatchConfigs);

        setSliderConfigs({
          min: 0,
          max: Math.min(configs.parameters.MAX_DAYS, main.frames.length - 1),
          step: 1,
        });
        setReady(true);
      });
    }
  }, [game]);

  // If play is toggled (running) or playback speed is changed, we update the playback
  useEffect(() => {
    if (running && configs) {
      let currTurn = turn;
      const interval = setInterval(() => {
        if (
          currTurn >=
          Math.min(configs.parameters.MAX_DAYS, main.frames.length - 1)
        ) {
          setRunning(false);
          return;
        }
        currTurn += 1;
        moveToTurn(currTurn);

        setTurn(currTurn);
      }, 1000 / playbackSpeed);
      return () => clearInterval(interval);
    }
  }, [running, playbackSpeed]);

  // if game loaded is ready, move to turn 0 and load that turn's frame
  useEffect(() => {
    if (isReady) {
      moveToTurn(0);
    }
  }, [isReady]);

  // whenever the main scene is changed or visualScale is changed, call main to change the visual scale appropriately.
  useEffect(() => {
    if (main && visualScale) {
      main.overallScale = visualScale;
      if (main.activeImageTile) {
        // main.activeImageTile.setY(main.originalTileY);
        // main.activeImageTile.clearTint();
        // main.activeImageTile = null;
        // main.originalTileY
      }
      // move to current turn to rerender all objects appropriately
      moveToTurn(turn);
      // TODO: do a on scale change instead inside main
      main.floorImageTiles.forEach((info, hash) => {
        [info.source, info.overlay, info.roadOverlay].forEach(
          (tileImage, i) => {
            const pos = hashToMapPosition(hash);
            const ps = mapCoordsToIsometricPixels(pos.x, pos.y, {
              scale: main.overallScale,
              width: main.mapWidth,
              height: main.mapHeight,
            });
            tileImage.setScale(main.defaultScales.block * main.overallScale);
            tileImage.setX(ps[0]);
            tileImage.setY(ps[1]);
            if (tileImage == main.activeImageTile) {
              main.originalTileY = tileImage.y;
            }
            if (tileImage == main.hoverImageTile) {
              main.originalHoverImageTileY = tileImage.y;
            }
          }
        );
      });
      const ps = mapCoordsToIsometricPixels(
        main.mapWidth / 2,
        main.mapWidth / 2,
        {
          scale: main.overallScale,
          width: main.mapWidth,
          height: main.mapHeight,
        }
      );
      // [main.islandbaseImage, main.islandbaseNightImage].forEach((tileImage) => {
      //   tileImage.setX(ps[0]);
      //   let f = 32.3;
      //   if (main.mapWidth <= 16) f = 31.7;
      //   tileImage.setY(ps[1] + main.overallScale * main.mapWidth * f);
      //   tileImage.setScale(
      //     main.defaultScales.islandBase * main.overallScale * main.mapWidth
      //   );
      // });
    }
  }, [main, visualScale]);

  /** handle the change of the slider to move turns */
  const handleSliderChange = (_event: any, newValue: number) => {
    setRunning(false);
    moveToTurn(newValue);
  };

  /** Move to a specific turn and render that turn's frame */
  const moveToTurn = (turn: number) => {
    setTurn(turn);
    main.renderFrame(turn);

    setFrame(main.frames[turn]);
    //render the right bg color
    const colors = [
      '00AFBD',
      '438D91',
      '846D68',
      'A55D53',
      '704A60',
      '4D3D59',
      '2C2E33',
    ];
    const canvasWrapper = document
      .getElementById('content')
      .getElementsByTagName('canvas')[0];
    const dayLength = main.luxgame.configs.parameters.DAY_LENGTH;
    const cycleLength =
      dayLength + main.luxgame.configs.parameters.NIGHT_LENGTH;
    let idx = 0;
    if (
      turn % cycleLength >= dayLength - 5 &&
      turn % cycleLength < dayLength + 1
    ) {
      idx = (turn % cycleLength) - (dayLength - 5);
    } else if (
      turn % cycleLength >= dayLength + 1 &&
      turn % cycleLength < cycleLength - 1
    ) {
      idx = 6;
    } else if (turn % cycleLength >= cycleLength - 1) {
      idx = 5;
    } else if (turn % cycleLength < 5 && turn > 5) {
      idx = 6 - ((turn % cycleLength) + 2);
    }
    canvasWrapper.style.transition = `background-color linear ${
      1 / main.speed
    }s`;
    canvasWrapper.style.backgroundColor = `#${colors[idx]}`;
  };

  /** track a unit by id */
  const trackUnit = (id: string) => {
    setTrackedUnitID(id);
    main.untrackUnit();
    main.trackUnit(id);
  };
  const untrackUnit = (id: string) => {
    setTrackedUnitID(null);
    main.untrackUnit(true);
  };

  /** load game given json replay data */
  const loadGame = (jsonReplayData: any) => {
    let versionMisMatch = false;
    let versionvals = ['x', 'x'];
    setReplayVersion(jsonReplayData.version);
    if (jsonReplayData.version !== undefined) {
      versionvals = jsonReplayData.version.split('.');
      if (
        versionvals[0] !== clientConfigs.version[0] ||
        versionvals[1] !== clientConfigs.version[2]
      ) {
        versionMisMatch = true;
      }
    }
    if (versionMisMatch) {
      let warningMessage = `Replay file works on version ${versionvals[0]}.${versionvals[1]}.x but client is on version ${clientConfigs.version}. The visualizer will not be able to parse this replay file. Download an older visualizer with version ${versionvals[0]}.${versionvals[1]}.x here to watch the replay: https://github.com/Lux-AI-Challenge/LuxViewer2021/releases`;
      setWarningMessage(warningMessage);
      return;
    }
    if (game) {
      game.destroy(true, false);
    }
    setReady(false);
    setReplayData(jsonReplayData);
    const newgame = createGame({
      replayData: jsonReplayData,
      handleTileClicked,
      handleUnitTracked,
      zoom,
    });
    setGame(newgame);
  };

  /** handle uploaded files */
  const handleUpload = () => {
    setUploading(true);
    setUseKaggleReplay(false);
    if (fileInput.current.files.length) {
      const file = fileInput.current.files[0];
      const name = file.name;
      const meta = name.split('.');

      if (meta[meta.length - 1] === 'json') {
        file
          .text()
          .then(JSON.parse)
          .then((data) => {
            setUploading(false);
            data = parseReplayData(data);
            loadGame(data);
          })
          .catch((err) => {
            console.error(err);
            alert(err);
          });
      }
    }
  };
  useEffect(() => {
    //@ts-ignore
    if (window.kaggle) {
      // check if window.kaggle.environment is valid and usable
      if (
        //@ts-ignore
        window.kaggle.environment &&
        //@ts-ignore
        window.kaggle.environment.steps.length > 1
      ) {
        console.log('Embedded kaggle replay detected, parsing it');
        //@ts-ignore
        let replay = window.kaggle.environment;
        replay = parseReplayData(replay);
        loadGame(replay);
      } else {
        console.log(
          'Kaggle detected, but no replay, listening for postMessage'
        );
        // add this listener only once
        window.addEventListener(
          'message',
          (event) => {
            // Ensure the environment names match before updating.
            try {
              if (event.data.environment.name == 'lux_ai_2021') {
                // updateContext(event.data);
                let replay = event.data.environment;
                console.log('post message:');
                console.log(event.data);
                replay = parseReplayData(replay);
                loadGame(replay);
                const el = document.getElementsByTagName('html');
                if (window.innerWidth * 0.65 <= 768) {
                  el[0].style.fontSize = '6pt';
                }
                if (window.innerWidth * 0.65 <= 1280) {
                  el[0].style.fontSize = '8pt';
                }
              }
            } catch (err) {
              console.error('Could not parse game');
              console.error(err);
            }
          },
          false
        );
      }
    }
    // change root font size depending on window size
    const el = document.getElementsByTagName('html');
    if (window.innerWidth <= 768) {
      // set the font size of root html smaller since this is being viewed on the kaggle page
      el[0].style.fontSize = '6pt';
    } else if (window.innerWidth <= 1280) {
      el[0].style.fontSize = '8pt';
    }
    // loadGame(parseReplayData(debug_replay));
  }, []);
  useEffect(() => {
    const handleKeyDown = (event: globalThis.KeyboardEvent) => {
      switch (event.key) {
        case 'ArrowUp':
          setPlaybackSpeed(playbackSpeed * 2);
          break;
        case 'ArrowDown':
          setPlaybackSpeed(playbackSpeed / 2);
          break;
        case 'ArrowRight':
          setRunning(false);
          if (
            turn < Math.min(configs.parameters.MAX_DAYS, main.frames.length - 1)
          ) {
            moveToTurn(turn + 1);
          }
          break;
        case 'ArrowLeft':
          setRunning(false);
          if (turn > 0) {
            moveToTurn(turn - 1);
          }
          break;
      }
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => {
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [turn, playbackSpeed, main, configs]);

  const handleTileClicked = (data: FrameTileData) => {
    setTileData(data);
    // deal with unit tracking, which unfortunately has data fragmented between react and the phaser scene
  };
  const handleUnitTracked = (id: string) => {
    setTrackedUnitID(id);
  };

  const [debugOn, _setDebug] = useState(true);
  const setDebug = (
    e: React.ChangeEvent<HTMLInputElement>,
    checked: boolean
  ) => {
    _setDebug(checked);
    main.debug = checked;
    moveToTurn(turn);
  };
  const renderDebugModeButton = () => {
    return (
      <FormGroup row className="debug-mode-button-wrapper">
        <FormControlLabel
          control={
            <Switch checked={debugOn} onChange={setDebug} name="checkedA" />
          }
          label="Debug Mode"
        />
      </FormGroup>
    );
  };
  let sidetextAnnotations = [];
  if (currentFrame && currentFrame.annotations) {
    sidetextAnnotations = currentFrame.annotations.filter((v) => {
      return (
        v.command.length > 2 &&
        v.command.split(' ')[0] === Game.ACTIONS.DEBUG_ANNOTATE_SIDETEXT
      );
    });
  }
  return (
    <div className="Game">
      <ThemeProvider theme={theme}>
        <div id="content"></div>
        {!isReady && warningMessage === '' && (
          <div className="upload-no-replay-wrapper">
            <p>Welcome to the Lux AI Season 1 Visualizer</p>
            <div>
              <Button
                className="upload-btn"
                color="secondary"
                variant="contained"
                onClick={() => {
                  fileInput.current.click();
                }}
              >
                <span className="upload-text">Upload a replay</span>
                <img className="upload-icon-no-replay" src={UploadSVG} />
              </Button>
              <p></p>
              <input
                accept=".json, .luxr"
                type="file"
                style={{ display: 'none' }}
                onChange={handleUpload}
                ref={fileInput}
              />
            </div>
          </div>
        )}
        {warningMessage !== '' && (
          <div className="upload-no-replay-wrapper">
            <p>{warningMessage}</p>
          </div>
        )}

        <div id="version-number">
          {replayVersion && (
            <>
              <strong>Replay Version: </strong>
              {replayVersion}
              <br></br>
            </>
          )}
          <strong>Client Version: </strong>
          {clientConfigs.version}
        </div>
        {isReady && (
          <div>
            <Controller
              turn={turn}
              moveToTurn={moveToTurn}
              handleUpload={handleUpload}
              running={running}
              isReady={isReady}
              setRunning={setRunning}
              playbackSpeed={playbackSpeed}
              setPlaybackSpeed={setPlaybackSpeed}
              fileInput={fileInput}
              sliderConfigs={sliderConfigs}
              handleSliderChange={handleSliderChange}
            />
            {debugOn && sidetextAnnotations.length > 0 && (
              <div className="debug-sidetext">
                <h4>Debug Text</h4>
                {sidetextAnnotations
                  .sort((v) => v.agentID)
                  .map((v) => {
                    return (
                      <div className={`sidetext-${v.agentID}`}>
                        {v.command.split(' ').slice(1).join(' ').split("'")[1]}
                      </div>
                    );
                  })}
              </div>
            )}
            <Button
              className="warnings-button"
              onClick={() => {
                setWarningsPanelOpen(true);
              }}
            >
              Warnings ({currentFrame.errors.length})
            </Button>
            <WarningsPanel
              panelOpen={warningsPanelOpen}
              closePanel={() => {
                setWarningsPanelOpen(false);
              }}
              turn={turn}
              warnings={currentFrame.errors}
            />
            <div className="tile-stats-wrapper">
              {selectedTileData && (
                <TileStats
                  {...selectedTileData}
                  cities={currentFrame.cityData}
                  trackUnit={trackUnit}
                  untrackUnit={untrackUnit}
                  trackedUnitID={trackedUnitID}
                />
              )}
            </div>
            <div className="global-stats-wrapper">
              {main && (
                <GlobalStats
                  currentFrame={currentFrame}
                  turn={turn}
                  accumulatedStats={main.accumulatedStats}
                  teamDetails={replayData.teamDetails}
                  staticGlobalStats={main.globalStats}
                />
              )}
            </div>
            {renderDebugModeButton()}
            <ZoomInOut
              className="zoom-in-out"
              handleZoomIn={() => {
                setVisualScale(visualScale + scaleSize);
              }}
              handleZoomOut={() => {
                setVisualScale(visualScale - scaleSize);
              }}
            />
            <div className="map-meta-wrapper">
              <p>
                <strong>Map Size:</strong>{' '}
                {(main.pseudomatch.state.game as Game).map.width}x
                {(main.pseudomatch.state.game as Game).map.height}
              </p>
              <p>
                <strong>Map Seed:</strong>{' '}
                {(main.pseudomatch.state.game as Game).configs.seed}
              </p>
            </div>
          </div>
        )}
      </ThemeProvider>
    </div>
  );
};