PokeBattle.commands ?= {}

Commands = {}

desc = (description) ->
  desc.lastDescription = description

makeCommand = (commandNames..., func) ->
  for commandName in commandNames
    Commands[commandName] = func

  # Generate description
  description = ""
  if commandNames.length > 1
    aliases = commandNames[1...].map((n) -> "/#{n}").join(', ')
    description += " <i>Also #{aliases}. </i>"
  description += desc.lastDescription
  # TODO: Hardcoded user level
  HelpDescriptions['1'][commandNames[0]] = description
  delete desc.lastDescription

parseCommand = (line) ->
  [ commandName, args... ] = line.split(/\s+/)
  if commandName[0] == '/'
    # It's a command. Remove leading slash
    commandName = commandName[1...]
    args = args.join(' ').split(/,/g)
    return [commandName, args]
  return null

PokeBattle.commands.execute = (room, line) ->
  result = parseCommand(line)
  return false  if !result
  [commandName, args] = result
  command = Commands[commandName]
  if !command
    # Fall-through to server.
    return false
  command(room, args...)
  return true

desc 'Displays a list of all commands.'
makeCommand "commands", "help", "h", (room) ->
  user = room.get('users').get(PokeBattle.username)

  for level, descriptions of HelpDescriptions
    level = Number(level)
    continue  if user.get('authority') < level

    message = []
    # TODO: Hardcoded levels
    authLevels = {1: "USER", 2: "DRIVER", 3: "MOD", 4: "ADMIN", 5: "OWNER"}
    humanLevel = authLevels[level]
    message.push("<b>#{humanLevel} COMMANDS:</b>")
    for name, description of descriptions
      message.push("<b>/#{name}:</b> #{description}")
    message = message.join("<br>")
    room.announce('success', message)
  true

desc 'Opens the challenge for a specific user. Usage: /challenge username'
makeCommand "challenge", "chall", "c", (room, username) ->
  if !username
    PokeBattle.events.trigger("errorMessage", "Usage: /challenge username")
    return
  message = PokeBattle.messages.add(id: username)
  message.openChallenge(username)

desc 'Private messages a certain user. Usage: /message username, message'
makeCommand "message", "msg", "pm", "whisper", "w", (room, username, messages...) ->
  username = username?.trim()
  if !username
    PokeBattle.events.trigger("errorMessage", "Usage: /message username, msg")
    return
  message = PokeBattle.messages.add(id: username)

  if messages.length > 0
    text = messages.join(',')
    PokeBattle.primus.send('privateMessage', message.id, text)
  else
    # The PM is opened without a message.
    message.trigger('open', message)

desc 'Clears the chat.'
makeCommand "clear", (room) ->
  room.clear()

desc 'Displays a Pokemon\'s PokeBattle value, or displays all Pokemon at or under a particular PBV. Usage: /pbv pkmn1, pkmn2, OR /pbv number'
makeCommand "pbv", (room, pokemon...) ->
  pbv = Number(pokemon[0])
  if !isNaN(pbv)
    messages = findPokemonAtPBV(pbv)
  else
    messages = findTotalPBV(pokemon)

  if messages.length == 0
    room.announce('error', "<b>PBV error:</b> Enter valid Pokemon or PBV.")
  else
    room.announce('success', "<b>PBV:</b> #{messages.join('; ')}")

findPokemonAtPBV = (pbv) ->
  messages = []
  counter = 0
  for speciesName, formes of window.Generations.XY.FormeData
    for formeName, formeData of formes
      if formeData.pokeBattleValue <= pbv
        counter += 1
        dexEntry = "pokemon/#{slugify(speciesName)}/#{slugify(formeName)}"
        icon = pokemonIcon(speciesName, formeName)
        formattedName = formatName(speciesName, formeName)
        messages.push("#{linkToDex(dexEntry, icon + formattedName)}:
          #{formeData.pokeBattleValue}")
  if messages.length > 10
    messages = _.sample(messages, 10)
    messages.push(linkToDex("pokemon/?pbv=<#{pbv + 1}",
      "See more Pokemon &raquo;"))
  if messages.length > 0
    plural = if messages.length == 1 then "is" else "are"
    messages.unshift("There #{plural} #{counter} Pokemon with a PBV of
      #{pbv} or less")
  messages

findTotalPBV = (pokemon) ->
  pokemon = _(pokemon).map(findPokemon)
  messages = []
  total = 0
  for array in pokemon
    continue  unless array
    [speciesName, formeName] = array
    pbv = PokeBattle.PBV.determinePBV(window.Generations.XY,
      species: speciesName, forme: formeName)
    total += pbv
    dexEntry = "pokemon/#{slugify(speciesName)}/#{slugify(formeName)}"
    icon = pokemonIcon(speciesName, formeName)
    formattedName = formatName(speciesName, formeName)
    messages.push("#{linkToDex(dexEntry, icon + formattedName)}: #{pbv}")
  messages.push("Total: #{total}")  if messages.length > 1
  messages

desc 'Looks up information about a Pokemon, move, item, or ability.'
makeCommand "data", "dex", (room, query) ->
  if (pokemon = findPokemon(query))
    message = dataPokemon(pokemon)
  else if (item = findItem(query))
    message = dataItem(item)
  else if (move = findMove(query))
    message = dataMove(move)
  else if (ability = findAbility(query))
    message = dataAbility(ability)
  else
    room.announce("error", "<b>Data error:</b> Enter a valid Pokemon, item,
      move, or ability.</div>")
    return
  room.announce('success', message)

dataPokemon = (pokemon) ->
  [speciesName, formeName] = pokemon
  [speciesSlug, formeSlug] = [slugify(speciesName), slugify(formeName)]
  forme = window.Generations.XY.FormeData[speciesName][formeName]
  {types, abilities, hiddenAbility, stats, pokeBattleValue} = forme

  # Format abilities
  abilities = _.clone(abilities)
  abilities.push(hiddenAbility)  if hiddenAbility?
  abilities = _(abilities).map((a) -> linkToDex("abilities/#{slugify(a)}", a))
  abilities = abilities.join('/')
  abilities += " (H)"  if hiddenAbility?

  # Format types, stats, and icon
  types = _(types).map (t) ->
    linkToDex("types/#{slugify(t)}",
      "<img src='#{window.TypeSprite(t)}' alt='#{t}'/>")
  statNames = [ 'HP', 'Attack', 'Defense', 'Sp.Attack', 'Sp.Defense', 'Speed']
  stats = [ stats.hp, stats.attack, stats.defense,
            stats.specialAttack, stats.specialDefense, stats.speed ]
  statsText = _.map(_.zip(statNames, stats), (a) -> a.join(': ')).join(' / ')

  # Build data
  message = """#{pokemonIcon(speciesName, formeName, "left")}
    <p class="ml3">
    <b>#{formatName(speciesName, formeName)}:</b> #{types.join('')} |
    #{abilities}<br />#{statsText} |
    #{_(stats).reduce((a, b) -> a + b)} <abbr title="Base Stat Total">BST</abbr>
    | <abbr title="PokeBattle Value">PBV</abbr>: #{pokeBattleValue}
    #{linkToDex("pokemon/#{speciesSlug}/#{formeSlug}", "See dex entry &raquo;")}
    </p>
    """
  message

dataItem = (itemName) ->
  item = window.Generations.XY.ItemData[itemName]
  message = "<b>#{itemName}:</b> #{item.description}"
  message += " Natural Gift is #{item.naturalGift.type} type
    and has #{item.naturalGift.power} base power."  if item.naturalGift
  message += " Fling has #{item.flingPower} base power."  if item.flingPower
  message += " Currently unreleased in Gen 6."  if item.unreleased
  message

dataMove = (moveName) ->
  move = window.Generations.XY.MoveData[moveName]
  type = linkToDex("types/#{slugify(move.type)}",
    "<img src='#{window.TypeSprite(move.type)}' alt='#{move.type}'/>")
  category = """<img src="#{CategorySprite(move.damage)}"
    alt="#{move.damage}"/>"""
  target = """<img src="#{TargetSprite(move)}"
    alt="#{move.target}"/>"""
  power = move.power || "&mdash;"
  acc = move.accuracy || "&mdash;"
  maxpp = Math.floor(move.pp * 8/5)
  if move.priority > 0
    priority = "+#{move.priority}"
  else if move.priority < 0
    priority = move.priority
  message = """<b>#{moveName}:</b> #{type} #{category} #{target} """
  message += "<b>Power:</b> #{power} <b>Acc:</b> #{acc} <b>PP:</b> #{move.pp} (max #{maxpp})"
  message += "<br />"
  message += "Priority #{priority}. "  if priority
  message += move.description
  message += " "
  message += linkToDex("moves/#{slugify(moveName)}",
    "See who learns this move &raquo;")
  message

dataAbility = (abilityName) ->
  ability = window.Generations.XY.AbilityData[abilityName]
  message = """<b>#{abilityName}:</b> #{ability.description}
    #{linkToDex("abilities/#{slugify(abilityName)}",
      "See who obtains this ability &raquo;")}"""
  message

# Finds the most lenient match possible.
findPokemon = (pokemonName) ->
  pokemonName = normalize(pokemonName)
  for speciesName, speciesData of window.Generations.XY.FormeData
    for formeName of speciesData
      name = speciesName
      name += formeName  unless formeName == 'default'
      name = normalize(name)
      name += name
      return [speciesName, formeName]  if name.indexOf(pokemonName) != -1
  # Return blank match
  null

# Finds the most lenient match possible.
findItem = (itemName) ->
  normalized = normalize(itemName)
  for name of window.Generations.XY.ItemData
    return name  if normalized == normalize(name)
  # Return blank match
  null

# Finds the most lenient match possible.
findMove = (moveName) ->
  normalized = normalize(moveName)
  for name of window.Generations.XY.MoveData
    return name  if normalized == normalize(name)
  # Return blank match
  null

# Finds the most lenient match possible.
findAbility = (abilityName) ->
  normalized = normalize(abilityName)
  for name of window.Generations.XY.AbilityData
    return name  if normalized == normalize(name)
  # Return blank match
  null

slugify = (str) ->
  str.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/\-{2,}/g, '-')

normalize = (str) ->
  str.trim().toLowerCase().replace(/[^a-zA-Z0-9]+/g, '')

formatName = (speciesName, formeName) ->
  if formeName == 'default'
    pokemonName = speciesName
  else
    pokemonName = speciesName
    pokemonName += ' '
    pokemonName += formeName.split('-')
      .map((n) -> n[0].toUpperCase() + n[1...])
      .join('-')
  return pokemonName

linkToDex = (slug, text) ->
  "<a href='//pokebattle.com/dex/#{slug}' target='_blank'>#{text}</a>"

pokemonIcon = (speciesName, formeName, classes="") ->
  style = window.PokemonIconBackground(speciesName, formeName)
  """<span class="pokemon_icon #{classes}" style="#{style}"></span>"""
