package edu.vt.marian.common;

import java.io.*;
import java.net.*;
import java.util.*;

/**
	An object together with a weight (e.g., match to a query).
	@author: Robert France (france@vt.edu)
	@see	FullID
	@see	Weight
	@see	Sortable
*/

public class WtdObj extends FullID implements Sortable
{
	/** the weight value for this object.	*/
	protected Weight weight;	// Always created INVALID.

	/** the methods' return values */
	public final static int INVALID = -1;

	/** this is just used for debugging */
	protected Debug debug;


	/**
		Create an invalid WtdObj.
		@param	debug -- used for debugging
	*/      
	public WtdObj(Debug debug)
	{
		super(debug);
	}

	/**
		Create a WtdObj equal to another WtdObj in all ways.
		@param	w -- the WtdObj to be copied
		@param	debug -- used for debugging
	*/      
	public WtdObj(WtdObj w, Debug debug)
	{
		super(w.getID(), debug);
		weight = w.getWeight();		// Returns a new Weight.
	}

	/**
		Create a WtdObj from a FullID and a Weight.
		@param	id -- the (full) ID of this object;
		@param	wt -- the weight assigned to this object;
		@param	debug -- used for debugging
	*/      
	public WtdObj(FullID id, Weight wt, Debug debug)
	{
		super(id, debug);
		weight = new Weight(wt);
	}

	/**
		Create a WtdObj from atomic values.
		@param	clID -- the class ID of this object;
		@param	instID -- the instance ID of this object;
		@param	wt -- the weight assigned to this object;
		@param	debug -- used for debugging
	*/      
	public WtdObj(int clID, int instID, Weight wt, Debug debug)
	{
		super(clID, instID, debug);
		weight = new Weight(wt);
	}


	/**
		Create a WtdObj from an input stream.
		@param	br -- the stream from which to read out this object;
		@param	debug -- used for debugging
	*/      
	public WtdObj(BufferedReader br, Debug debug)
	{
		super(br, debug);
		weight = new Weight(br, debug);
		this.debug = debug;
	}



	/**
		Indicate whether or not the this WtdObj is valid.
		@return	true / false
	*/      
	public boolean isValid()
	{
		return ( super.isValid() && weight.isValid() );
	}


	/**
		How does this compare to w by weight?
		@param	w -- another WtdObj
		@return	(< 0) lower weight
		<BR>	(== 0) same weight
		<BR>	(> 0) higher weight
	*/
	public int compare(WtdObj w)
	{
		return( weight.compare(w.weight) );
	}

	/**
		How does this compare to a constant weight?
		@param	wt -- a constant Weight
		@return	(< 0) lower weight
		<BR>	(== 0) same weight
		<BR>	(> 0) higher weight
	*/
	public int compare(Weight wt)
	{
		return( weight.compare(wt) );
	}

	/**
		The functional union of compare(WtdObj) and compare(Weight).
	*/
        public int compare(Object obj)
        {
	    if (obj instanceof WtdObj)
		return this.compare((WtdObj) obj);
	    else if (obj instanceof Weight)
		return this.compare((Weight) obj);
	    else
		return Sortable.INCOMPARABLE;
	}


	/**
		Scale the weight of this WtdObj by a Weight.
		@param	wt -- the value to scale by.
	*/      
	public void scale(Weight wt)
	{
		weight.scale(wt);
	}

	/**
		Scale the weight of this WtdObj by a double between 0..1.
		@param	f -- the value to scale by.
	*/      
	public void scale(double f)
	{
		weight.scale(f);
	}

	/**
		"Add" a Weight to the weight of this WtdObj.
		@param	wt -- the value to scale by.
	*/      
	public void accum(Weight wt)
	{
		weight.accum(wt);
	}

	/**
		"Add" a double between 0..1 to the weight of this WtdObj.
		@param	f -- the value to scale by.
	*/      
	public void accum(double f)
	{
		weight.accum(f);
	}


	/**
		Return the weight of this WtdObj.
		@return	the value of this WtdObj as a (new) Weight.
	*/      
	public Weight getWeight()
	{
		return( new Weight(weight) );
	}

	/**
		Return the id portion of this WtdObj.
		@return	the value of this WtdObj as a (new) FullID.
	*/      
	public FullID getID()
	{
		return( new FullID(classID, instanceID, debug) );
	}



	/**
		Pack a WtdObj into a Byte array of small, fixed size.
		@param	s -- the ByteArrayOutputStream into which to pack this.
		@return	OK or INVALID
		<P><B>FORMAT:</B>  classID and weight by design will each fit into two bytes.  To minimize
		allignment problems, we pack those two first and the four-byte instanceID
		last.  %lt;Wt>%lt;ClID>%lt;InstID>.
	*/      
	public int writePacked(ByteArrayOutputStream s)
	{
		if (! isValid() )
			return(INVALID);
		int ErrCode;
		if ( (ErrCode = weight.writePacked(s)) != OK)
			return(ErrCode);
                if ( (ErrCode = super.writePacked(s)) != OK)
			return(ErrCode);
		return(OK);
	}

	/**
		Unpack a WtdObj from a Byte array of small, fixed size.
		@param	s -- the ByteArrayInputStream from which to unpack this.
		@return	OK, NULL_STREAM, or INVALID
		<P>FORMAT:  see writePacked().
	*/      
	public int readPacked(ByteArrayInputStream s)
	{
		int ErrCode;
		if ( (ErrCode = weight.readPacked(s)) != OK)
			return(ErrCode);
                if ( (ErrCode = super.readPacked(s)) != OK)
			return(ErrCode);

		if (! isValid() )
			return(INVALID);
		return(OK);
	}


	/**
		Print the content of this object to a stream.
		@param	pw -- the stream to write this object
		@return	OK -- the object has been written to the stream correctly
		<BR>	NULL_STREAM -- the parameter stream is null 
	*/      
	public int toStream(PrintWriter pw)
	{
		if (pw == null)
		{
			debug.dumpTrace("WtdObj:toStream(): stream is null");
			return NULL_STREAM;
		}

		// pw is not null:  no other error return is possible from below.
		super.toStream(pw);
		weight.toStream(pw);
		pw.flush();
		return(OK);
	}

	/**
		Create a human-readable string for this.
		@return	A string of the format #classID:instanceID:weight# for
			any valid object, or @null@ for an invalid one. 
	*/      
	public String toString()
	{
		if (! isValid() )
			return( new String("@null@") );

		return( new String("#" + classID + ":" + instanceID + ":" +
		                   weight.getValue() + "#") );
	}
}
