public class PointPeriodRelation extends PeriodRelation
{
	public static final short L = 1;
	public static final short E = 2;
	public static final short G = 4;
	public static final short ANY = 4095;
	
	public static final int SS = 0;
	public static final int SE = 3;
	public static final int ES = 6;
	public static final int EE = 9;
	
	private static int stringReadPos = 0; 
		//used by readEndPhase and readMidPhase
	
	public PointPeriodRelation()
	{
	}
	
	public PointPeriodRelation(String s) throws FormatException
	{
		set(s);
	}
	
	public PointPeriodRelation(int d) throws FormatException
	{
		set(d);
	}
	
	public PointPeriodRelation(PointRelation ss, PointRelation se,
		PointRelation es, PointRelation ee) throws FormatException
	{
		if (!valid(ss, se, es, ee))
			throw new FormatException(
				"Bad point period relation: (" 
				+ ss.toString() + ", "
				+ se.toString() + ", "
				+ es.toString() + ", "
				+ ee.toString() + ")");
		data = (short)(ss.data | (se.data<<SE) | (es.data<<ES) 
			| (ee.data<<EE));
	}		
	
	public String toString()
	{
		PointRelation ss = ssComponent();
		PointRelation se = seComponent();
		PointRelation es = esComponent();
		PointRelation ee = eeComponent();
		
		if (ss.isNull() || se.isNull() || es.isNull() || ee.isNull())
			return "null";
		else
		{
			return ( "(" +
				ss.toString() + ", " +
				se.toString() + ", " +
				es.toString() + ", " +
				ee.toString() + ")");
		}
	}
	
    public void set(String s) throws FormatException
    {
    	if (s.equals("null"))
    		data = 0;
    	else if (s.length() < 2)
            throw (new FormatException
                ("Bad point period relation string: " + s));
    	else if (s.charAt(0) != '(' 
    			|| s.charAt(s.length()-1) != ')')
            throw (new FormatException
                ("Bad point period relation string: " + s));
        else
    	{
    		PointRelation ss, se, es, ee;
    		
    		stringReadPos = 1;
    		ss = readComponent(s);
    		se = readComponent(s);
    		es = readComponent(s);
    		ee = readComponent(s);
			while (stringReadPos != s.length() && 
				s.charAt(stringReadPos++) == ' ');
    		if (stringReadPos != s.length())
            	throw (new FormatException
                	("Bad point period relation string: " + s));
            if (valid(ss, se, es, ee))
            	data = (short)(ss.data | (se.data<<SE) | (es.data<<ES)
            		| (ee.data<<EE));
            else
            	throw (new FormatException
                	("Bad point period relation string: " + s));
    	}
	}
	
    public void set(int d) throws FormatException
    {
        // This method is derived from class Relation
		if (!valid((d & (L|E|G)), ((d>>>SE) & (L|E|G)), 
				((d>>>ES) & (L|E|G)), ((d>>>EE) & (L|E|G))))
			throw (new FormatException
                ("Bad point period relation number: " + d));
		data = (short)d;
    }

	public void setRandom()
	{
		AllenPeriodRelation a = new AllenPeriodRelation();
		PointPeriodRelation p;
		
		while (true)
		{
			try
			{
				a.setRandom();
				p = a.toPointPeriodRelation();
				break;
			}
			catch (TranslationException e)
			{
			}
		}
		data = p.data;
	}

	public void setRandomDeterminate()
	{
		AllenPeriodRelation a = new AllenPeriodRelation();
		PointPeriodRelation p;
		
		a.setRandomDeterminate();
		try
		{
			p = a.toPointPeriodRelation();
		}
		catch (TranslationException e)
		{
    		throw (new RuntimeException("Unexpected Translation Exception"));
		}
		data = p.data;
	}

	public boolean isDeterminate()
	{
		return (toAllenPeriodRelation().isDeterminate());
	}

	public boolean isAny()
	{
		return (data == ANY);
	}
	
    public Relation intersection(Relation r)
    {
        // This method is derived from class Relation
        PointPeriodRelation p;
        
        if (!(r instanceof PeriodRelation))
        	return new PointPeriodRelation();
        try
        {
        	p = ((PeriodRelation)r).toPointPeriodRelation();
        	try
        	{
        		return new PointPeriodRelation(dataIntersection(p));
        	}
        	catch (FormatException e)
        	{
        		return new PointPeriodRelation();
        	}
        }
        catch (TranslationException e)
        {
        	AllenPeriodRelation a = 
        		(AllenPeriodRelation)toAllenPeriodRelation().intersection(r);      	
        	try
        	{
	    		return a.toPointPeriodRelation();
        	}
        	catch (TranslationException e2)
        	{
        		return a;
        	}
        }
    }

    public Relation inverse()
    {
        // This method is derived from class Relation
		PointRelation ss = ssComponent();
		PointRelation se = seComponent();
		PointRelation es = esComponent();
		PointRelation ee = eeComponent();
		try
		{
			return new PointPeriodRelation(
				(PointRelation)ss.inverse(), 
				(PointRelation)es.inverse(), 
				(PointRelation)se.inverse(), 
				(PointRelation)ee.inverse());
		}
		catch (FormatException e)
		{
			throw new RuntimeException(
				"Unexpected FormatException");
		}
    }

    public AllenPeriodRelation toAllenPeriodRelation()
    {
        // This method is derived from class PeriodRelation

		//Algorithm 6
		PointRelation ss = ssComponent(); //1
		PointRelation se = seComponent();
		PointRelation es = esComponent();
		PointRelation ee = eeComponent();
		int a = 0, b = 0, c = 0, d = 0; //2
		
		if (ss.includes(PointRelation.L))
			a = AllenPeriodRelation.LMODIFI;
		if (ss.includes(PointRelation.E))
			a |= AllenPeriodRelation.ESSI;
		if (ss.includes(PointRelation.G))
			a |= AllenPeriodRelation.DFGMIOI;
		if (se.includes(PointRelation.L))
			b = (AllenPeriodRelation.EOSDFOISIDIFI
				| AllenPeriodRelation.LMO);
		if (se.includes(PointRelation.E))
			b |= AllenPeriodRelation.MI;
		if (se.includes(PointRelation.G))
			b |= AllenPeriodRelation.G;
		if (es.includes(PointRelation.L))
			c = AllenPeriodRelation.L;
		if (es.includes(PointRelation.E))
			c |= AllenPeriodRelation.M;
		if (es.includes(PointRelation.G))
			c |= (AllenPeriodRelation.EOSDFOISIDIFI
				| AllenPeriodRelation.GMIOI);
		if (ee.includes(PointRelation.L))
			d = AllenPeriodRelation.LMOSD;
		if (ee.includes(PointRelation.E))
			d |= AllenPeriodRelation.EFFI;
		if (ee.includes(PointRelation.G))
			d |= AllenPeriodRelation.GMIOISIDI;
		try
		{
			return new AllenPeriodRelation(a & b & c & d);
		}
		catch (FormatException e)
		{
			return new AllenPeriodRelation();
		}
    }

    public PointPeriodRelation toPointPeriodRelation() throws TranslationException
    {
        // This method is derived from class PeriodRelation
        return this;
    }

    public SymbolicPeriodRelation toSymbolicPeriodRelation() throws TranslationException
    {
        // This method is derived from class PeriodRelation
        return toAllenPeriodRelation().toSymbolicPeriodRelation();
    }

	public PointPeriodRelation[] toPointPeriodRelations()
	{
		PointPeriodRelation[] pa = new PointPeriodRelation[1];
		
		pa[1] = this;
		return pa;
	}

    public Relation transition(Relation r)
    {
        // This method is derived from class Relation
        if (!(r instanceof PeriodRelation))
        	return new PointPeriodRelation();
        try
        {
        	PointPeriodRelation p = ((PeriodRelation)r)
        		.toPointPeriodRelation();
			PointRelation ss = ssComponent();
			PointRelation se = seComponent();
			PointRelation es = esComponent();
			PointRelation ee = eeComponent();
			PointRelation pss = p.ssComponent();
			PointRelation pse = p.seComponent();
			PointRelation pes = p.esComponent();
			PointRelation pee = p.eeComponent();
		
			return new PointPeriodRelation(
				(PointRelation)ss.transition(pss)
					.intersection(se.transition(pes)),
				(PointRelation)se.transition(pee)
					.intersection(ss.transition(pse)),
				(PointRelation)es.transition(pss)
					.intersection(ee.transition(pes)),
				(PointRelation)ee.transition(pee)
					.intersection(es.transition(pse)) );
		}
		catch (TimeRepresentationException e)
		{
			AllenPeriodRelation a = (AllenPeriodRelation)
        		(toAllenPeriodRelation().transition(
        			((PeriodRelation)r).toAllenPeriodRelation()));      	
        	return a;
        }
    }

    public Relation union(Relation r)
    {
        // This method is derived from class Relation
        AllenPeriodRelation a;
        
        a = (AllenPeriodRelation)(toAllenPeriodRelation().union(r));
        try
        {
        	return a.toPointPeriodRelation();;
        }
        catch (TranslationException e)
        {
        	return a;
        }
    }
    
    public PointRelation ssComponent()
    {
    	try
    	{
    		return new PointRelation(data & (L|E|G));
    	}
		catch (FormatException e)
		{
			throw new RuntimeException(
				"Unexpected FormatException");
		}
    }

    public PointRelation seComponent()
    {
    	try
    	{
	    	return new PointRelation((data>>>SE) & (L|E|G));
    	}
		catch (FormatException e)
		{
			throw new RuntimeException(
				"Unexpected FormatException");
		}
    }

    public PointRelation esComponent()
    {
    	try
    	{
	    	return new PointRelation((data>>>ES) & (L|E|G));
    	}
		catch (FormatException e)
		{
			throw new RuntimeException(
				"Unexpected FormatException");
		}
    }

    public PointRelation eeComponent()
    {
    	try
    	{
	    	return new PointRelation((data>>>EE) & (L|E|G));
    	}
		catch (FormatException e)
		{
			throw new RuntimeException(
				"Unexpected FormatException");
		}
    }

	//Private methods used by set(String)
	private PointRelation readComponent(String s) throws FormatException
	{
		char c;
		StringBuffer sb = new StringBuffer("");
		
		try
		{
			while ((c = s.charAt(stringReadPos)) == ',' 
				|| c == ' ')
			{
				stringReadPos++;
			}
			while ((c = s.charAt(stringReadPos)) != ',' 
				&& c != ' ' && c != ')')
			{
				sb.append(c);
				stringReadPos++;
			}
			return new PointRelation(sb.toString());
		}
		catch (StringIndexOutOfBoundsException e)
		{
			throw new FormatException(
				"Missing component in point period relation: " + s);
		}
	}
	
	private boolean valid(int ss, int se, int es, int ee)
		throws FormatException
	{
		return valid (new PointRelation(ss), 
			new PointRelation(se),
			new PointRelation(es),
			new PointRelation(ee));
	}
		
	private boolean valid(PointRelation ss, PointRelation se, 
			PointRelation es, PointRelation ee)
		throws FormatException
	{
		//Algorithm 10		
		if (!ss
				.transition(new PointRelation(PointRelation.L))
				.includes(se)) //2
			return false;
		if (!se
				.transition(new PointRelation(PointRelation.G))
				.includes(ss)) //3
			return false;
		if (!(new PointRelation(PointRelation.G))
				.transition(ss)
				.includes(es)) //4
			return false;
		if (!(new PointRelation(PointRelation.L))
				.transition(es)
				.includes(ss)) //5
			return false;
		if (!(new PointRelation(PointRelation.G))
				.transition(se)
				.includes(ee)) //6
			return false;
		if (!(new PointRelation(PointRelation.L))
				.transition(ee)
				.includes(se)) //7
			return false;
		if (!es
				.transition(new PointRelation(PointRelation.L))
				.includes(ee)) //8
			return false;
		if (!ee
				.transition(new PointRelation(PointRelation.G))
				.includes(es)) //9
			return false;
		return true; //10
	}
	
	public Relation propagate(Relation r2, Relation r3)
	{
		//Algorithm 20, function propagate(r1, r2, r3) [r1 = this]
		PointPeriodRelation p2, p3; //1
		PointRelation ss1, se1, es1, ee1, ss2, se2, es2, ee2,
			ss3, se3, es3, ee3, ss4, se4, es4, ee4;
		try {p2 = ((PeriodRelation)r2).toPointPeriodRelation();}
		catch (TranslationException e)
			{return new AllenPeriodRelation();}
		try {p3 = ((PeriodRelation)r3).toPointPeriodRelation();}
		catch (TranslationException e)
			{return new AllenPeriodRelation();}
		ss1 = ssComponent(); //3
		se1 = seComponent();
		es1 = esComponent();
		ee1 = eeComponent();
		ss2 = p2.ssComponent();
		se2 = p2.seComponent();
		es2 = p2.esComponent();
		ee2 = p2.eeComponent();
		ss3 = p3.ssComponent();
		se3 = p3.seComponent();
		es3 = p3.esComponent();
		ee3 = p3.eeComponent();
		ss4 = (PointRelation)ss1.propagate(ss2, ss3) //3.1
			.intersection(se1.propagate(ss2, es3)
			.intersection(es1.propagate(se2, ss3)
			.intersection(ee1.propagate(se2, es3))));	
		se4 = (PointRelation)ss1.propagate(ss2, se3)
			.intersection(se1.propagate(ss2, ee3)
			.intersection(es1.propagate(se2, se3)
			.intersection(ee1.propagate(se2, ee3))));
		es4 = (PointRelation)ss1.propagate(es2, ss3)
			.intersection(se1.propagate(es2, es3)
			.intersection(es1.propagate(ee2, ss3)
			.intersection(ee1.propagate(ee2, es3))));
		ee4 = (PointRelation)ss1.propagate(es2, se3)
			.intersection(se1.propagate(es2, ee3)
			.intersection(es1.propagate(ee2, se3)
			.intersection(ee1.propagate(ee2, ee3))));
		try
		{
			return new PointPeriodRelation(ss4, se4, es4, ee4); //4
		}
		catch (FormatException e)
		{
			return new PointPeriodRelation();
		}
	}
}