/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2004 Gregor Koukkoullis ( phex <at> kouk <dot> de )
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package phex.http;

import java.util.*;
import java.lang.Math;
import phex.download.swarming.SWDownloadConstants;
 
import phex.utils.Logger;


public class PriorityRating implements IRating
{
    // This value is used when a candidate has no speed
    // NOTE: this is added without using the POWER variable.
    
    //public static final int NOT_SET = 10; // assume 100bps

    // By setting the value to 0 we effectively disable 
    // candidates from affecting ratings until they have a
    // speed associated with them.
    public static final int NOT_SET = 0;

    // TODO: consider making a configuration variable
    // though how could we handle this at runtime???
    public static final double CANDIDATE_POWER = 1.0;

    private long sumOfTransferSpeeds;
    private long nCandidates;

    public PriorityRating()
    {
        this(0,0);
    }

    public PriorityRating( IRating src )
    {
        this(((PriorityRating)src).sumOfTransferSpeeds, ((PriorityRating)src).nCandidates);
    }
    /*
     * sumOfTransferSpeeds is not strictly a sum of the speeds,
     * but rather a sum of a power of the speeds
     */
    public PriorityRating( long sumOfTransferSpeeds, long nCandidates )
    {
        this.sumOfTransferSpeeds = sumOfTransferSpeeds;
        this.nCandidates = nCandidates;
    }

    public String toString()
    {
        if ( nCandidates == 0 )
            return new String(""); // will never be sorted by rating; effectively unrated
        else
            return new String("[" + sumOfTransferSpeeds + "]");
           //return new String("[total candidate rating:" + sumOfTransferSpeeds + ", # candidates:" + nCandidates + "]");
    }

    /*
     * Return a new instance of a PriorityRating object calculated (solely) from the given CandidateRating
     */
    public static IRating derive ( IRating source )
    {
        CandidateRating c = (CandidateRating)source;
        if ( c.hasValidTransferSpeed())
            return new PriorityRating(Math.round(Math.pow( (double)c.getTransferSpeed(), CANDIDATE_POWER)) , 1);
        else
            return new PriorityRating(NOT_SET, 0);
    }

    /*
     * Assimulate the given candidateRating into the current object
     */
    public void accumulate ( IRating source )
    {
        CandidateRating c = (CandidateRating)source;
        if ( c.hasValidTransferSpeed() )
        {
            sumOfTransferSpeeds += Math.round(Math.pow( (double)c.getTransferSpeed(), CANDIDATE_POWER));
            nCandidates ++;
        } else {
            sumOfTransferSpeeds += NOT_SET;
        }
    }

    public void set (String key, long value) throws Exception
    {
        throw new Error("Should never be called - can be set during construction");
    }

    /*
     * This results the value used for sorting segments.
     * If no candidates (who have only parts of the whole file)
     * can provide this segment, put it last.
     */
    public long getValue()
    {
        if ( nCandidates == 0 ) return SWDownloadConstants.WORST_RATING;
        else return sumOfTransferSpeeds;
    }

    public boolean equals (Object obj)
    {
        if ( obj == null ) return false;
        try {
            PriorityRating p = (PriorityRating) obj;
            return ( // nCandidates == p.nCandidates &&
                     sumOfTransferSpeeds == p.sumOfTransferSpeeds );
        } catch (ClassCastException ex) {
            return false;
        }
    }

    public int hashCode()
    {
        return (int) (getValue() % 65535L);
    }
}

