import { SHOW_DICE } from "../../constants.js";
import { Misc } from "../../misc.js";
import { RdDCarac } from "../../rdd-carac.js";
import { RdDDice } from "../../rdd-dice.js";
import { RdDNameGen } from "../../rdd-namegen.js";
import { RdDTimestamp } from "../../time/rdd-timestamp.js";

async function randomFrom(values) {
  const max = Object.values(values).reduce(Misc.sum(), 0)
  const total = await RdDDice.rollTotal(`1d${max}`)
  let sum = 0
  for (let entry of Object.entries(values)) {
    sum = sum + entry[1]
    if (sum >= total) {
      return entry[0]
    }
  }
  return Object.keys(values)[0]
}

async function randomTailleCm(taille) {
  const infoTaille = RdDCarac.getCaracDerivee(taille)
  const infoTailleSup = RdDCarac.getCaracDerivee(taille + 1)
  const variation = (infoTailleSup.taille - infoTaille.taille) + Math.floor((infoTaille.poidsMax - infoTaille.poidsMin) / 2)
  const base = infoTaille.taille
  const total = await RdDDice.rollTotal(`1d${variation} -1d ${variation} + ${base}`)
  const cm = total % 100
  const dm = cm < 10 ? '0' : ''
  const m = (total - cm) / 100
  return `${m}m${dm}${cm}`
}

async function randomPoidsKg(taille) {
  const infoTaille = RdDCarac.getCaracDerivee(taille)
  const range = infoTaille.poidsMax - infoTaille.poidsMin + 1
  const total = await RdDDice.rollTotal(`1d${range} + ${infoTaille.poidsMin}`)
  return total + ' kg'
}

async function randomHeure() {
  return RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "self", showDice: SHOW_DICE })).key
}

const CONTROL_UNKNOWN = { name: 'unknown', path: '', getter: (act) => { undefined }, random: async act => undefined }
const TABLE_SEXES = { 'masculin': 1, 'féminin': 1 };
const TABLE_MAINS = { 'droitier': 51, 'gaucher': 15, 'ambidextre': 6 };
const TABLE_COULEURS_CHEVEUX = {
  'noirs': 2,
  'bruns': 5,
  'châtains': 3,
  'châtain clair': 5,
  'blonds': 4,
  'blond platine': 1,
  'roux carotte': 1,
  'roux cuivré': 3,
  'chauve': 1
};
const TABLE_COULEURS_YEUX = {
  'noirs': 2,
  'noisette': 3,
  'brun-vert': 4,
  'verts': 3,
  'bleu clair': 3,
  'bleu gris': 2,
  'gris': 1,
  'mauves': 1,
  'indigos': 1
};

const CONTROLS = [
  { name: 'name', path: 'name', getter: (act) => act.name, random: async act => await RdDNameGen.generate() },
  { name: 'sexe', path: 'system.sexe', getter: act => act.system.sexe, random: async act => await randomFrom(TABLE_SEXES) },
  { name: 'age', path: 'system.age', getter: act => act.system.age, random: async act => await RdDDice.rollTotal('(2d4kl)*10 + 1d7xo + 2d20kl') },
  { name: 'taille', path: 'system.taille', getter: act => act.system.taille, random: async act => await randomTailleCm(act.system.carac.taille.value) },
  { name: 'poids', path: 'system.poids', getter: act => act.system.poids, random: async act => await randomPoidsKg(act.system.carac.taille.value) },
  { name: 'main', path: 'system.main', getter: act => act.system.main, random: async act => await randomFrom(TABLE_MAINS) },
  { name: 'heure', path: 'system.heure', getter: act => act.system.heure, random: async act => await randomHeure() },
  { name: 'cheveux', path: 'system.cheveux', getter: act => act.system.cheveux, random: async act => await randomFrom(TABLE_COULEURS_CHEVEUX) },
  { name: 'yeux', path: 'system.yeux', getter: act => act.system.yeux, random: async act => await randomFrom(TABLE_COULEURS_YEUX) },
]

const { HandlebarsApplicationMixin } = foundry.applications.api;
export class AppPersonnageAleatoire extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
  static PARTS = {
    main: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/random/app-personnage-aleatoire.hbs" }
  }

  static DEFAULT_OPTIONS = {
    classes: ['app-personnage-aleatoire'],
    window: {
      title: "Génération aléatoire",
      controls: []
    },
    position: {
      width: 600,
      height: 600
    }
  }

  static preloadHandlebars() {
    foundry.applications.handlebars.loadTemplates([
      'systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs',
      'systems/foundryvtt-reve-de-dragon/templates/actor/random/sexe-aleatoire.hbs',
    ])
  }

  constructor(actor) {
    super({})
    this.actor = actor
    this.current = Object.fromEntries(CONTROLS.map(it => [it.name, it.getter(this.actor)]));
    this.checked = Object.fromEntries(CONTROLS.map(it => [it.name, [0, '', undefined].includes(this.current[it.name])]))
  }

  _prepareContext() {
    return {
      actor: this.actor,
      current: this.current,
      checked: this.checked,
      options: { isGM: game.user.isGM }
    }
  }

  _onRender(context, options) {
    super._onRender(context, options)
    this.html = $(this.element)
    this.html.find("input.current-value").change(async event => await this.onChange(event))
    this.html.find("a.random").click(async event => await this.onRandom(event))
    this.html.find("a.reset").click(async event => await this.onReset(event))
    this.html.find("a.randomize-selected").click(async event => await this.onRandomizeSelected())
    this.html.find("input.check-for-random").click(async event => await this.onCheckForRandom(event))

    this.html.find("div.random-field[data-field-name='heure'] select.current-value").change(async event => await this.onChange(event))
    this.html.find('a[data-action="sexe-masculin"]').click(async event => await this.onSexe('masculin'))
    this.html.find('a[data-action="sexe-feminin"]').click(async event => await this.onSexe('féminin'))

    this.html.find("button.button-cancel").click(async event => await this.close())
    this.html.find("button.button-apply").click(async event => await this.onApply())
  }

  async onApply() {
    const updates = Object.fromEntries(
      CONTROLS.filter(control => game.user.isGM || control.name != 'name')
        .map(control => [control.path, this.current[control.name]])
    )
    await this.actor.update(updates)
    await this.close()
  }

  getName(selector) {
    const fields = this.html.find(selector).parents("div.random-field:first")
    return fields[0].attributes['data-field-name'].value
  }

  getControl(name) {
    return CONTROLS.find(it => it.name == name) ?? CONTROL_UNKNOWN
  }

  async onSexe(sexe) {
    this.current['sexe'] = sexe
    this.render({ force: true })
  }

  async onChange(event) {
    const name = this.getName(event.currentTarget)
    const control = this.getControl(name)
    this.current[control.name] = event.currentTarget.value
    this.render({ force: true })
  }


  async onRandom(event) {
    const name = this.getName(event.currentTarget)
    const control = this.getControl(name)
    await this.randomControl(control)
    this.render({ force: true })
  }

  async onReset(event) {
    const name = this.getName(event.currentTarget)
    const control = this.getControl(name)
    this.current[control.name] = control.getter(this.actor)
    this.render({ force: true })
  }

  async onCheckForRandom(event) {
    const name = this.getName(event.currentTarget)
    this.checked[name] = event.currentTarget.checked
    this.render({ force: true })
  }

  async onRandomizeSelected() {
    const controls = this.html.find("input.check-for-random:checked")
      .parents("div.random-field")
      .toArray()
      .map(it => this.getControl(it.attributes['data-field-name'].value))
    await Promise.all(controls.map(it => this.randomControl(it)))
    this.render({ force: true })
  }

  async randomControl(control) {
    this.current[control.name] = await control.random(this.actor)
  }
}
