 # Description:
-#   Steam Web API (Dota 2)
+#   Steam Web API w/ Dota 2
 #
 # Dependencies:
-#   "moment": "^2.6.0"
+#   "moment": "^2.8.1"
 #   "msgpack": "^0.2.3"
-#   "ref": "^0.2.1"
+#   "ref": "^0.3.2"
 #
 # Configuration:
 #   STEAM_API_KEY

    "Top tier 2"
    "Top tier 1"
]

module.exports = (robot) ->
    if not STEAM_API_KEY?
        return robot.logger.debug 'Missing STEAM_API_KEY in environment. Please set and try again.'

    fs.readFile 'data/heroes.bin', (err, data) ->
        return robot.logger.error if err
        heroData = msgpack.unpack data

    steamData = ->
        robot.brain.data.steam or= {}

    robot.respond /steam id( me)? (.*)/i, (msg) ->
        getSteamID msg, msg.match[2], (id) ->
            msg.match[2] += if msg.match[2].slice(-1) is "s" then "'" else "'s"
            msg.reply "#{msg.match[2]} Steam ID is: #{id}"

    robot.respond /steam status (.*)/i, (msg) ->
        summary = (steamID) ->
            steamRequest msg, "/ISteamUser/GetPlayerSummaries", steamids: steamID, (object) ->
                player = object.response.players[0]
                status = if player.communityvisibilitystate is 1 then 'Unavailable (Private)' else personaStates[player.personastate]
                lastOnline = moment.unix(player.lastlogoff).fromNow()
                msg.reply "#{msg.match[1]} belongs to #{player.personaname} who is currently #{status} and was last online #{lastOnline}."
            , 2

        if msg.match[1].match /\d{17}/
            summary msg.match[1]
        else
            getSteamID msg, msg.match[1], (steamID) ->
                summary steamID

    robot.respond /dota history (.*)/i, (msg) ->
        [command, value] = msg.match

        params =
            original: value
            account_id: value
            matches_requested: 5

        history = (params, type = 'Steam ID') ->
            steamRequest msg, "/IDOTA2Match_570/GetMatchHistory", params, (object) ->
                if object.result.status is 15
                    msg.reply "The #{type} you have entered (\"#{params.original}\") does not exist or it does not have match history enabled."
                    return
                else if object.result.num_results is 0
                    msg.reply "No game matches were found for the #{type}: #{params.original}."
                    return

                communityID = getCommunityID params.account_id

                for match in object.result.matches
                    hero = "N/A"
                    start = moment.unix(match.start_time).fromNow()

                    for player in match.players
                        if player.account_id is communityID
                            hero = getHero(player.hero_id).LocalizedName
                            break;

                    msg.send "Match ID: #{match.match_id} | Lobby: #{lobbies[match.lobby_type]} | Hero: #{hero} | #{start}"

        if value.match /\d{17}/
            history params
        else
            getSteamID msg, value, (id) ->
                params.account_id = id
                history params, 'profile URL'

    robot.respond /dota match (\d+)\s*(.*)?/i, (msg) ->
        steamRequest msg, "/IDOTA2Match_570/GetMatchDetails", match_id: msg.match[1], (object) ->
            match = object.result
            start = moment.unix(match.start_time).fromNow()
            duration = moment.duration(match.duration, 'seconds').minutes()
            firstBlood = moment.duration(match.first_blood_time, 'seconds').humanize()
            victor = if match.radiant_win then "Radiant" else "Dire"

            radiantTowers = getTowers(match.tower_status_radiant).join(', ') or 'None'
            direTowers = getTowers(match.tower_status_dire).join(', ') or 'None'

            msg.send "Match ID #{match.match_id} is a #{lobbies[match.lobby_type].toLowerCase()} game that took place #{start}. The #{victor} won the game in #{duration} minutes. First blood was drawn #{firstBlood} into the game."
            msg.send "Radiant towers remaining: #{radiantTowers} | Dire towers remaining: #{direTowers}"

            playerInfo = (communityID) ->
                for player in match.players
                    if player.account_id is communityID
                        faction = if player.player_slot > 4 then 'Dire' else 'Radiant'
                        msg.reply "#{getHero(player.hero_id).LocalizedName} (Lvl #{player.level}), #{faction} | KDA: #{player.kills}/#{player.deaths}/#{player.assists} | LH: #{player.last_hits} | GPM: #{player.gold_per_min} | XPM: #{player.xp_per_min}"
                        return
                msg.reply "The Steam ID you have entered (\"#{msg.match[1]}\") was not found in Match ID #{match.match_id}."

            if msg.match[2]?
                if msg.match[2].match /\d{17}/
                    playerInfo getCommunityID(msg.match[2])
                else
                    getSteamID msg, msg.match[2], (steamID) ->
                        playerInfo getCommunityID(steamID)

    getSteamID = (msg, customURL, handler) ->
        for steamID, user of steamData()
            return handler steamID if user.url is customURL

        steamRequest msg, "/ISteamUser/ResolveVanityURL", vanityurl: customURL, (object) ->
            if object.response.success is 42
                msg.reply "The custom URL you have entered (\"#{customURL}\") does not exist."
                return

            steamData()[object.response.steamid] or= {}
            steamData()[object.response.steamid].url = customURL

            handler object.response.steamid

    getCommunityID = (steamID) ->
        if steamData()[steamID]?.cID?
            return steamData()[steamID].cID

        # 64 -> 32
        buffer = new Buffer 8
        buffer.writeUInt64LE steamID, 0
        communityID = buffer.readUInt32LE 0

        steamData()[steamID] or= {}
        steamData()[steamID].cID = communityID

        communityID

getHero = (heroID) ->
    return hero for hero in heroData.Heroes when hero.Id is heroID

getTowers = (dec) ->
    for status, tower in "00000000000#{(+dec).toString(2)}".slice(-11).split('')
        if parseInt(status) then towers[tower] else continue

steamRequest = (msg, endpoint, params = {}, handler, version = 1) ->
    params.key = STEAM_API_KEY

    msg.http("http://api.steampowered.com#{endpoint}/v#{version}/")
        .query(params)
        .get() (err, res, body) ->
            if err or res.statusCode isnt 200
                err = "Bad request (invalid Steam web API key)" if res.statusCode is 400
                msg.reply "An error occurred while attempting to process your request."
                return robot.logger.error err

            handler JSON.parse(body)
    "Looking to trade"
    "Looking to play"
]
DOTA_LOBBIES =
    "-1": "Invalid"
    0: "Public matchmaking"
    1: "Practice"
