// Copyright 2020 Thomas Hintz
//
// This file is part of the Alpha Centauri Farming project.
//
// The Alpha Centauri Farming project is free software: you can
// redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// The Alpha Centauri Farming project is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the Alpha Centauri Farming project.  If not, see
// <https://www.gnu.org/licenses/>.

import { GAME_STATES, ALERTS } from '../../constants.js'

import { batch } from 'react-redux'
import * as websocket from '../../websocket.js'

import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
         movePlayer, setOldMessages, markActionChangeHandled,
         mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
         autoSkip, message, alertHandled, setHarvestTable,
         setCardError, setMovingSkip, serverError, removePlayer } from './actions.js'
import { itemCard, fateCard } from 'game.js'

export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
         submitTradeDeny, submitTradeCancel, audit, handleMessage,
         nextAction, buyUncleBert, actionsFinished, skip, endAiTurn,
         startGame, readyToStart, leaveGame, kickPlayer, toggleRevealForTrade,
         addAIPlayer }

let store;
let movingTimer = 0;

function handleMessage(evt) {
    const data = JSON.parse(evt.data),
          type = data.event;

    if (data.event === 'error') {
        console.log('error:' + data.exn);
        store.dispatch(serverError(data.exn));
        return;
    }
    batch(() => {
        if (data.event === 'left-game') {
            window.location.href = window.location.pathname;
        }
        if (data.event === 'player-left-game') {
            store.dispatch(removePlayer(data.color));
        }
        if (data.game.state === GAME_STATES.preGame) {
            store.dispatch(alert(ALERTS.preGame, '', 'pre-game'));
        }
        if (store.getState().farm.game.state === GAME_STATES.preGame &&
            data.game.state === GAME_STATES.preTurn) {
            store.dispatch(alertHandled('pre-game'));
        }
        if (data.player.state === GAME_STATES.preTurn &&
            data.game.otherPlayers.length > 0 &&
            store.getState().farm.player.state !== GAME_STATES.preTurn) {
            store.dispatch(alert(ALERTS.beginTurn, '', 'beginTurn' + data.game.turn));
        }
        if (data.game.calledAudit && !store.getState().farm.game.calledAudit) {
            store.dispatch(alert(ALERTS.auditCalled, data.game.calledAudit,
                                 'auditCalled' + data.game.turn));
        }
        if (data.player.trade.player && data.player.trade.originator !== data.player.name) {
            store.dispatch(alert(ALERTS.proposedTrade, data.player.trade.originator,
                                 'tradeProposed' + data.player.trade.number));
        }
        // new turn clear actions
        if (data.event === 'update' &&
            data.game.currentPlayer !== store.getState().farm.game.currentPlayer) {
            store.dispatch(setNextAction(false, false));
            store.dispatch(nextUIAction());
        }
        if (data.event === 'buy' && data.error) {
            store.dispatch(setCardError(data.error));
        }
        store.dispatch(updatePlayer(data.player));
        if (data.event === 'init') {
            store.dispatch(movePlayer(data.player.space, -1, data.player.color));
            store.dispatch(setHarvestTable(data.harvestTable));
        }
        // player(s) added or removed from game, put them on the board
        if (data.game.otherPlayers.length !== store.getState().farm.game.otherPlayers.length) {
            const otherPlayers = store.getState().farm.game.otherPlayers;
            const newPlayers = data.game.otherPlayers.filter(
                x => !otherPlayers.find(y => y.player.name === x.player.name));
            for (const p of newPlayers) {
                store.dispatch(movePlayer(p.player.space, -1, p.player.color));
            }
        }
        const oldMessages = store.getState().farm.game.messages.slice(0, 20);
        store.dispatch(updateGame(data.game));
        store.dispatch(setOldMessages(oldMessages));
        store.dispatch(setCards(data.player.cards));
        const currentPlayer = (data.player.name === data.game.currentPlayer
                               || data.game.currentPlayer === '') ?
              data.player : data.game.otherPlayers
              .find(p => p.player.name === data.game.currentPlayer).player;
        if (data.event === 'action') {
            if (data.player.name !== data.game.currentPlayer &&
                data.action !== 'roll' &&
                !currentPlayer.ai) {
                store.dispatch(nextUIAction());
            }
            store.dispatch(setNextAction(data.action, data.value));
            if (data.action === 'roll') {
                store.dispatch(nextUIAction());
            }
        }
        if (data.player.state === GAME_STATES.midTurn &&
            data.player.cash < 0 &&
            !store.getState().farm.ui.nextAction) {
            store.dispatch(alert(ALERTS.raiseMoney, '', 'raiseMoney' + data.game.turn));
        }//  else if (data.player.state === GAME_STATES.midTurn &&
        //           !data.game.state === 'finished') {
        //     store.dispatch(alertHandled());
        // }
        if (data.event === 'auto-skip') {
            store.dispatch(autoSkip(data.component));
        }
        if (data.event === 'end-of-game') {
            store.dispatch(alert(ALERTS.endOfGame, { results: data.results, stats: data.stats }, 'endOfGame' + data.game.turn));
        }
    });
};

let sendCommand;

function buy(id, cash) {
    sendCommand({ type: 'buy', id: id, cash: cash });
}

function roll() {
    store.dispatch(setNextAction('pre-rolling', false));
    store.dispatch(nextUIActionSilent());
    sendCommand({ type: 'roll' });
}

function endTurn() {
    const farmState = store.getState().farm;
    const playerSpaces = farmState.ui.playerSpaces;
    farmState.game.otherPlayers.map(x => x.player).concat([farmState.player]).forEach(player => {
        if (player.space !== playerSpaces[player.color]) {
            store.dispatch(movePlayer(player.space, playerSpaces[player.color],
                                      player.color));
        }
    });
    store.dispatch(gameState(GAME_STATES.turnEnded));
    sendCommand({ type: 'turn-ended' });
}

function endAiTurn() {
    sendCommand({ type: 'end-ai-turn' });
}

function loan(amount) {
    sendCommand({ type: 'loan', amount: amount });
}

function trade(parameters) {
    sendCommand({ type: 'trade',
                  parameters: parameters });
}

function submitTradeAccept() {
    sendCommand({ type: 'trade-accept' });
}

function submitTradeDeny() {
    sendCommand({ type: 'trade-deny' });
}

function submitTradeCancel() {
    sendCommand({ type: 'trade-cancel' });
}

function audit() {
    sendCommand({ type: 'audit' });
}

function nextAction() {
    sendCommand({ type: 'next-action' });
}

function buyUncleBert() {
    sendCommand({ type: 'buy-uncle-bert' });
}

function actionsFinished() {
    sendCommand({ type: 'actions-finished' });
}

function skip(component) {
    sendCommand({ type: 'skip', component });
}

function startGame() {
    sendCommand({ type: 'start-game' });
}

function readyToStart() {
    sendCommand({ type: 'ready-to-start' });
}

function leaveGame() {
    sendCommand({ type: 'leave-game' });
}

function kickPlayer(name) {
    sendCommand({ type: 'kick-player', name });
}

function addAIPlayer() {
    sendCommand({ type: 'add-ai-player' });
}

function toggleRevealForTrade(id) {
    sendCommand({ type: 'toggle-reveal-for-trading', id });
}

// TODO share with Board.jsx
// http://stackoverflow.com/questions/149055
function formatMoney(n) {
    return n.toFixed(1).replace(/(\d)(?=(\d{3})+\.)/g, '$1,').slice(0, -2); }

function initialize(st, sc) {
    store = st;
    sendCommand = sc;

    let lastAction = false,
        lastAutoSkip = false,
        rollMessage = '',
        rollTimer = false,
        movingAction = { from: 99, to: 99 },
        handledResolveMove = true;

    const unsubscribe = store.subscribe(
        () => {
            const state = store.getState();
            const currentPlayer = (state.farm.player.name === state.farm.game.currentPlayer
                                   || state.farm.game.currentPlayer === '') ?
                  state.farm.player : state.farm.game.otherPlayers
                  .find(p => p.player.name === state.farm.game.currentPlayer).player;
            if ((state.farm.player.name === state.farm.game.currentPlayer ||
                 (currentPlayer.ai)) // TODO add test for first player, or something. only one player should be doing this
                && !state.farm.ui.actionChangeHandled) {
                store.dispatch(markActionChangeHandled());
                nextAction();
            }
            if (state.farm.player.name !== state.farm.game.currentPlayer &&
               lastAction !== state.farm.ui.action) {
                lastAction = state.farm.ui.action;
                switch (lastAction) {
                case 'roll':
                    const roll = state.farm.ui.actionValue.to -
                        (state.farm.ui.actionValue.to < state.farm.ui.actionValue.from ?
                         state.farm.ui.actionValue.from - 49 :  state.farm.ui.actionValue.from);
                    rollMessage = state.farm.game.currentPlayer + ' rolled a ' + roll;
                    rollTimer = setInterval(() => {
                        store.dispatch(message(rollMessage));
                        clearInterval(rollTimer);
                    }, 5000);
                    break;
                case 'money':
                    store.dispatch(message(state.farm.ui.actionValue.player +
                                           (state.farm.ui.actionValue.amount < 0 ? ' lost ' : ' gained ') +
                                          '$' + formatMoney(Math.abs(state.farm.ui.actionValue.amount))));
                    break;
                case 'farmers-fate':
                    store.dispatch(message(state.farm.game.currentPlayer + ' drew a ' +
                                           fateCard));
                    break;
                case 'ff-uncle-bert':
                    store.dispatch(message(state.farm.game.currentPlayer + ' drew a ' +
                                           fateCard));
                    break;
                case 'otb':
                    store.dispatch(message(state.farm.game.currentPlayer + ' drew an ' +
                                           itemCard));
                    break;
                case 'harvest':
                    store.dispatch(message(state.farm.game.currentPlayer +
                                           ' is harvesting ' +
                                           state.farm.ui.actionValue.acres + ' ' +
                                           (state.farm.ui.actionValue.crop === 'cows' ? ' head of cow' : ' acres') +
                                           ' of ' + state.farm.ui.actionValue.crop));
                    break;
                case false:
                    store.dispatch(message(''));
                    break;
                }
            }
            if (state.farm.player.name !== state.farm.game.currentPlayer &&
                lastAutoSkip !== state.farm.ui.autoSkip) {
                lastAutoSkip = state.farm.ui.autoSkip;
                switch (lastAutoSkip) {
                case 'die':
                    if (lastAction !== 'harvest') {
                        clearInterval(rollTimer);
                        store.dispatch(message(rollMessage));
                    }
                    break;
                case 'harvest|income':
                    store.dispatch(message(state.farm.game.currentPlayer +
                                           ' rolled a ' +
                                           state.farm.ui.actionValue.rolled +
                                           ' and earned $' +
                                           formatMoney(state.farm.ui.actionValue.income)));
                    break;
                case 'harvest|expense-value':
                    store.dispatch(message(state.farm.game.currentPlayer +
                                           ' had operating expense of $' +
                                           formatMoney(Math.abs(state.farm.ui.actionValue.operatingExpenseValue[state.farm.game.currentPlayer]))));
                    break;
                }
            }
            if ((state.farm.ui.action === 'move' || state.farm.ui.action === 'goto') &&
                movingAction.from !== state.farm.ui.actionValue.from &&
                movingAction.to !== state.farm.ui.actionValue.to) {
                handledResolveMove = false;
                movingAction = state.farm.ui.actionValue;
                const movingProc = (player, from, to) => {
                    let currentPos = from < 0 ? from + 49 : from;
                    const color = player.color;
                    const hurtBack = to === 2 && from === 11 ? true : false;
                    const delta = currentPos === 48 ? -48 : hurtBack ? -1 : 1;
                    store.dispatch(movePlayer(currentPos + delta, currentPos, color));
                    currentPos += delta;

                    const tick = () => {
                        if (store.getState().farm.ui.movingSkip ||
                            store.getState().farm.ui.autoSkip === 'moving') {
                            store.dispatch(setMovingSkip(false));
                            store.dispatch(movePlayer(to, currentPos, color));
                            currentPos = to;
                        } else {
                            const delta = currentPos === 48 ? -48 : hurtBack ? -1 : 1;
                            store.dispatch(movePlayer(currentPos + delta, currentPos, color));
                            currentPos += delta;
                        }
                        if (currentPos === to) {
                            clearInterval(movingTimer);
                            movingTimer = 0;
                        }
                    }

                    if (currentPos !== to) {
                        clearInterval(movingTimer);
                        movingTimer = setInterval(tick, 500);
                    }
                }
                movingProc(state.farm.player.name === state.farm.game.currentPlayer ?
                           state.farm.player : state.farm.game.otherPlayers
                           .find(p => p.player.name === state.farm.game.currentPlayer).player,
                           movingAction.from, movingAction.to);
            } else if (state.farm.ui.action === 'resolve-move' && !handledResolveMove) {
                handledResolveMove = true;
                clearInterval(movingTimer);
                movingTimer = 0;
                movingAction = { from: 99, to: 99 };
                store.dispatch(movePlayer(state.farm.ui.actionValue.to,
                                          state.farm.ui.playerSpaces[state.farm.ui.actionValue.color],
                                          state.farm.ui.actionValue.color));
            }
            if (state.farm.ui.action === 'resolve-move' && state.farm.ui.nextAction !== 'resolve-move') {
                store.dispatch(nextUIAction());
            }
        });

    // mpDims.mouseX = e.clientX
    // window.onmousemove = e => store.
    //     dispatch(mpMouse(e.clientX, store.getState().farm.mpDims.mouseY));
    // document.addEventListener('keydown', keydown);
}

function keydown(e) {
    switch (e.key) {
    case 'r':
        roll();
        break;
    case 'e':
        endTurn();
        break;
    }
}
