/*******************************************************************************
 * GenRGenS v.1.0, Generation of Random Genomic Sequences                      *
 * Copyright (C) 2001  Alain Denise/Yann Ponty                                 *
 *                                                                             *
 * 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 GenRGenS.master;

/*****************************************************************************
*              classe Master v1
*
*   Classe principale de la génération "chainée"
******************************************************************************/

import java.io.*;
import java.util.*;
import GenRGenS.*;
import GenRGenS.markov.*;
import GenRGenS.grammar.*;
import GenRGenS.utility.*;

public class Master
  implements Generator
{
    /*
      Contient les chemins des fichiers "maitres" en cours d'utilisation. 
      Permet de detecter et d'interdire les dependances circulaires.
    */
    public static Hashtable usedMasterFiles = new Hashtable(13);
    public static String firstFile = "";
    
    
    /*
      Chemin du fichier maitre a partir duquel les chemins absolus des fichiers apparaissant
      seront déduis de leurs chemins relatifs.
    */
    
    public static String masterFileDir = "";

		
		/*
		  Specifie si les sequences generees doivent l'etre dans un format ou les symboles sont 
		  separe par un caractère espace ou non.
		*/
		private boolean words = true;
		
		/*
		  Table associant un nom de symbole dans le fichier principal de la génération au 
		  générateur permettant d'en "developper" le contenu.
		*/
		private Hashtable symbol2Generator = new Hashtable(7);

		/*
		  Table associant un nom de symbole dans le fichier principal de la génération au 
		  générateur permettant d'en "developper" le contenu.
		*/
		private Hashtable symbol2DistributionLaw = new Hashtable(7);
		
		private Generator mainGenerator = null;
		private int symbolNumber = 0;
		
		private Hashtable symbol2Index = new Hashtable(7);
		private String[] Symbols = new String[0];
		
		private Hashtable generator2Offset = new Hashtable(7);
		
		
		/************************** Constructeurs *****************************/

		public Master(String s)
				throws GeneratorCreationException
		{
				throw (new GeneratorCreationException("Can't use masterfile generation from an applet !\n(Can't reach \"slave\" files due to security restrictions ...)"));
		};

		public Master(File f)
				throws IOException,BadMasterFileException
		{
		  Reader r = new FileReader(f);
		  if (firstFile.equals(""))
		  {
		    firstFile = f.getPath();
  		  masterFileDir = f.getCanonicalFile().getParent();
  		  usedMasterFiles = new Hashtable(7);
		  }
      if (usedMasterFiles.containsKey(f.getPath()))
	    {
	      throw (new BadMasterFileException("Circular dependancy for file \""+f.getName()+"\"."));
	    }
		  
		  usedMasterFiles.put(f.getPath(),f.getPath());
      ParseMaster p = null;
      GenRGenSScanner s = new GenRGenSScanner(r);
      p = new ParseMaster(s);
      try
      {
        if (GenRGenS.verbose)
        { GenRGenS.err.println("Parsing description file ..."); }
        p.parse();
      }
      catch(Exception e)
      {
      	GenRGenS.err.println("Exception thrown while parsing description file :"+e);
      }
      if (p.isParseError())
      {
        throw(new BadMasterFileException("Invalid description file !"));
      }
      
      if (GenRGenS.verbose)
      { GenRGenS.err.println("Parsing of \""+f.getName()+"\" successfull !"); }
      if (firstFile.equals(f.getPath()))
      {
        firstFile = "";
      }
      
      words = p.isWords();
      symbol2Generator = p.getSymbol2Generator();
      symbol2DistributionLaw = p.getSymbol2DistributionLaw();
      mainGenerator = p.getMainGenerator();
      
      // First, we count terminal symbols ...
      
 		  Iterator i = symbol2Generator.values().iterator();
		  symbolNumber = mainGenerator.numberOfterminalsSymbols();

		  while(i.hasNext())
		  {
		    Generator g = (Generator) i.next();
		    symbolNumber += g.numberOfterminalsSymbols();
		  }
		  
		  // ... then we fill the symbols array and hashtable ...
		  
		  Symbols = new String[symbolNumber];
		  int tmp = 0;
		  for(int j=0;j<mainGenerator.numberOfterminalsSymbols();j++)
		  {
		    Symbols[j] = mainGenerator.symbolName(j);
		  }
		  tmp = mainGenerator.numberOfterminalsSymbols();
		  i = symbol2Generator.values().iterator();
		  while(i.hasNext())
		  {
		    Generator g = (Generator) i.next();
		    generator2Offset.put(g,new Integer(tmp));
  		  for(int j=0;j<g.numberOfterminalsSymbols();j++)
	  	  {
		      Symbols[tmp+j] = g.symbolName(j);
		      symbol2Index.put(g.symbolName(j),new Integer(tmp+j));
		    }
		    tmp += g.numberOfterminalsSymbols();
		  }
		};
		
		public boolean isWORDS()
		{
		  return words;
		}
		
		public int numberOfterminalsSymbols()
		{
		  return symbolNumber;
		}
		
		public int symbolIndex(String s) throws IllegalArgumentException
		{
		  if(symbol2Index.containsKey(s))
		  {
		    return ((Integer)symbol2Index.get(s)).intValue();
		  }
		  else
		  {
 				throw new IllegalArgumentException("int symbolIndex(String) : "+s+" is not a terminal symbol");
		  }
		}

		public String symbolName(int i) throws IllegalArgumentException
		{
		  if ((i>=0)&&(i<symbolNumber))
		  {
		    return Symbols[i];
		  }
		  else throw new IllegalArgumentException("String symbolName(int) : index out of bounds");
		}
		
    public StringBuffer litteralSequence(List sequence, boolean spacing)
    {
      StringBuffer result = new StringBuffer();
      return result;
    };
		
		public boolean isTerminalSymbol(String s)
		{
		  return symbol2Index.containsKey(s);
		}
		
		// renvoit une representation litterale du chainage
		public String toString()
		{
				/*GenerationLink actualGenerationLink;
				GenerationLink[] actualArrayTermLinks;
				String fileLinkKey;
				StringBuffer temp = new StringBuffer
						("Root generation file : \""+startFileName+"\"\n");
				for (Enumeration e = fileLink.keys() ; e.hasMoreElements() ;)
						{
								// cle de FileLink Courant
								fileLinkKey=(String) e.nextElement();
								temp.append("\n++ links for file \""+fileLinkKey+"\" :\n");
								actualArrayTermLinks = 
										(GenerationLink[]) fileLink.get(fileLinkKey);
								for (int index=0; index<actualArrayTermLinks.length; index++)
										{
												temp.append
														(
														 ((Generator) file2generator.get(fileLinkKey))
														 .symbolName(index)+"("+index+") ---> ");
												actualGenerationLink = actualArrayTermLinks[index];
												if (actualGenerationLink==null)
														temp.append("(final)\n");
												else
														temp.append("\""+
																				actualGenerationLink.getFileName()+
																				"\" ["+actualGenerationLink.getSize()+"]\n"
																				);
										};
						};
				return temp.toString();*/
				return "";
		};

		public StringBuffer generateRandomWord(int size)
				throws GenerationException
		{
				Vector joe = generateTokenizedRandomWord(size);
				StringBuffer buff = new StringBuffer();
				for(int i =0;i<joe.size();i++)
				{
				  buff.append(Symbols[(((Integer)joe.elementAt(i)).intValue())]+(words?" ":""));
				}
				return(buff);
		};

		public Vector generateTokenizedRandomWord(int size)
				throws GenerationException
		{
				Vector buff = mainGenerator.generateTokenizedRandomWord(size);
				for(int i =0;i<buff.size();)
				{
				  String sym = mainGenerator.symbolName(((Integer) buff.elementAt(i)).intValue());
				  if (symbol2Generator.containsKey(sym))
				  {
				      Generator gen = (Generator) symbol2Generator.get(sym);
				      DistributionLaw law = (DistributionLaw) symbol2DistributionLaw.get(sym);
				      int n = (int) Math.round(law.getNumber(size));
				      if(n<0) {n=0;}
				      Vector tmp = gen.generateTokenizedRandomWord(n);
				      int offset = ((Integer)generator2Offset.get(gen)).intValue();
				      buff.remove(i);
				      for(int j = 0; j<tmp.size();j++)
				      {
				        buff.insertElementAt(new Integer(((Integer)tmp.elementAt(j)).intValue()+offset),i+j);
				      }
				      i+=tmp.size();
				  }
				  else
				  {
				    i++;
				  }
				}
				return(buff);
		};

	/************************ Methode main (tests) ***********************/
		private static void main(String[] arg)
		{
				
			/*	if (arg.length<2)
						{
								GenRGenS.out.println
										("usage : \n"+
										 "\tjava Linking <size> <number of generation>");
								System.exit(1);
						};
				
				int howMany = (Integer.valueOf(arg[1])).intValue();
				int size = (Integer.valueOf(arg[0])).intValue();
				
				String chemin="../testfiles/Linking/";
				Linking lnk=null;
				File[] directoryListing = 
						(new File(chemin).listFiles
						 (new FileExtensionFilter("ggm")));
				PrintStream testResult;
				for (int numFichier=0;
						 numFichier<directoryListing.length;
						 numFichier++)
						{
								// = null : seule solution trouvée au prb du
								// try/catch et "may not have been init."
								testResult = null;
								try
										{
												testResult = new PrintStream
														(new FileOutputStream
														 (directoryListing[numFichier].toString()+
															".result"));
										}
								catch (IOException e) 
										{ 
												GenRGenS.out.println("Damned ! "+e.toString());
										}
								if (testResult!=null)
										{
												try
														{
															testResult.println
																		("=*=*= Result for file "+
																		 directoryListing[numFichier]+" =*=*=");
															lnk = new Linking	
																	(new FileReader
																	 (directoryListing[numFichier]),
																	 chemin,
																	 false);
															testResult.println
																	("Stored Linking :\n"+lnk.toString());
															for (int i=0; i<howMany; i++)
																	{
																			try
																					{
																							testResult.println
																									("*"+(i+1)+"* "+
																									 lnk.generateRandomWord
																									 (size));
																					}
																			catch (GenerationException e)
																					{
																							testResult.
																									println(e.toString());
																					};
																	};
															testResult.println("\n\n\n");
															testResult.close();
															GenRGenS.out.println
																	("wrote "+
																	 directoryListing[numFichier].toString()+
																	 ".result");
														}
												catch (IOException ioe) 
														{ testResult.println 
																	("Damned ! "+ioe.toString()); }
												catch (BadMasterFileException bmfe)
														{ testResult.println(bmfe.toString());};
												catch (GeneratorCreationException gce)
														{ testResult.println(gce.toString());};
										};
								
						};
				GenRGenS.out.println("All done !");
				System.exit(0);*/
		};

}
