package org.limewire.libtorrent;

import org.limewire.bittorrent.TorrentState;
import org.limewire.bittorrent.TorrentStatus;
import org.limewire.util.StringUtils;

import com.sun.jna.Structure;

/**
 * Structure used to pass data from the native code regarding a torrents state
 * back to java.
 */
public class LibTorrentStatus extends Structure implements TorrentStatus {

    /**
     * long containing the total amount of the torrent downloaded and verified.
     */
    public long total_done;

    /**
     * String containing the total amount of wanted bytes of the torrent
     * downloaded and verified.
     */
    public long total_wanted_done;

    /**
     * long containing the total amount of wanted bytes of the torrent.
     */
    public long total_wanted;

    /**
     * long containing the total amount of the torrent downloaded, this session.
     */
    public long total_download;

    /**
     * long containing the total amount of the torrent uploaded, this session.
     */
    public long total_upload;

    /**
     * long containing the total amount of the torrent payload downloaded, this
     * session.
     */
    public long total_payload_download;

    /**
     * long containing the total amount of the torrent payload uploaded, this
     * session.
     */
    public long total_payload_upload;

    /**
     * long containing the total amount of the torrent payload downloaded over
     * all time.
     */
    public long all_time_payload_download;

    /**
     * long containing the total amount of the torrent payload uploaded over all
     * time.
     */
    public long all_time_payload_upload;

    /**
     * The current rate of the torrent download in bytes/second
     */
    public float download_rate;

    /**
     * The current rate of the torrent upload in bytes/second
     */
    public float upload_rate;

    /**
     * The current rate of the torrent payload download in bytes/second
     */
    public float download_payload_rate;

    /**
     * The current rate of the torrent payload upload in bytes/second
     */
    public float upload_payload_rate;

    /**
     * The number of peers this torrent has.
     */
    public int num_peers;

    /**
     * The number of uplaod peers this torrent has.
     */
    public int num_uploads;

    /**
     * The number of seeds this torrent has.
     */
    public int num_seeds;

    /**
     * The number of connections this torrent has.
     */
    public int num_connections;

    /**
     * The state of this torrent, aligning with LibTorrentState.
     */
    public int state;

    /**
     * The progress of this torrents download, from 0 to 1.0.
     */
    public float progress;

    /**
     * boolean of whether this torrent is paused.
     */
    public int paused;

    /**
     * boolean of whether this torrent is finished.
     */
    public int finished;

    /**
     * boolean of whether this torrent is valid.
     */
    public int valid;

    /**
     * boolean of whether this torrent is auto managed.
     */
    public int auto_managed;

    /**
     * int for the amount of time this torrent has been seeding in seconds.
     */
    public int seeding_time;

    /**
     * int for the amount of time this torrent has been seeding in seconds.
     */
    public int active_time;

    /**
     * String containing the error message for the torrent. Null/empty if there
     * is no error.
     */
    public String error;

    /**
     * Represents the current tracker for this torrent. Null/empty if there was
     * never a successful tracker connect.
     */
    public String current_tracker;

    /**
     * Total number of peers that are seeding (complete).
     * <p> -1 if no data from tracker
     */
    public int num_complete;


    /**
     * Total number of peers that are downloading (incomplete).
     * <p> -1 if no data from tracker
     */
    public int num_incomplete;
    
    /**
     * Amount of download data that has been discarded due to error.
     */
    public long total_failed_bytes;
    
    @Override
    public float getDownloadPayloadRate() {
        return download_payload_rate;
    }

    @Override
    public float getUploadPayloadRate() {
        return upload_payload_rate;
    }

    @Override
    public int getNumPeers() {
        return num_peers;
    }

    @Override
    public int getNumUploads() {
        return num_uploads;
    }

    @Override
    public int getNumSeeds() {
        return num_seeds;
    }

    @Override
    public int getNumConnections() {
        return num_connections;
    }

    @Override
    public float getProgress() {
        return progress;
    }

    @Override
    public long getTotalDone() {
        return total_done;
    }

    @Override
    public long getAllTimePayloadDownload() {
        return all_time_payload_download;
    }

    @Override
    public long getAllTimePayloadUpload() {
        return all_time_payload_upload;
    }

    @Override
    public long getTotalFailedDownload() {
        return total_failed_bytes;
    }
    
    @Override
    public boolean isPaused() {
        return paused != 0;
    }

    @Override
    public boolean isFinished() {
        return finished != 0;
    }

    @Override
    public boolean isError() {
        return !StringUtils.isEmpty(error);
    }

    @Override
    public String getError() {
        return error;
    }

    @Override
    public TorrentState getState() {
        LibTorrentState libTorrentState = LibTorrentState.forId(state);
        switch (libTorrentState) {
        case ALLOCATING:
            return TorrentState.ALLOCATING;
        case CHECKING_FILES:
            return TorrentState.CHECKING_FILES;
        case DOWNLOADING:
            return TorrentState.DOWNLOADING;
        case DOWNLOADING_METADATA:
            return TorrentState.DOWNLOADING_METADATA;
        case FINISHED:
            return TorrentState.FINISHED;
        case QUEUED_FOR_CHECKING:
            return TorrentState.QUEUED_FOR_CHECKING;
        case SEEDING:
            return TorrentState.SEEDING;
        default:
            throw new UnsupportedOperationException("No known state for id: " + state
                    + " and libtorrent state: " + libTorrentState);
        }
    }

    @Override
    public float getSeedRatio() {
        if (getAllTimePayloadDownload() <= 0 || getAllTimePayloadUpload() <= 0) {
            return 0;
        }
        float seedRatio = getAllTimePayloadUpload() / (float) getAllTimePayloadDownload();
        return seedRatio;
    }

    @Override
    public float getDownloadRate() {
        return download_rate;
    }

    @Override
    public float getUploadRate() {
        return upload_rate;
    }

    @Override
    public boolean isAutoManaged() {
        return auto_managed != 0;
    }

    @Override
    public int getSeedingTime() {
        // Partial downloads have no notion of seeding time since they never
        // fully
        // enter the seeding state. Instead use the active time in this case for
        // comparison.
        // TODO: Update when something better becomes available. This will cause
        // partial torrent download seeds to become stale comparatively sooner
        // than their normal peers.
        //TODO after talking with arvid, he will add a finished_time variable for this extraneous case.
        //it will be a part of 0.15.0 when it is released.
        if (seeding_time == 0 && LibTorrentState.forId(state) == LibTorrentState.FINISHED) {
            return active_time;
        } else {
            return seeding_time;
        }
    }

    @Override
    public int getActiveTime() {
        return active_time;
    }

    @Override
    public long getTotalWanted() {
        return total_wanted;
    }

    @Override
    public long getTotalWantedDone() {
        return total_wanted_done;
    }

    @Override
    public String getCurrentTracker() {
        return current_tracker != null && current_tracker.isEmpty() ? null : current_tracker;
    }

    @Override
    public int getNumComplete() {
        return num_complete;
    }

    @Override
    public int getNumIncomplete() {
        return num_incomplete;
    }
}
