'use strict';
import AnyPlayer from './player/any-player';
import allHeroesJson from '../../../server/src/client-server-shared/all-heroes.json';
import cloneDeep from 'lodash.clonedeep';

/** @type {import('type/game').TH_GameGlobalState} */
const initGameState = {
  gameState: '',
  allHeroes: [],
  deckMode: '',
  advRules: [],
  'waitingFor/blue': false,
  'waitingFor/red': false,
  currentPlayer: '',
  'battleTiles/left': [],
  'battleTiles/center': [],
  'battleTiles/right': [],
  totalFood: 0,
  allHeroesJson
};
const state = cloneDeep(initGameState);

const getters = {
  state(state) {
    return state.gameState;
  },
  allHeroes(state) {
    return state.allHeroes;
  },
  deckMode(state) {
    return state.deckMode;
  },
  myColor(state, getters, rootState) {
    let color = 'blue';
    if (rootState.username === state.redPlayer.name) {
      color = 'red';
    }
    return color;
  },
  my: (state, getters) => target => {
    return getters.myColor + 'Player/' + target;
  },
  ennemyColor(state, getters) {
    let color = 'red';
    if (getters.myColor === 'red') {
      color = 'blue';
    }
    return color;
  },
  his: (state, getters) => target => {
    return getters.ennemyColor + 'Player/' + target;
  },
  isItMyTurn(state, getters) {
    return state['waitingFor/' + getters.myColor];
  },
  isPopularityRule(state) {
    return state.advRules.includes('popularity');
  },
  isDiscardRule(state) {
    return state.advRules.includes('discard');
  },
  heroesByIdsFrom: () => (
    /** @type {number[]}*/ ids,
    /** @type {import('type/game').TH_HeroCard[]} */ heroesFrom
  ) => {
    let heroes = [];
    // This will keep the order of the ids specified in input parameter array
    ids.forEach(id => {
      heroes.push(heroesFrom.find(hero => hero.id === id));
    });
    return heroes;
  },
  heroesByIds: state => (/** @type {number[]}*/ ids) => {
    // This getters filter through all heroes;
    return getters.heroesByIdsFrom()(ids, state.allHeroes);
  },
  filterHeroes: (state, getters) =>
    /**
     * Get a filtered array of heroes
     *
     * vuex : 'game/filterHeroes'
     *
     * @param {object} filter - the filter to apply on all heroes array
     * @param {string} filter.faction - Filter on faction attribute, can be : orcs humans elves meca none all
     * @param {string} filter.popularity - Filter on popularity attribute
     * can be :
     * - none : no filter on popularity
     * - with : do not return heroes that are usable only when no popularity rule
     * - without : do not return heroes that are usable only when popularity rule
     * @param {string} filter.draft - Filter on draft attribute, can be : yes no all
     * @param {number} filter.minPower - Filter on power attribute >= minPower
     * @param {number} filter.maxPower - Filter on power attribute <= maxPower
     * @param {number} filter.minCost - Filter on cost attribute >= minCost
     * @param {number} filter.maxCost - Filter on cost attribute <= maxCost
     * @param {boolean} filter.byName - Activate filter on Hero's name
     * @param {string} filter.name - Filter on Hero's name
     * @param {number[]} filter.byId - Filter on Heroes Id(s). Do not filter in Ids if array is empty
     * @param {boolean} draftMode - If true, only filter through the IDs in player/draftHeroesIds
     * @returns {object[]} Filtered array of heroes
     */
    (filter, draftMode = false) => {
      if (!filter.faction) filter.faction = 'all';
      if (!filter.popularity) filter.popularity = 'without';
      if (!filter.minPower) filter.minPower = 0;
      if (!filter.maxPower) filter.maxPower = 8;
      if (!filter.minCost) filter.minCost = 0;
      if (!filter.maxCost) filter.minCost = 8;

      let draftFilter = 'all';
      switch (filter.draft) {
        case 'yes':
          draftFilter = true;
          break;
        case 'no':
          draftFilter = false;
          break;

        default:
          draftFilter = 'all';
          break;
      }
      let heroesToFilter = state.allHeroes;
      if (draftMode === true) {
        heroesToFilter = getters.heroesByIds(getters.myDraftIds);
      }
      return heroesToFilter.filter(hero => {
        if (filter.faction !== 'all' && hero.faction !== filter.faction)
          return false;
        if (
          filter.popularity !== 'none' &&
          hero.popularity !== 'any' &&
          hero.popularity !== filter.popularity
        )
          return false;

        if (draftFilter !== 'all' && hero.isDraftable !== draftFilter)
          return false;

        if (hero.power < filter.minPower || hero.power > filter.maxPower)
          return false;
        if (hero.cost < filter.minCost || hero.cost > filter.maxCost)
          return false;

        if (
          filter.byName === true &&
          !hero.name.toLowerCase().includes(filter.name.toLowerCase())
        )
          return false;

        if (
          filter.byId &&
          filter.byId.length > 0 &&
          !filter.byId.includes(hero.id)
        ) {
          return false;
        }
        return true;
      });
    },
  myTwelveHeroes: (state, getters) => getters[getters.my('twelveHeroes')],
  hisTwelveHeroes: (state, getters) => getters[getters.his('twelveHeroes')],

  myDraftIds(state, getters) {
    return getters[getters.my('draftIds')];
  },
  myHeroesIn: (state, getters) => (
    /** @type {import('type/game').TH_HeroPosition} */ position
  ) => {
    let heroesIds = getters[getters.my('heroesIn' + position)];
    let heroesFrom = getters.myTwelveHeroes;
    return getters.heroesByIdsFrom(heroesIds, heroesFrom);
  },
  hisHeroesIn: (state, getters) => (
    /** @type {import('type/game').TH_HeroPosition} */ position
  ) => {
    let heroesIds = getters[getters.his('heroesIn' + position)];
    return getters.heroesByIdsFrom(heroesIds, getters.hisTwelveHeroes);
  },
  myNbOfHeroesIn: (state, getters) => (
    /** @type {import('type/game').TH_HeroPosition} */ position
  ) => {
    return getters.myHeroesIn(position).length;
  },
  hisNbOfHeroesIn: (state, getters) => (
    /** @type {import('type/game').TH_HeroPosition} */ position
  ) => {
    return getters.hisHeroesIn(position).length;
  },
  canIEndTurn: (state, getters) => getters[getters.my('canEndTurn')],
  canIPass: (state, getters) => getters[getters.my('canPass')],
  canIDrawCards: (state, getters) => getters[getters.my('canDrawCards')],
  myInstructions: (state, getters) => getters[getters.my('instructions')]
};

const mutations = {
  SET_HEROES: (state, payload) => {
    state.allHeroes = payload;
  },
  SET_FULL_GAME_STATE: (state, payload) => {
    Object.assign(state, payload);
  },
  INIT_GAME_STATE: state => {
    Object.assign(state, initGameState);
  },
  SET_WAITING_FOR: (state, payload) => {
    state['waitingFor/' + payload.color] = payload.value;
  }
};

const actions = {
  /**
   * Sets the array containing all heroes
   *
   * vuex : 'game/setHeroes'
   *
   * @param {object[]} payload - the array with all heroes
   */
  setHeroes: ({ commit }, payload) => {
    commit('SET_HEROES', payload);
  },
  /**
   * Update the full game state (main + players)
   *
   * Will be called by the game master to update the game state
   *
   * vuex : 'game/update'
   *
   * @param {object} Context - vuex context
   * @param {Function} Context.commit - vuex commit func
   * @param {import('type/game').TH_GameDataStore} payload - the full game state
   * @param {object} payload.game - match store state in game.js
   * @param {object} payload.bluePlayer - match store state in any-player.js for blue
   * @param {object} payload.redPlayer - match store state in any-player.js for red
   */
  update: ({ commit }, payload) => {
    commit('SET_FULL_GAME_STATE', payload.game);
    if (
      payload.game.updatePlayerState === 'both' ||
      payload.game.updatePlayerState === 'blue'
    ) {
      commit('bluePlayer/SET_FULL_PLAYER_STATE', payload.bluePlayer);
    }
    if (
      payload.game.updatePlayerState === 'both' ||
      payload.game.updatePlayerState === 'red'
    ) {
      commit('redPlayer/SET_FULL_PLAYER_STATE', payload.redPlayer);
    }
  },
  /**
   * Reset the whole game state
   *
   * vuex : 'game/resetGameState'
   *
   * Typically to call when game is stopped or user disconnects
   *
   * @param {object} Context - vuex context
   * @param {Function} Context.commit - vuex commit func
   */
  resetGameState: ({ commit }) => {
    commit('INIT_GAME_STATE');
    commit('bluePlayer/INIT_PLAYER_STATE');
    commit('redPlayer/INIT_PLAYER_STATE');
    // Also clear chosenIds array if we leave game during deck building phase
    commit('CLEAR_CHOSEN_IDS', null, { root: true });
  },
  /**
   * Submit chosen faction to the game master
   *
   * vuex : 'game/submitMyFaction'
   *
   * @param {string} payload - the faction name (lowercase)
   */
  submitMyFaction: ({ dispatch, getters }, payload) => {
    dispatch(getters.my('submitFaction'), payload);
  },

  submitMyChosenIds: ({ dispatch, getters }) => {
    dispatch(getters.my('submitChosenIds'));
  },
  addInMyDraftIds: ({ dispatch, getters }, payload) => {
    dispatch(getters.my('addInDraftIds'), payload);
  },
  removeInMyDraftIds: ({ dispatch, getters }, payload) => {
    dispatch(getters.my('removeInDraftIds'), payload);
  },
  draw: ({ dispatch, getters }, payload = 1) => {
    dispatch(getters.my('draw'), payload);
    dispatch('updateHeroesPossibleActions');
  },
  submitMyReplace3CardsTurn: ({ dispatch, getters }) => {
    dispatch(getters.my('submitReplace3CardsTurn'));
  },
  updateHeroesPossibleActions: ({ commit, getters, state }) => {
    let myHeroesInHand = getters.myHeroesIn('Hand');
    switch (state.gameState) {
      case '2_CHANGE_UP_TO_3_CARDS':
        myHeroesInHand.forEach((
          /** @type {import('type/game').TH_HeroCard} */ hero
        ) => {
          hero.possibleActions = ['replace'];
        });

        break;

      default:
        break;
    }
    commit(getters.my('SET_TWELVE_HEROES'), myHeroesInHand);
  }
};
const bluePlayer = new AnyPlayer();
const redPlayer = new AnyPlayer();
export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions,
  modules: {
    bluePlayer: {
      namespaced: true,
      ...bluePlayer
    },
    redPlayer: {
      namespaced: true,
      ...redPlayer
    }
  }
};
