/*
 * transmission-remote-gtk - A GTK RPC client to Transmission
 * Copyright (C) 2011-2013  Alan Fitton

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <glib-object.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <json-glib/json-glib.h>

#include "trg-client.h"
#include "json.h"
#include "torrent.h"
#include "protocol-constants.h"
#include "util.h"

/* Just some functions to get fields out of the torrent object. */

JsonArray *torrent_get_peers(JsonObject * t)
{
    g_assert(json_object_get_array_member(t, FIELD_PEERS));
    return json_object_get_array_member(t, FIELD_PEERS);
}

JsonObject *torrent_get_peersfrom(JsonObject * t)
{
    return json_object_get_object_member(t, FIELD_PEERSFROM);
}

JsonArray *torrent_get_wanted(JsonObject * t)
{
    g_assert(json_object_get_array_member(t, FIELD_WANTED));
    return json_object_get_array_member(t, FIELD_WANTED);
}

JsonArray *torrent_get_priorities(JsonObject * t)
{
    g_assert(json_object_get_array_member(t, FIELD_PRIORITIES));
    return json_object_get_array_member(t, FIELD_PRIORITIES);
}

JsonArray *torrent_get_tracker_stats(JsonObject * t)
{
    g_assert(json_object_get_array_member(t, FIELD_TRACKER_STATS));
    return json_object_get_array_member(t, FIELD_TRACKER_STATS);
}

gint64 torrent_get_id(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_ID);
}

const gchar *torrent_get_download_dir(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_DOWNLOAD_DIR);
}

const gchar *torrent_get_comment(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_COMMENT);
}

gdouble torrent_get_metadata_percent_complete(JsonObject * t)
{
    JsonNode *node =
        json_object_get_member(t, FIELD_METADATAPERCENTCOMPLETE);
    if (node)
        return json_double_to_progress(node);
    else
        return 100.0;
}

const gchar *torrent_get_name(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_NAME);
}

gint64 torrent_get_added_date(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_ADDED_DATE);
}

gboolean torrent_get_honors_session_limits(JsonObject * t)
{
    return json_object_get_boolean_member(t, FIELD_HONORS_SESSION_LIMITS);
}

gint64 torrent_get_bandwidth_priority(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_BANDWIDTH_PRIORITY);
}

const gchar *torrent_get_magnetlink(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_MAGNETLINK);
}

gint64 torrent_get_upload_limit(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_UPLOAD_LIMIT);
}

gint64 torrent_get_peer_limit(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_PEER_LIMIT);
}

gboolean torrent_get_upload_limited(JsonObject * t)
{
    return json_object_get_boolean_member(t, FIELD_UPLOAD_LIMITED);
}

gint64 torrent_get_seed_ratio_mode(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_SEED_RATIO_MODE);
}

gdouble torrent_get_seed_ratio_limit(JsonObject * t)
{
    return
        json_node_really_get_double(json_object_get_member
                                    (t, FIELD_SEED_RATIO_LIMIT));
}

gint64 torrent_get_download_limit(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_DOWNLOAD_LIMIT);
}

gboolean torrent_get_download_limited(JsonObject * t)
{
    return json_object_get_boolean_member(t, FIELD_DOWNLOAD_LIMITED);
}

gint64 torrent_get_total_size(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_TOTAL_SIZE);
}

gint64 torrent_get_size_when_done(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_SIZEWHENDONE);
}

gint64 torrent_get_rate_down(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_RATEDOWNLOAD);
}

gint64 torrent_get_rate_up(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_RATEUPLOAD);
}

gint64 torrent_get_eta(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_ETA);
}

gint64 torrent_get_downloaded(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_DOWNLOADEDEVER);
}

gint64 torrent_get_uploaded(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_UPLOADEDEVER);
}

gint64 torrent_get_corrupted(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_CORRUPTEVER);
}

gint64 torrent_get_have_valid(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_HAVEVALID);
}

gint64 torrent_get_have_unchecked(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_HAVEUNCHECKED);
}

gint64 torrent_get_status(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_STATUS);
}

gboolean torrent_get_is_finished(JsonObject * t)
{
    return torrent_get_left_until_done(t) <= 0;
}

gboolean torrent_get_is_private(JsonObject * t)
{
    return json_object_get_boolean_member(t, FIELD_ISPRIVATE);
}

gdouble torrent_get_percent_done(JsonObject * t)
{
    return
        json_double_to_progress(json_object_get_member
                                (t, FIELD_PERCENTDONE));
}

gdouble torrent_get_recheck_progress(JsonObject * t)
{
    return
        json_double_to_progress(json_object_get_member
                                (t, FIELD_RECHECK_PROGRESS));
}

gint64 torrent_get_activity_date(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_ACTIVITY_DATE);
}

guint32
torrent_get_flags(JsonObject * t, gint64 rpcv, gint64 status,
                  gint64 fileCount, gint64 downRate, gint64 upRate)
{
    guint32 flags = 0;

    if (fileCount > 0 && torrent_get_is_finished(t) == TRUE)
        flags |= TORRENT_FLAG_COMPLETE;
    else
        flags |= TORRENT_FLAG_INCOMPLETE;

    if (rpcv >= NEW_STATUS_RPC_VERSION) {
        switch (status) {
        case TR_STATUS_STOPPED:
            flags |= TORRENT_FLAG_PAUSED;
            break;
        case TR_STATUS_CHECK_WAIT:
            flags |= TORRENT_FLAG_WAITING_CHECK;
            flags |= TORRENT_FLAG_CHECKING_ANY;
            break;
        case TR_STATUS_CHECK:
            flags |= TORRENT_FLAG_CHECKING;
            flags |= TORRENT_FLAG_CHECKING_ANY;
            break;
        case TR_STATUS_DOWNLOAD_WAIT:
            flags |= TORRENT_FLAG_DOWNLOADING_WAIT;
            flags |= TORRENT_FLAG_QUEUED;
            break;
        case TR_STATUS_DOWNLOAD:
            if (!(flags & TORRENT_FLAG_COMPLETE))
                flags |= TORRENT_FLAG_DOWNLOADING;

            if (fileCount <= 0)
                flags |= TORRENT_FLAG_DOWNLOADING_METADATA;

            flags |= TORRENT_FLAG_ACTIVE;
            break;
        case TR_STATUS_SEED_WAIT:
            flags |= TORRENT_FLAG_SEEDING_WAIT;
            break;
        case TR_STATUS_SEED:
            flags |= TORRENT_FLAG_SEEDING;
            if (torrent_get_peers_getting_from_us(t))
                flags |= TORRENT_FLAG_ACTIVE;
            break;
        }
    } else {
        switch (status) {
        case OLD_STATUS_DOWNLOADING:
            flags |= TORRENT_FLAG_DOWNLOADING;
            break;
        case OLD_STATUS_PAUSED:
            flags |= TORRENT_FLAG_PAUSED;
            break;
        case OLD_STATUS_SEEDING:
            flags |= TORRENT_FLAG_SEEDING;
            break;
        case OLD_STATUS_CHECKING:
            flags |= TORRENT_FLAG_CHECKING;
            break;
        case OLD_STATUS_WAITING_TO_CHECK:
            flags |= TORRENT_FLAG_WAITING_CHECK;
            flags |= TORRENT_FLAG_CHECKING;
            break;
        }

        if (downRate > 0 || upRate > 0)
            flags |= TORRENT_FLAG_ACTIVE;
    }

    if (torrent_get_error(t) > 0)
        flags |= TORRENT_FLAG_ERROR;

    return flags;
}

gchar *torrent_get_status_icon(gint64 rpcv, guint flags)
{
    if (flags & TORRENT_FLAG_ERROR)
        return g_strdup("dialog-warning");
    else if (flags & TORRENT_FLAG_DOWNLOADING_METADATA)
        return g_strdup("edit-find");
    else if (flags & TORRENT_FLAG_DOWNLOADING)
        return g_strdup("go-down");
    else if (flags & TORRENT_FLAG_PAUSED)
        return g_strdup("media-playback-pause");
    else if (flags & TORRENT_FLAG_SEEDING)
        return g_strdup("go-up");
    else if (flags & TORRENT_FLAG_CHECKING)
        return g_strdup("view-refresh");
    else if (flags & TORRENT_FLAG_DOWNLOADING_WAIT)
        return g_strdup("media-seek-backward");
    else if (flags & TORRENT_FLAG_SEEDING_WAIT)
        return g_strdup("media-seek-forward");
    else
        return g_strdup("dialog-question");
}

gint64 torrent_get_done_date(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_DONE_DATE);
}

const gchar *torrent_get_errorstr(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_ERROR_STRING);
}

gint64 torrent_get_error(JsonObject * t)
{
    if (!json_object_has_member(t, FIELD_ERROR))
        return 0;
    else
        return json_object_get_int_member(t, FIELD_ERROR);
}

const gchar *torrent_get_creator(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_CREATOR);
}

gint64 torrent_get_date_created(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_DATE_CREATED);
}

const gchar *torrent_get_hash(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_HASH_STRING);
}

gchar *torrent_get_status_string(gint64 rpcv, gint64 value, guint flags)
{
    if (rpcv >= NEW_STATUS_RPC_VERSION) {
        switch (value) {
        case TR_STATUS_DOWNLOAD:
            if (flags & TORRENT_FLAG_DOWNLOADING_METADATA)
                return g_strdup(_("Metadata Downloading"));
            else
                return g_strdup(_("Downloading"));
        case TR_STATUS_DOWNLOAD_WAIT:
            return g_strdup(_("Queued download"));
        case TR_STATUS_CHECK_WAIT:
            return g_strdup(_("Waiting To Check"));
        case TR_STATUS_CHECK:
            return g_strdup(_("Checking"));
        case TR_STATUS_SEED_WAIT:
            return g_strdup(_("Queued seed"));
        case TR_STATUS_SEED:
            return g_strdup(_("Seeding"));
        case TR_STATUS_STOPPED:
            return g_strdup(_("Paused"));
        }
    } else {
        switch (value) {
        case OLD_STATUS_DOWNLOADING:
            if (flags & TORRENT_FLAG_DOWNLOADING_METADATA)
                return g_strdup(_("Metadata Downloading"));
            else
                return g_strdup(_("Downloading"));
        case OLD_STATUS_PAUSED:
            return g_strdup(_("Paused"));
        case OLD_STATUS_SEEDING:
            return g_strdup(_("Seeding"));
        case OLD_STATUS_CHECKING:
            return g_strdup(_("Checking"));
        case OLD_STATUS_WAITING_TO_CHECK:
            return g_strdup(_("Waiting To Check"));
        }
    }

    return g_strdup(_("Unknown"));
}

gboolean torrent_has_tracker(JsonObject * t, GRegex * rx, gchar * search)
{
    GList *trackers;
    GList *li;
    gboolean ret = FALSE;

    trackers = json_array_get_elements(torrent_get_tracker_stats(t));

    for (li = trackers; li; li = g_list_next(li)) {
        JsonObject *tracker = json_node_get_object((JsonNode *) li->data);
        const gchar *trackerAnnounce = tracker_stats_get_announce(tracker);
        gchar *trackerAnnounceHost =
            trg_gregex_get_first(rx, trackerAnnounce);
        int cmpResult = g_strcmp0(trackerAnnounceHost, search);
        g_free(trackerAnnounceHost);
        if (!cmpResult) {
            ret = TRUE;
            break;
        }
    }

    g_list_free(trackers);

    return ret;
}

gint64 torrent_get_left_until_done(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_LEFTUNTILDONE);
}

const gchar *tracker_stats_get_announce(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_ANNOUNCE);
}

const gchar *tracker_stats_get_scrape(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_SCRAPE);
}

JsonArray *get_torrents_removed(JsonObject * response)
{
    if (G_UNLIKELY(json_object_has_member(response, FIELD_REMOVED)))
        return json_object_get_array_member(response, FIELD_REMOVED);
    else
        return NULL;
}

JsonArray *get_torrents(JsonObject * response)
{
    g_assert(json_object_get_array_member(response, FIELD_TORRENTS));
    return json_object_get_array_member(response, FIELD_TORRENTS);
}

JsonArray *torrent_get_files(JsonObject * args)
{
    return json_object_get_array_member(args, FIELD_FILES);
}

gint64 torrent_get_peers_connected(JsonObject * args)
{
    return json_object_get_int_member(args, FIELD_PEERS_CONNECTED);
}

gint64 torrent_get_peers_sending_to_us(JsonObject * args)
{
    return json_object_get_int_member(args, FIELD_PEERS_SENDING_TO_US);
}

gint64 torrent_get_peers_getting_from_us(JsonObject * args)
{
    return json_object_get_int_member(args, FIELD_PEERS_GETTING_FROM_US);
}

gint64 torrent_get_web_seeds_sending_to_us(JsonObject * args)
{
    return json_object_get_int_member(args, FIELD_WEB_SEEDS_SENDING_TO_US);
}

gint64 torrent_get_queue_position(JsonObject * args)
{
    if (json_object_has_member(args, FIELD_QUEUE_POSITION))
        return json_object_get_int_member(args, FIELD_QUEUE_POSITION);
    else
        return -1;
}

/* tracker stats */

gint64 tracker_stats_get_id(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_ID);
}

gint64 tracker_stats_get_tier(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_TIER);
}

gint64 tracker_stats_get_last_announce_peer_count(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_LAST_ANNOUNCE_PEER_COUNT);
}

gint64 tracker_stats_get_last_announce_time(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_LAST_ANNOUNCE_TIME);
}

gint64 tracker_stats_get_last_scrape_time(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_LAST_SCRAPE_TIME);
}

gint64 tracker_stats_get_seeder_count(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_SEEDERCOUNT);
}

gint64 tracker_stats_get_leecher_count(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_LEECHERCOUNT);
}

gint64 tracker_stats_get_download_count(JsonObject * t)
{
    return json_object_get_int_member(t, FIELD_DOWNLOADCOUNT);
}

const gchar *tracker_stats_get_announce_result(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_LAST_ANNOUNCE_RESULT);
}

const gchar *tracker_stats_get_host(JsonObject * t)
{
    return json_object_get_string_member(t, FIELD_HOST);
}

gchar *torrent_get_full_path(JsonObject * obj)
{
    const gchar *location =
        json_object_get_string_member(obj, FIELD_DOWNLOAD_DIR);
    const gchar *name = json_object_get_string_member(obj, FIELD_NAME);
    return g_strdup_printf("%s/%s", location, name);
}

gchar *torrent_get_full_dir(JsonObject * obj)
{
    gchar *containing_path, *name, *delim;
    const gchar *location;
    JsonArray *files = torrent_get_files(obj);
    JsonObject *firstFile;

    location = json_object_get_string_member(obj, FIELD_DOWNLOAD_DIR);
    firstFile = json_array_get_object_element(files, 0);
    name = g_strdup(json_object_get_string_member(firstFile, TFILE_NAME));

    if ((delim = g_strstr_len(name, -1, "/"))) {
        *delim = '\0';
        containing_path = g_strdup_printf("%s/%s", location, name);
    } else {
        containing_path = g_strdup(location);
    }

    g_free(name);
    return containing_path;
}

/* peers */

const gchar *peer_get_address(JsonObject * p)
{
    return json_object_get_string_member(p, TPEER_ADDRESS);
}

const gchar *peer_get_flagstr(JsonObject * p)
{
    return json_object_get_string_member(p, TPEER_FLAGSTR);
}

const gchar *peer_get_client_name(JsonObject * p)
{
    return json_object_get_string_member(p, TPEER_CLIENT_NAME);
}

gboolean peer_get_is_encrypted(JsonObject * p)
{
    return json_object_get_boolean_member(p, TPEER_IS_ENCRYPTED);
}

gboolean peer_get_is_uploading_to(JsonObject * p)
{
    return json_object_get_boolean_member(p, TPEER_IS_UPLOADING_TO);
}

gboolean peer_get_is_downloading_from(JsonObject * p)
{
    return json_object_get_boolean_member(p, TPEER_IS_DOWNLOADING_FROM);
}

gdouble peer_get_progress(JsonObject * p)
{
    return
        json_double_to_progress(json_object_get_member(p, TPEER_PROGRESS));
}

gint64 peer_get_rate_to_client(JsonObject * p)
{
    return json_object_get_int_member(p, TPEER_RATE_TO_CLIENT);
}

gint64 peer_get_rate_to_peer(JsonObject * p)
{
    return json_object_get_int_member(p, TPEER_RATE_TO_PEER);
}

gint64 peerfrom_get_pex(JsonObject * pf)
{
    return json_object_get_int_member(pf, TPEERFROM_FROMPEX);
}

gint64 peerfrom_get_dht(JsonObject * pf)
{
    return json_object_get_int_member(pf, TPEERFROM_FROMDHT);
}

gint64 peerfrom_get_trackers(JsonObject * pf)
{
    return json_object_get_int_member(pf, TPEERFROM_FROMTRACKERS);
}

gint64 peerfrom_get_ltep(JsonObject * pf)
{
    return json_object_get_int_member(pf, TPEERFROM_FROMLTEP);
}

gint64 peerfrom_get_resume(JsonObject * pf)
{
    return json_object_get_int_member(pf, TPEERFROM_FROMRESUME);
}

gint64 peerfrom_get_incoming(JsonObject * pf)
{
    return json_object_get_int_member(pf, TPEERFROM_FROMINCOMING);
}


gint64 peerfrom_get_lpd(JsonObject * pf)
{
    return json_object_has_member(pf, TPEERFROM_FROMLPD) ?
        json_object_get_int_member(pf, TPEERFROM_FROMLPD) : -1;
}

/* files */

gdouble file_get_progress(gint64 length, gint64 completed)
{
    if (length > 0) {
        gdouble progress =
            ((gdouble) completed / (gdouble) length) * 100.0;
        if (progress > 100.0)
            return 100.0;
        else
            return progress;
    } else {
        return 0.0;
    }
}

gint64 file_get_length(JsonObject * f)
{
    return json_object_get_int_member(f, TFILE_LENGTH);
}

gint64 file_get_bytes_completed(JsonObject * f)
{
    return json_object_get_int_member(f, TFILE_BYTES_COMPLETED);
}

const gchar *file_get_name(JsonObject * f)
{
    return json_object_get_string_member(f, TFILE_NAME);
}
