import { Grammar } from "../../grammar.js"
import { RdDItemArme } from "../../item-arme.js"
import { RdDItemCompetence } from "../../item-competence.js"
import { RdDItemSort } from "../../item-sort.js"
import { ITEM_TYPES } from "../../item.js"
import { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { RdDBonus } from "../../rdd-bonus.js"


const CATEGORIES_COMPETENCES = [
  "generale",
  "particuliere",
  "specialisee",
  "connaissance",
]
const CATEGORIES_DRACONIC = [
  "draconic",
]

const CATEGORIES_COMBAT = [
  "melee",
  "tir",
  "lancer"
]

const NIVEAU_BASE = {
  "generale": -4,
  "particuliere": -8,
  "specialisee": -11,
  "connaissance": -11,
  "draconic": -11,
  "melee": -6,
  "tir": -8,
  "lancer": -8,
}

class ColumnMappingFactory {
  static createMappingArme(part, i) {
    return { column: `arme-${part}-${i}`, getter: (actor, context) => Mapping.getArme(actor, context, part, i) }
  }

  static createMappingSort(part, i) {
    return { column: `sort-${part}-${i}`, getter: (actor, context) => Mapping.getSort(actor, context, part, i) }
  }
}

const NB_ARMES = 10
const NB_SORTS = 20
const TABLEAU_ARMES = [...Array(NB_ARMES).keys()]
const TABLEAU_SORTS = [...Array(NB_SORTS).keys()]

const MAPPING_BASE = [
  { column: "ID", getter: (actor, context) => actor.id },
  { column: "name", getter: (actor, context) => actor.name },
  //    { column: "biographie", getter: (actor, context) => actor.system.biographie },
  { column: "taille", getter: (actor, context) => actor.system.carac.taille.value },
  { column: "apparence", getter: (actor, context) => actor.system.carac.apparence.value },
  { column: "constitution", getter: (actor, context) => actor.system.carac.constitution.value },
  { column: "force", getter: (actor, context) => actor.system.carac.force.value },
  { column: "agilite", getter: (actor, context) => actor.system.carac.agilite.value },
  { column: "dexterite", getter: (actor, context) => actor.system.carac.dexterite.value },
  { column: "vue", getter: (actor, context) => actor.system.carac.vue.value },
  { column: "ouie", getter: (actor, context) => actor.system.carac.ouie.value },
  { column: "odoratgout", getter: (actor, context) => actor.system.carac.odoratgout.value },
  { column: "volonte", getter: (actor, context) => actor.system.carac.volonte.value },
  { column: "intellect", getter: (actor, context) => actor.system.carac.intellect.value },
  { column: "empathie", getter: (actor, context) => actor.system.carac.empathie.value },
  { column: "reve", getter: (actor, context) => actor.system.carac.reve.value },
  { column: "chance", getter: (actor, context) => actor.system.carac.chance.value },
  { column: "melee", getter: (actor, context) => actor.system.carac.melee.value },
  { column: "tir", getter: (actor, context) => actor.system.carac.tir.value },
  { column: "lancer", getter: (actor, context) => actor.system.carac.lancer.value },
  { column: "derobee", getter: (actor, context) => actor.system.carac.derobee.value },
  { column: "vie", getter: (actor, context) => actor.system.sante.vie.max },
  { column: "plusdom", getter: (actor, context) => actor.system.attributs.plusdom.value },
  { column: "protectionnaturelle", getter: (actor, context) => actor.system.attributs.protection.value },
  { column: "endurance", getter: (actor, context) => actor.system.sante.endurance.max },
  { column: "description", getter: (actor, context) => Mapping.getDescription(actor) },
  { column: "armure", getter: (actor, context) => Mapping.getArmure(actor, context) },
  { column: "protection", getter: (actor, context) => Mapping.getProtectionArmure(actor, context) },
  { column: "malus-armure", getter: (actor, context) => Mapping.getMalusArmure(actor, context) },
  { column: "esquive", getter: (actor, context) => Mapping.getEsquiveNiveau(context) },
  { column: "competences", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_COMPETENCES) },
  { column: "draconic", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_DRACONIC) },
]

const MAPPING_ARMES = TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('name', i))
  .concat(TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('niveau', i)))
  .concat(TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('init', i)))
  .concat(TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('dommages', i)))
const MAPPING_SORTS = TABLEAU_SORTS.map(i => ColumnMappingFactory.createMappingSort('voie', i))
  .concat(TABLEAU_SORTS.map(i => ColumnMappingFactory.createMappingSort('description', i)))
  .concat(TABLEAU_SORTS.map(i => ColumnMappingFactory.createMappingSort('bonus', i)))
const MAPPING = MAPPING_BASE
  .concat(MAPPING_ARMES)
  .concat(MAPPING_SORTS)

export class Mapping {

  static getMapping() {
    return MAPPING
  }

  static prepareContext(actor) {
    return {
      armes: Mapping.prepareArmes(actor),
      armure: Mapping.prepareArmure(actor),
      esquive: Mapping.prepareEsquive(actor),
      sorts: Mapping.prepareSorts(actor)
    }
  }

  static prepareArmes(actor) {
    const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme)
    return armes.map(arme =>
      [
        arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined,
        arme.system.lancer = "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined,
        arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined,
        arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined,
        !(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined
      ]
        .filter(it => it != undefined)
    ).reduce((a, b) => a.concat(b), [])
  }

  static prepareArme(actor, arme, maniement) {
    const nameCompArme = RdDItemArme.getCompetenceArme(arme, maniement)
    const competence = actor.getCompetence(nameCompArme)
    if (RdDItemCompetence.isNiveauBase(competence)) {
      return undefined
    }
    const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
    const dommages = dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme)
    return {
      name: arme.name,
      niveau: Misc.toSignedString(competence.system.niveau),
      init: Mapping.calculBaseInit(actor, competence.system.categorie) + competence.system.niveau,
      dommages: Misc.toSignedString(dommages)
    }
  }

  static calculBaseInit(actor, categorie) {
    const mapping = MAPPING_BASE.find(it => it.column == categorie)
    if (mapping) {
      switch (categorie) {
        case 'melee':
        case 'tir':
        case 'lancer':
          const caracteristique = Number(actor.system.carac[categorie].value)
          return Math.floor(caracteristique / 2)
      }
    }
    return 0
  }

  static prepareArmure(actor) {
    const armures = actor.itemTypes[ITEM_TYPES.armure].filter(it => it.system.equipe)
    if (armures.length > 1) {
      console.warn(`${actor.name} a équipé ${armures.length} armures, seule la première sera considérée`)
    }
    if (armures.length > 0) {
      const armure = armures[0]
      return {
        name: armure.name,
        protection: armure.system.protection,
        malus: armure.system.malus ?? 0
      }
    }
    return {
      name: '',
      protection: actor.system.attributs.protection.value,
      malus: 0
    }
  }

  static prepareEsquive(actor) {
    const esquives = actor.getCompetences("Esquive")
    if (esquives.length > 0) {
      const esquive = esquives[0]
      return {
        name: esquive.name,
        niveau: Misc.toSignedString(esquive.system.niveau)
      }
    }
    return undefined
  }

  static prepareSorts(actor) {
    return actor.itemTypes[ITEM_TYPES.sort].map(it => Mapping.prepareSort(it))
  }

  static prepareSort(sort) {
    return {
      voie: RdDItemSort.getCodeDraconic(sort),
      description: Mapping.descriptionSort(sort),
      bonus: Mapping.bonusCase(sort)
    }
  }

  static descriptionSort(sort) {
    const ptSeuil = Array(sort.system.coutseuil).map(it => '*')
    const caseTMR = sort.system.caseTMRspeciale.length > 0 ? sort.system.caseTMRspeciale : sort.system.caseTMR
    return `${sort.name}${ptSeuil} (${caseTMR}) R${sort.system.difficulte} r${sort.system.ptreve}`
  }

  static bonusCase(sort) {
    const list = RdDItemSort.bonuscaseStringToList(sort.system.bonuscase).sort(Misc.descending(it => it.bonus))
    if (list.length > 0) {
      const bonus = list[0]
      return `+${bonus.bonus}% en ${bonus.case}`
    }
    return ''
  }

  static getDescription(actor) {
    const sexe = actor.system.sexe
    const sexeFeminin = sexe.length > 0 && sexe.charAt(0).toLowerCase() == 'f' ? 'Née' : 'Né'
    const race = ['', 'humain'].includes(Grammar.toLowerCaseNoAccent(actor.system.race)) ? '' : (actor.system.race + ' ')
    const heure = actor.system.heure
    const hn = `${sexeFeminin} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
    const age = actor.system.age ? `${actor.system.age} ans` : undefined
    const taille = actor.system.taille
    const poids = actor.system.poids
    const cheveux = actor.system.cheveux ? `cheveux ${actor.system.cheveux}` : undefined
    const yeux = actor.system.yeux ? `yeux ${actor.system.yeux}` : undefined
    const beaute = actor.system.beaute ? `Beauté ${actor.system.beaute}` : undefined
    const list = [race, hn, age, taille, poids, cheveux, yeux, beaute]
    return Misc.join(list.filter(it => it), ', ')
  }

  static getArmure(actor, context) {
    return context.armure?.name ?? ''
  }

  static getProtectionArmure(actor, context) {
    return Number(context?.armure?.protection ?? 0) + Number(actor.system.attributs.protection.value)
  }

  static getMalusArmure(actor, context) {
    return context?.armure?.malus ?? 0
  }

  static getEsquiveNiveau(context) {
    if (context.esquive) {
      const niveau = context.esquive.niveau + context.armure.malus
      return niveau > 0 ? ('+' + niveau) : ('' + niveau)
    }
    return ''
  }

  static getCompetences(actor, categories) {
    const competences = Mapping.getCompetencesCategorie(actor, categories)
    if (competences.length == 0) {
      return ''
    }
    const byCartegories = Mapping.competencesByCategoriesByNiveau(competences, categories)
    const txtByCategories = Object.values(byCartegories)
      .map(it => it.competencesParNiveau)
      .map(byNiveau => {
        const niveaux = Object.keys(byNiveau).map(it => Number(it)).sort(Misc.ascending())
        if (niveaux.length == 0) {
          return ''
        }
        const txtCategorieByNiveau = niveaux.map(niveau => {
          const names = Misc.join(byNiveau[niveau].map(it => it.name).sort(Misc.ascending()), ', ')
          return names + ' ' + Misc.toSignedString(niveau)
        })
        const txtCategorie = Misc.join(txtCategorieByNiveau, ' / ')
        return txtCategorie
      }).filter(it => it != '')

    return Misc.join(txtByCategories, ' / ')
  }

  static competencesByCategoriesByNiveau(competences, categories) {
    return categories.map(c => {
      return {
        categorie: c,
        competencesParNiveau: Misc.classify(
          competences.filter(comp => comp.system.categorie == c),
          comp => comp.system.niveau)
      }
    })
  }

  static getArme(actor, context, part, numero) {
    if (numero < context.armes.length) {
      return context.armes[numero][part] ?? ''
    }
    return ''
  }

  static getCompetencesCategorie(actor, categories) {
    return actor.itemTypes[ITEM_TYPES.competence]
      .filter(it => categories.includes(it.system.categorie))
      .filter(it => !RdDItemCompetence.isNiveauBase(it))
  }

  static getSort(actor, context, part, numero) {
    if (numero < context.sorts.length) {
      return context.sorts[numero][part]
    }
    return ''
  }
}