import java.text.MessageFormat;
import java.util.Vector;

/** Kursus-klasse. Objekter af denne klasse beskriver et kursus. Et kursus har følgende egenskaber tilknyttet: <ul>
  * <li>Navn - navn på dette kursus</li>
  * <li>Nummer - nummeret på dette kursus. Dette nummer <b>skal</b> være unikt, ellers kan bindingerne ikke laves rigtigt!</li>
  * <li>Pointtal - ubetydeligt</li>
  * <li>Skemamoduler - moduler, som dette kursus ligger i. Hvis kurset ligger i forårsmoduler, antages at det afholdes om foråret. Er der 
  *     angivet efterårsmoduler antages det tillige at det også afholdes om efteråret. Angives kun efterårsmoduler antages det at kurset
  *     kun afholdes om efteråret.</li>
  * <li>Forudsætninger - Hver kursusobjekt kan undersøge om de nødvendige forudsætninger er til stede for tilmelding til dette kursus.</li>
  * </ul>
  * Disse kursusobjekter anvendes af Semester-objekter til at indeholde information om kurserne.<br>
  *
  * @see Semester
  * @see Planner
  * @version 1999-05-12
  * @author Rune
  */
public class Kursus
{
	/* Fejl-beskeder */	
	private static final String E_ML_NUMMER    = "Forventede kursusnummer";
	private static final String E_ML_NAVN      = "Forventede navn" ;
	private static final String E_SYNTAKS      = "Syntaksfejl:";
	private static final String E_UGY_MODUL    = "Ugyldigt modulnummer: ";
	private static final String E_UGY_SEMESTER = "Ugyldigt semester i ";
	private static final String E_FORUD_OGTEGN = "Fejl i forudsætningsliste - forventede &";
	private static final String E_FORUD        = "Forudsætninger ikke i orden for kursus: ";
	private static final String E_ML_MODUL     = "Mangler angivelse af modul, fag afholdes ikke:";
	private static final String E_ML_POINT     = "Forventede pointangivelse";
	private static final String E_ = "";
	
	/** Outputformat for toString(). 0=nummer, 1=navn, 2=point, 3=moduler, 4=forudsætninger. */
	private final static String OUTPUT = "{0}\t{1}\t({2} p.)\t\t{3}\t{4}";
	/** Kursusnummer som i håndbogen */
	private int kursusNr = 0;
	/** Kursusnavn */
	private String navn = "";
	/** Point for kursus */
	private double point = 0.0;
	/** Skemamodul, hvori det er placeret om foråret, =null hvis ikke afviklet */
	private int[] fModul;
	/** Skemamodul, hvori det er placeret om efteråret, =null hvis ikke afviklet */
	private int[] eModul;
	/** Hvilke forudsætninger kræves. Og lodret, eller vandret */
	private int forudsaet[][];
	
	
	/** Laver et nyt kursusobjekt.
	  * @param nr Kursusnummer fra studiehåndbog
	  * @param nvn Kursusnavn
	  * @param pnt Antal point givet for kursus
	  */
	public Kursus( int nr, String nvn, double pnt )
	{
		kursusNr = nr;
		navn = nvn;
		point = pnt;
	}
	
	
	/** Laver et nyt kursusobjekt baseret på en tekststreng med følgende format:<br>
	  * <code>[nummer] [navn] [pointtal] [moduler] [forudsætninger]</code><br>
	  * [nummer] - heltal<br>
	  * [navn] - tekststreng, evt. med ""<br>
	  * [pointtal] - kommatal<br>
	  * [moduler] - liste over brugte skemamoduler: E1 E2 F1 = efterårsmoduler 1 og 2, forårsmodul 1. Kurset afvikles kun hvis det tildeles moduler.<br>
	  * [forudsætninger] - liste over forudsætningskurser. Alle er heltal. ex. 01001 01010 & 01012 = et af kurserne 01001 og 01010 samt 01012 er krævet.<br>
	  * @param s Streng med beskrivelse
	  * @exception ESyntaks smides når der er en syntaksfejl i tekststrengen.
	  */
	public Kursus( String s ) throws ESyntax
	{
		Tokenizer tk = new Tokenizer(s);
		
		if (tk.nextType()==Tokenizer.INT)
			kursusNr = ((Integer)tk.nextToken()).intValue();
		else
			throw new ESyntax( E_ML_NUMMER, tk.getLn() );
			
		// Prøv at læse navn
		if ( tk.nextType() == Tokenizer.STR )
			navn = (String)tk.nextToken();
		else
			throw new ESyntax( E_ML_NAVN, tk.getLn() );

		// Prøv at læse point
		if ( tk.nextType() == Tokenizer.INT )
			point = ((Integer)tk.nextToken()).intValue();
		else
			throw new ESyntax( E_ML_POINT, tk.getLn() );

		// Prøv at læse moduler ind: syntaks: <e|f><1|2|3|4|5|6|7> 
		if ( tk.nextType() != Tokenizer.STR )
			throw new ESyntax( E_ML_MODUL, tk.getLn() );
		while ( tk.nextType() == Tokenizer.STR)
		{
			String m = ((String)tk.nextToken());
			int modul = 0;
			
			if (m.length() < 2) 
				throw new ESyntax( E_UGY_MODUL+m, tk.getLn() );
			switch (Character.toUpperCase(m.charAt(1)))
			{
				case '1' : modul = 1; break;
				case '2' : modul = 2; break;
				case '3' : modul = 3; break;
				case '4' : modul = 4; break;
				case '5' : modul = 5; break;
				case '6' : modul = 6; break;
				case '7' : modul = 7; break;
				default: throw new ESyntax( E_UGY_MODUL+m, tk.getLn() );
			}
			switch (Character.toUpperCase(m.charAt(0)))
			{
				case 'F' : addForModul(modul); break;
				case 'E' : addEftModul(modul); break;
				default: throw new ESyntax( E_UGY_SEMESTER+m, tk.getLn() );
			}
		}
					
					
		// For resten af strengen prøves dekodning, dekod indtil &-tegn, tilføj gruppe og fortsæt dekodning
		while (tk.nextType() != Tokenizer.NONE)
		{
			Vector ellerforud = new Vector();
			while ( tk.nextType() == Tokenizer.INT )
			{
				ellerforud.addElement( tk.nextToken() );
			}
			int forud[] = new int[ellerforud.size()];
			for (int lp1=0; lp1<forud.length; lp1++)
			{
				forud[lp1] = ((Integer)ellerforud.elementAt(lp1)).intValue();
			}
			addForudsGrp( forud );
			if (tk.nextType() == Tokenizer.STR )
				if (!((String)tk.nextToken()).equals("&"))
					throw new ESyntax( E_FORUD_OGTEGN, tk.getLn());
		}
	}
	
	
	
	
	
	/** Tilføjer et forårsmodul til modullisten.
	  * @param modulnr Nummer på forårsmodul, som skal tilføjes
	  */
	public void addForModul( int modulnr )
	{
		// hvis ingen moduler anvendes	
		if (fModul==null) 
		{
			fModul = new int[1];
			fModul[0] = modulnr;
			return;
		}
		// se om det allerede er markeret
		if ( in(modulnr, fModul) ) return ;
		// Tilføj til listen
		int t[] = new int[fModul.length+1];
		System.arraycopy(fModul, 0, t, 0, fModul.length);
		t[fModul.length] = modulnr;
		fModul = t;
	}
	
	
	

	/** Tilføjer et efterårsmodul til modullisten.
	  * @param modulnr Nummer på eftersmodul, som skal tilføjes
	  */
	public void addEftModul( int modulnr )
	{
		// hvis ingen moduler anvendes	
		if (eModul== null) 
		{
			eModul = new int[1];
			eModul[0] = modulnr;
			return;
		}
		// se om det allerede er markeret
		if ( in(modulnr, eModul) ) return;
		// Tilføj til listen
		int t[] = new int[eModul.length+1];
		System.arraycopy(eModul, 0, t, 0, eModul.length);
		t[eModul.length] = modulnr;
		eModul = t;
	}
	
	
	
	
	/** Tilføjer en forudsætningsgruppe til kurset 
	  * @param grp Forudsætningsgrupper, der skal tilføjes.
	  */
	public void addForudsGrp( int grp[] )
	{
		if (forudsaet==null)
		{
			forudsaet = new int[1][];
			forudsaet[0] = grp;
			return;
		}
		
		int t[][] = new int[forudsaet.length+1][];
		System.arraycopy( forudsaet, 0, t, 0, forudsaet.length );
		t[forudsaet.length] = grp;
		forudsaet = t;
		
	}
	
	
	
	/** @return Nummer på dette kursus */
	public int getNr()
	{
		return kursusNr;
	}



	/** @return Navn på dette kursus */
	public String getNavn()
	{
		return navn;
	}



	/** @return Point tildelt for dette kursus */
	public double getPoint()
	{
		return point;
	}
	
	

	/** @return Liste over forårsmoduler optaget af dette kursus. */
	public int[] getForModul()
	{
		return fModul;
	}



	/** @return Liste over efterårsmoduler optaget af dette kursus. */
	public int[] getEftModul()
	{
		return eModul;
	}

	
	
	/** @return Kører dette kursus om foråret. */
	public boolean isForaar()
	{
		return (fModul!=null) && (fModul.length>0);
	}


	
	
	/** @return Kører dette kursus om efteråret. */
	public boolean isEfteraar()
	{
		return (eModul!=null) && (eModul.length>0);
	}


	
	
	/** Intern metode til at bestemme om et tal er i en tabel 
	  * @param i Det tal du søger
	  * @param arr Den tabel, det skulle være i 
	  */
	private boolean in( int i, int arr[] )
	{
		for (int lp1=0; lp1<arr.length; lp1++)
			if ( arr[lp1] == i )
				return true;
		return false;
	}


	/** Er faget givet en forudsætning for dette fag */
	public boolean erForudsaetning( int nr )
	{
		if (forudsaet==null) return false;
		boolean ok = false;
		for (int lp1=0; lp1<forudsaet.length; lp1++)
		 ok |= in( nr, forudsaet[lp1] );
		return ok;
	}
	
	
	
	/** Prøv om de givne forudsætninger er gode nok til dette kursus... 
	  * @param foruds Liste over de forudsætninger, man har
	  */	
	public boolean harForudsaetninger( int foruds[] )
	{
		if (forudsaet==null) return true;
		
		boolean ok = true;
		
		for (int lp1=0; lp1<forudsaet.length; lp1++)
		{
			boolean raekke=false;
			for (int lp2=0; lp2<forudsaet[lp1].length; lp2++)
				raekke |= in( forudsaet[lp1][lp2], foruds );
			ok &= raekke;
		}
					
		return ok;
	}
	
	
	public String toString()
	{
		String fs="";
		if (forudsaet!=null)
			for (int lp1=0; lp1<forudsaet.length; lp1++)
			{
				String ss = "";
				for (int lp2=0; lp2<forudsaet[lp1].length; lp2++)
					ss +=(forudsaet[lp1][lp2]<10000?"0":"")+forudsaet[lp1][lp2]+",";
				if (ss.length()>0)
					fs+=(fs.length()>0?" & ":"") +ss.substring(0,ss.length()-1);
			}
		
		String md="";
		if (eModul!=null)
			for (int lp1=0; lp1<eModul.length; lp1++)
				md += "E"+eModul[lp1]+" ";
		if (fModul!=null)
			for (int lp1=0; lp1<fModul.length; lp1++)
				md += "F"+fModul[lp1]+" ";
		
		Object p[] = {
			(kursusNr<10000?"0":"")+kursusNr,
			navn,
			""+point,
			md,
			fs};
			
		return MessageFormat.format(OUTPUT, p);
	}
}