import { clampNumber } from "../../utils/helpers/clampNumber";

/**
 * Given an experience amount and stat multiplier, calculates the
 * stat level. Stat-agnostic (same formula for every stat)
 */
export function calculateSkill(exp: number, mult = 1): number {
  // Mult can be 0 in BN12 when the player has a very high SF12 level. In this case, the skill level will never change
  // from its initial value (1 for most stats, except intelligence).
  if (mult === 0) {
    return 1;
  }
  const value = Math.floor(mult * (32 * Math.log(exp + 534.6) - 200));
  return clampNumber(value, 1);
}

export function calculateExp(skill: number, mult = 1): number {
  const floorSkill = Math.floor(skill);
  let value = Math.exp((skill / mult + 200) / 32) - 534.6;
  if (skill === floorSkill && Number.isFinite(skill) && Number.isFinite(value)) {
    // Check for floating point rounding issues that would cause the inverse
    // operation to return the wrong result.
    let calcSkill = calculateSkill(value, mult);
    let diff = Math.abs(value * Number.EPSILON);
    let newValue = value;
    while (calcSkill < skill) {
      newValue = value + diff;
      diff *= 2;
      calcSkill = calculateSkill(newValue, mult);
    }
    value = newValue;
  }
  return clampNumber(value, 0);
}

export function calculateSkillProgress(exp: number, mult = 1): ISkillProgress {
  const currentSkill = calculateSkill(exp, mult);
  const nextSkill = currentSkill + 1;

  const baseExperience = calculateExp(currentSkill, mult);
  const nextExperience = calculateExp(nextSkill, mult);

  const normalize = (value: number): number => ((value - baseExperience) * 100) / (nextExperience - baseExperience);

  const rawProgress = nextExperience - baseExperience !== 0 ? normalize(exp) : 99.99;
  const progress = clampNumber(rawProgress, 0, 100);

  const currentExperience = clampNumber(exp - baseExperience, 0);
  const remainingExperience = clampNumber(nextExperience - exp, 0);

  return {
    currentSkill,
    nextSkill,
    baseExperience,
    experience: exp,
    nextExperience,
    currentExperience,
    remainingExperience,
    progress,
  };
}

export interface ISkillProgress {
  currentSkill: number;
  nextSkill: number;
  baseExperience: number;
  experience: number;
  nextExperience: number;
  currentExperience: number;
  remainingExperience: number;
  progress: number;
}

export function getEmptySkillProgress(): ISkillProgress {
  return {
    currentSkill: 0,
    nextSkill: 0,
    baseExperience: 0,
    experience: 0,
    nextExperience: 0,
    currentExperience: 0,
    remainingExperience: 0,
    progress: 0,
  };
}
