import math
import json
import discord

from gwendolyn_old.utils import cap

STATS = [
  "strength",
  "dexterity",
  "constitution",
  "intelligence",
  "wisdom",
  "charisma"
]

def mod(statistic):
  """Calculates D&D modifier."""
  modifier = math.floor((statistic-10)/2)
  if modifier >= 0:
    modifier = "+"+str(modifier)

  return modifier

class LookupFuncs():
  def __init__(self, bot):
    self.bot = bot
    self.saves = [
      "strength_save",
      "dexterity_save",
      "constitution_save",
      "intelligence_save",
      "wisdom_save",
      "charisma_save"
    ]
    self.abilities = [
      "acrobatics",
      "animal_handling",
      "arcana",
      "athletics",
      "deception",
      "history",
      "insight",
      "intimidation",
      "investigation",
      "medicine",
      "nature",
      "perception",
      "performance",
      "persuasion",
      "religion",
      "sleight_of_hand",
      "stealth",
      "survival"
    ]

  def _format_monster(self, monster):
    # Looks at the information about the monster and
    # returns that information in separate variables,
    # allowing Gwendolyn to know where to separate
    # the messages
    types = monster["type"]
    if monster["subtype"] != "":
      types +=  " ("+monster["subtype"]+")"

    stats = []
    for stat in STATS:
      value = monster[stat]
      stats.append(f"**{cap(stat[:3])}:** {value} ({mod(value)})")

    stats = "\t".join(stats[:3]) + "\n" + "\t".join(stats[3:])

    saving_throws = []
    for save in self.saves:
      if save in monster:
        value = monster[save]
        if monster[save] >= 0:
          saving_throws.append(f"{cap(save[:3])} +{value}")
        else:
          saving_throws.append(f"{cap(save[:3])} {value}")

    if saving_throws:
      saving_throws = f"\n**Saving Throws:** {', '.join(saving_throws)}"
    else:
      saving_throws = ""

    skills = []
    for skill in self.abilities:
      if skill in monster:
        skill_name = cap(skill.replace("_"," "))
        if monster[skill] >= 0:
          skills.append(f"{skill_name} +{monster[skill]}")
        else:
          skills.append(f"{skill_name} {monster[skill]}")

    if skills:
      skills = f"\n**Skills:** {', '.join(skills)}"
    else:
      skills = ""

    vulnerabilities = monster["damage_vulnerabilities"]
    if vulnerabilities != "":
      vulnerabilities = "\n**Damage Vulnerabilities** "+vulnerabilities

    resistances = monster["damage_resistances"]
    if resistances != "":
      resistances = "\n**Damage Resistances** "+resistances

    immunities = monster["damage_immunities"]
    if immunities != "":
      immunities = "\n**Damage Immunities** "+immunities

    c_immunities = monster["condition_immunities"]
    if c_immunities != "":
      c_immunities = "\n**Condition Immunities** "+c_immunities

    special_abilities = ""
    if "special_abilities" in monster:
      for ability in monster["special_abilities"]:
        special_abilities += "\n\n***"+ability["name"]+".*** "+ability["desc"]

    act = ""
    if "actions" in monster:
      for action in monster["actions"]:
        act += "\n\n***"+action["name"]+".*** "+action["desc"]

    react = ""
    if "reactions" in monster:
      for reaction in monster["reactions"]:
        react += "\n\n***"+reaction["name"]+".*** "+reaction["desc"]

    legendaryActions = ""
    if "legendary_actions" in monster:
      for action in monster["legendary_actions"]:
        legendaryActions += "\n\n***"+action["name"]+".*** "+action["desc"]

    hit_dice = monster["hit_dice"]
    dice_amount = int(monster["hit_dice"].replace("d"," ").split()[0])
    con_mod = math.floor((monster['constitution']-10)/2)
    if con_mod < 0:
      hit_dice += f" - {abs(con_mod) * dice_amount}"
    elif con_mod > 0:
      hit_dice += (f" + {con_mod * dice_amount}")

    new_part = "\n--------------------"

    monster_type = f"*{monster['size']} {types}, {monster['alignment']}*"

    basic_info = "\n**Armor Class** "+str(monster["armor_class"])+"\n**Hit Points** "+str(monster["hit_points"])+" ("+hit_dice+")\n**Speed **"+monster["speed"]+new_part+"\n"

    info = (monster_type+new_part+basic_info+stats+new_part+saving_throws+skills+vulnerabilities+resistances+immunities+c_immunities+"\n**Senses** "+monster["senses"]+"\n**Languages** "+monster["languages"]+"\n**Challenge** "+monster["challenge_rating"])

    monster_info = [(info, monster['name']),
            (special_abilities, "Special Abilities"),
            (act, "Actions"),
            (react, "Reactions"),
            (legendaryActions, "Legendary Actions")]

    self.bot.log("Returning monster information")
    return monster_info

  # Looks up a monster
  async def monster_func(self, ctx, query):
    query = cap(query)
    self.bot.log("Looking up "+query)

    # 1-letter monsters don't exist
    if len(query) < 2:
      self.bot.log("Monster name too short")
      await ctx.send("I don't know that monster...")
      return

    # Opens "monsters.json"
    monster_file_path = "gwendolyn/resources/lookup/monsters.json"
    with open(monster_file_path,"r", encoding="utf-8") as file_pointer:
      data = json.load(file_pointer)

    for monster in data:
      if "name" in monster and str(query) == monster["name"]:
        self.bot.log("Found it!")

        monster_info = self._format_monster(monster)

        # Sends the received information. Separates into separate messages if
        # there is too much text
        await ctx.send(f"Result for \"{query}\"")
        for text, title in monster_info:
          if text != "":
            if len(text) < 2000:
              em = discord.Embed(title = title, description = text, colour=0xDEADBF)
              await ctx.channel.send(embed = em)
            else:
              index = text[:2000].rfind(".")+1
              em1 = discord.Embed(title = title, description = text[:index], colour=0xDEADBF)
              await ctx.channel.send(embed = em1)
              em2 = discord.Embed(title = "", description = text[index+1:], colour=0xDEADBF)
              await ctx.channel.send(embed = em2)

        break
    else:
      self.bot.log("Monster not in database")
      await ctx.send("I don't know that monster...")

  # Looks up a spell
  async def spell_func(self, ctx, query):
    query = cap(query)
    self.bot.log("Looking up "+query)

    # Opens "spells.json"
    data = json.load(open('gwendolyn/resources/lookup/spells.json', encoding = "utf8"))
    if query in data:
      self.bot.log("Returning spell information")
      send_message = (f"***{query}***\n*{data[query]['level']} level {data[query]['school']}\nCasting Time: {data[query]['casting_time']}\nRange:{data[query]['range']}\nComponents:{data[query]['components']}\nDuration:{data[query]['duration']}*\n \n{data[query]['description']}")
    else:
      self.bot.log("I don't know that spell")
      send_message = "I don't think that's a spell"

    if len(send_message) > 2000:
      await ctx.send(send_message[:2000])
      await ctx.send(send_message[2000:])
    else:
      await ctx.send(send_message)
