package jondo;

import anon.infoservice.PerformanceEntry;
import anon.infoservice.PerformanceInfo;
import anon.util.Util;

public class MixServicePerformance implements Comparable<MixServicePerformance>
{
	public static final int COMPARABLE_SIGN_FORMAT_COMMAND_LINE = 0;
	public static final int COMPARABLE_SIGN_FORMAT_GUI = 1;
	
	private static int ms_comparableSignFormat = COMPARABLE_SIGN_FORMAT_COMMAND_LINE;
	
	private static final int SPEED_LOWER_BOUND =
		PerformanceEntry.BOUNDARIES[PerformanceEntry.SPEED][1];
	
	private static final int SPEED_UPPER_BOUND =
		PerformanceEntry.BOUNDARIES[PerformanceEntry.SPEED]
		  [PerformanceEntry.BOUNDARIES[PerformanceEntry.SPEED].length - 1];
	
	private static final int SPEED_LOW_PERFORMANCE = 
		PerformanceEntry.BOUNDARIES[PerformanceEntry.SPEED][0];
	
	private static final int DELAY_UPPER_BOUND =
	PerformanceEntry.BOUNDARIES[PerformanceEntry.DELAY][
	       PerformanceEntry.BOUNDARIES[PerformanceEntry.DELAY].length - 2];
	
	private static final int DELAY_LOWER_BOUND =
		PerformanceEntry.BOUNDARIES[PerformanceEntry.DELAY][0];
	
	private static final int DELAY_LOW_PERFORMANCE =
		PerformanceEntry.BOUNDARIES[PerformanceEntry.DELAY][
		       PerformanceEntry.BOUNDARIES[PerformanceEntry.DELAY].length - 1];
	
	private MixServiceInfo m_service;
	
	
	protected MixServicePerformance(MixServiceInfo a_service)
	{
		if (a_service == null)
		{
			throw new NullPointerException();
		}
		m_service = a_service;
	}
	
	public static void setComparableSignFormat(int a_comparableSignFormat)
	{
		ms_comparableSignFormat = a_comparableSignFormat;
	}
	
	public MixServiceInfo getServiceInfo()
	{
		return m_service;
	}
	
	public int compareTo(MixServicePerformance a_other)
	{
		int iThisMaxDelay = this.getMaximumDelay();
		int iThisMinSpeed = this.getMinimumSpeed();
		
		int iOtherMaxDelay = a_other.getMaximumDelay();
		int iOtherMinSpeed = a_other.getMinimumSpeed();
		
		if ((iThisMinSpeed == Integer.MAX_VALUE || iOtherMinSpeed == Integer.MAX_VALUE) &&
			(iThisMaxDelay <= 0 || iOtherMaxDelay <= 0))
		{
			// no comparison is possible
			return 0;
		}
		
		if (iThisMinSpeed == Integer.MAX_VALUE || iOtherMinSpeed == Integer.MAX_VALUE)
		{
			// compare only delay
			if (iThisMaxDelay < iOtherMaxDelay)
			{
				return 1;
			}
			if (iThisMaxDelay > iOtherMaxDelay)
			{
				return -1;
			}
			return 0;
		}
		
		if (iThisMaxDelay <= 0 || iOtherMaxDelay <= 0)
		{
			// compare only speed
			if (iThisMinSpeed > iOtherMinSpeed)
			{
				return 1;
			}
			if (iThisMinSpeed < iOtherMinSpeed)
			{
				return -1;
			}
			return 0;
		}
		
		if (iThisMaxDelay < iOtherMaxDelay && iThisMinSpeed > iOtherMinSpeed)
		{
			return 1;
		}
		
		if (iThisMaxDelay <= iOtherMaxDelay && iThisMinSpeed > iOtherMinSpeed)
		{
			return 1;
		}
		
		if (iThisMaxDelay < iOtherMaxDelay && iThisMinSpeed >= iOtherMinSpeed)
		{
			return 1;
		}
		
		if (iThisMaxDelay > iOtherMaxDelay && iThisMinSpeed < iOtherMinSpeed)
		{
			return -1;
		}
		
		if (iThisMaxDelay >= iOtherMaxDelay && iThisMinSpeed < iOtherMinSpeed)
		{
			return -1;
		}
		
		if (iThisMaxDelay > iOtherMaxDelay && iThisMinSpeed <= iOtherMinSpeed)
		{
			return -1;
		}
		
		if (iThisMaxDelay == DELAY_LOW_PERFORMANCE || iThisMinSpeed == SPEED_LOW_PERFORMANCE)
		{
			if (iOtherMaxDelay == DELAY_LOW_PERFORMANCE || iOtherMinSpeed == SPEED_LOW_PERFORMANCE)
			{
				return 0;
			}
			return -1;
		}
		
		if (iOtherMaxDelay == DELAY_LOW_PERFORMANCE || iOtherMinSpeed == SPEED_LOW_PERFORMANCE)
		{
			return 1;
		}
		
		
		return 0;
	}
	
	public boolean equals(Object a_other)
	{
		return ((MixServicePerformance)a_other).compareTo(this) == 0;
	}
	
	public int hashCode()
	{
		return getMaximumDelay() + getMinimumSpeed();
	}
	
	/**
	 * Get the minimum speed to expect on this cascade in kbit/s.
	 * @return the minimum speed to expect on this cascade in kbit/s; if < 0, then no information is available; 
	 * if >= SPEED_LOW, then this speed  is below measurement (very bad)
	 */
	private int getMinimumSpeed()
	{
		return PerformanceInfo.getLowestCommonBoundEntry(
				m_service.getMixCascade().getId()).getBound(
				PerformanceEntry.SPEED).getBound();
	}
	
	
	
	/**
	 * Get the maximum speed to expect on this cascade in kbit/s.
	 * @return the maximum speed to expect on this cascade in kbit/s; if < 0, then no information is available; 
	 * if >= SPEED_LOW, then this speed  is below measurement (very bad)
	 */
	private int getMaximumSpeed()
	{
		int best = PerformanceInfo.getLowestCommonBoundEntry(
				m_service.getMixCascade().getId()).getBestBound(
				PerformanceEntry.SPEED);
		
		if (best < getMinimumSpeed())
		{
			// this might happen if not all InfoServices send best bounds
			return getMinimumSpeed();
		}
		
		return best;
	}
	
	/**
	 * Get the maximum delay to expect on this cascade in ms.
	 * @return the maximum delay to expect on this cascade in ms; if < 0, then no information is available; 
	 * if >= DELAY_HIGH, then this delay is above measurement (very bad)
	 */
	private int getMaximumDelay()
	{
		return PerformanceInfo.getLowestCommonBoundEntry(
				m_service.getMixCascade().getId()).getBound(
				PerformanceEntry.DELAY).getBound();
	}
	
	/**
	 * Get the minimum delay to expect on this cascade in ms.
	 * @return the minimum delay to expect on this cascade in ms; if < 0, then no information is available; 
	 * if >= DELAY_HIGH, then this delay is above measurement (very bad)
	 */
	private int getMinimumDelay()
	{
		return PerformanceInfo.getLowestCommonBoundEntry(
				m_service.getMixCascade().getId()).getBestBound(
				PerformanceEntry.DELAY);
	}
	
	public String toStringSpeed()
	{	
		int iMinimumSpeed = getMinimumSpeed();
		int iMaximumSpeed = getMaximumSpeed();
		
		String strPerformance = "";
		String strSpeed = Util.formatKbitPerSecValueWithUnit(
				 getMinimumSpeed(), 
					Util.MAX_FORMAT_KBIT_PER_SEC);
		
		if (iMinimumSpeed >= 0 && iMinimumSpeed < Integer.MAX_VALUE)
		{
			if (iMinimumSpeed == 0)
			{
				strPerformance += "< " + Util.formatKbitPerSecValueWithUnit(SPEED_LOWER_BOUND,
						Util.MAX_FORMAT_KBIT_PER_SEC);
			}
			else if (SPEED_UPPER_BOUND == iMaximumSpeed)
			{
				if (ms_comparableSignFormat == COMPARABLE_SIGN_FORMAT_GUI)
				{
					strPerformance +="\u2265";
				}
				else
				{
					strPerformance += ">=";
				}
				strPerformance += " " + strSpeed;
				
			}
			else if (getMaximumSpeed() == iMinimumSpeed || iMaximumSpeed == Integer.MAX_VALUE)
			{
				strPerformance += strSpeed;
			}
			else
			{
				strPerformance += Util.formatKbitPerSecValueWithoutUnit(
						getMinimumSpeed(), Util.MAX_FORMAT_KBIT_PER_SEC) + "-" + 
						Util.formatKbitPerSecValueWithUnit(getMaximumSpeed(), 
								Util.MAX_FORMAT_KBIT_PER_SEC);
			}
			return strPerformance;
		}
		
		return null;
	}
	
	public String toStringDelay()
	{
		String strPerformance = "";
		int iMinimumDelay = getMinimumDelay();
		int iMaximumDelay = getMaximumDelay();
		
		
		if (iMaximumDelay > 0)
		{
			if (iMaximumDelay == Integer.MAX_VALUE)
			{
				strPerformance += "> " + DELAY_UPPER_BOUND + " ms";
			}
			else if (DELAY_LOWER_BOUND == iMinimumDelay)
			{
				if (ms_comparableSignFormat == COMPARABLE_SIGN_FORMAT_GUI)
				{
					strPerformance += "\u2264";
				}
				else
				{
					strPerformance += "<=";
				}
				strPerformance += " " + iMaximumDelay + " ms";
			}
			else if(iMinimumDelay == iMaximumDelay || iMinimumDelay == 0)
			{
				strPerformance += iMaximumDelay + " ms";
			}
			else
			{
				strPerformance += iMaximumDelay + "-" + iMinimumDelay + " ms";
			}
			return strPerformance;
		}
		
		return null;
	}
}
