import java.io.*;
import java.util.StringTokenizer;
import java.util.Date;

/** executeThread
  * Implemente la gestion des commandes programmees
  *<PRE>
  * fichier d'entree: <BR>
  * command param<br>
  * command = get|put|at<br>
  * param = param_get_put | param_at<br>
  * param_get_put = source [fichier] [dest]<br>
  * param_at = date<br>
  * </pre>
  * @version        0.1 Feb 1997 
  * @author         Ebele Nicolas, Mallet Frederic
  */
class executeThread extends Thread
{
/** le nom de la classe (pour debugger) */
  static private String ClassName = "executeThread";
/** les commandes reconnues et implementees */
  static private String  Commands[] = {"get","put","at"};
/** le fichier ou on lit les commandes */
  private String SourceFile;
/** le fichier de log -> on y met le resultat des commndes */
  private PrintStream log;
/** le numero d'id du thread (chaque resultat de commandes est precedee par cet id */
  private int id;

/** la ligne courante de lecture du fichier de commande */
  private int LCourante = 0 ;
/** la ligne courante lue */
  private String CommandBack = "";

/** Constructeur
  *@param id l'identificateur du thread
  *@param file le nom du fichier de configuration
  *@param l le fichier de log pour y mettre les resultats des commandes
  */  
  executeThread(int Id, String file, PrintStream l)
  {
    log = l;
    id = Id;
    SourceFile = (file == null)?".exe0":file;
  }

/** recupere un, ou plusieurs fichiers
  * @param Source la source (Url+Rep) sans le fichier
  * @param Fic le fichier peut contenir une *
  * @param Dest ou mettre le fichier en local
  * @return renvoie vrai si tout c'est bien passe, faux sinon 
  */
  private boolean commandGet(String Source, String Fic, String Dest)
  {
    boolean Res = false;
    try {
      if (Dest == null) Dest = "";
      if (!Dest.endsWith("/"))
	Dest = Dest + "/";
      
      String path="";
      int j = Fic.lastIndexOf("/"); // separe le nom de fichier avec son path
      if (j != -1) {	 
	path = Fic.substring(0,j);
	Fic = Fic.substring(j+1);
      }
      boolean ftp = !Source.toLowerCase().startsWith("http://");
      // si ce n'est pas un hhtp, c'est un ftp
      String decrypt = Passwd.decryptUrl(Source);
      if ((j = Fic.indexOf("*")) != -1) { // il y a un * dans le fichier
	Init.println(ClassName, "attention liste de fichiers");
	
	
	
	Init.println(ClassName, "Source ="+ Source + " fic=" + Fic + " path=" + path + " dest=" +  Dest);
	AccessibleDir Connect;
	if (ftp)
	  Connect = new FtpDir(decrypt);
	else
	  Connect = new UrlDir(Source);
	
	String list[] = Connect.list(path, new RegexpFilter(Fic,Connect));
	Res = true;
	for(int t=0;t<list.length;t++) {
	  Init.println(ClassName, " Geting List de "+ path + "/" + list[t] +" vers"+Dest+ list[t] );
	  // Il faut changer ici pour faire du recursif ...
	  if (!Connect.isDirectory(path+"/"+list[t])) {
	    Res &= Connect.GetFile(path + "/" + list[t], Dest+list[t]); 
	  }
	}
      }
      else {  // ce n'est pas une liste *
	Init.println(ClassName, "----------->Source="+Source + " Fic=" + Fic + "path="+ path + " vers = " + Dest);
	AccessibleDir Connect;
	if (ftp)
	  Connect = new FtpDir(decrypt, false);  // pas de cache
	else
	  Connect = new UrlDir(Source, false); // pas de cache
	// lance l'exectution de la commande get	
	if (Dest.endsWith("/"))
	  return Connect.GetFile(path + "/" + Fic, Dest+Fic); 
	else
	  return Connect.GetFile(path + "/" + Fic, Dest+"/"+Fic);
	
      }
    }
    catch (Exception e) {
      log.println(id + "|" + LCourante +"|" + CommandBack + "->exception " +e);
      return false;
    }
    if (Source == null) {
      log.println(id + "|" +LCourante + "|" + CommandBack+ "->syntax error");
      return false;
    }
    return Res;
  }


/** met (upload) un, ou plusieurs fichiers
  * @param Source la source du / des fichiers en loacl (peut contenir une *)
  * @param Fic l'URL ou mettre les fichiers
  * @param Dest ou mettre le fichier en distant
  * @return renvoie vrai si tout c'est bien passe, faux sinon
  */
  private boolean commandPut(String Source, String Fic, String Dest)
  {
    boolean Res =  false;
    try {
      if (Dest.startsWith("/"))
	Dest = Dest.substring(1);

      Init.println(ClassName, "----------->Source="+Source + " Fic=" + Fic + " vers = " + Dest);
      boolean ftp = !Source.toLowerCase().startsWith("http://");
      int j;
      String path="";
      
      j = Source.lastIndexOf("/"); // re-split ...
      if (j != -1) {	 
	path = Source.substring(0,j);
	Source = Source.substring(j+1);
      }
      
      String decrypt = Passwd.decryptUrl(Fic);
      
      if ((j = Source.indexOf("*")) != -1) {
	Init.println(ClassName, "attention liste de fichiers");
	
	AccessibleDir Connect;
	Res = true;
	if (ftp) {
	  Connect = new FtpDir(decrypt, false); // pas besoin de cache
	  String list[] = new LocalDir().list(path, new RegexpFilter(Source));
	  
	  for(int t=0;t<list.length;t++) {
	    Init.println(ClassName, "putfile de " + path + "/" + list[t]+ " vers " +  Dest+list[t]);
	    if (new File(path+"/"+list[t]).isFile()){
	      Res &= Connect.PutFile(path + "/" + list[t], Dest+list[t]);
	    }
	  }
	}
	else {
	  log.println(id + "|" +LCourante + "|" + CommandBack+ "->non implemente");
	  return false;
	}
      }
      else {  // ce n'est pas une liste *
	Init.println(ClassName, "----------->Source="+Source + " Fic=" + Fic + " vers = " + Dest);
	AccessibleDir Connect;
	if (ftp) {
	  Connect = new FtpDir(decrypt, false); // pas de cache ...
	  if (Dest.endsWith("/"))
	    Res = Connect.PutFile(path + Source , Dest + Source);
	  else
	    Res = Connect.PutFile(path + Source , Dest + "/" + Source);
	}
	else {
	  log.println(id + "|" +LCourante + "|" + CommandBack+ "->non implemente");
	  return false;
	}
      }
    }
    catch (Exception e) {
      log.println(id + "|" +LCourante + "|" + CommandBack + "->exception "+e);
      return false;;
    }
    if (Source == null) {
      log.println(id + "|" + LCourante + "|" + CommandBack + "->syntax error");
      return false;
    }
    return Res;
  }

/** endort le thread jusqu'a une date
  * @param time jusque quand on endort le thread
  * @return renvoie vrai si tout c'est bien passe, faux sinon
  */
  private boolean commandAt(String time)
  {
    if (time == null)
      return false;
    Init.println(ClassName, "time="+time);
    long Now = new Date().getTime();
    long At = new Date(time).getTime();
    Init.println(ClassName, "at:"+At+" Now:"+Now+":"+(At-Now));
    if (Now <= At) {
      try {
	sleep(At-Now); // on endort le thread jusqu'a la bonne date ... 
      }
      catch (Exception e) {
	log.println(id + "|" + LCourante + "|" + CommandBack + "->exception "+e);
	return false;
      }
    }
    log.println(id + "|" +"apres ..." + new Date().toString());
    return true;
  }


/** lance le thread
  *@param pas de parametre
  *@return pas de valeur de retour
  */  
  public void run()
  {
    String inputLine = null;
    String Source = null;
    String Dest = null;
    String Command = null;
    String Fic = null;
    boolean Res = false;
 

    try {
      FileInputStream fou =  new FileInputStream(SourceFile);
      DataInputStream myOutput = new DataInputStream(fou);
      
      for(;;) {
	inputLine = myOutput.readLine(); // on lit ligne / ligne
	LCourante ++;
	if (inputLine == null) break;
	inputLine.trim();
	if (inputLine.length() > 0) {
	  CommandBack = inputLine;
          Init.println("Execute", "lu:"+inputLine);
	  StringTokenizer st = new StringTokenizer(inputLine);
	  for(int i=0;st.hasMoreTokens();i++) {
	    // lit different champ de la ligne
	    switch (i) {
	    case 0:
	      Command = st.nextToken();
	      break;
	    case 1:
	      Source = st.nextToken();
	      break;
	    case 2:
	      Fic = st.nextToken();
	      break;
	    case 3:
	      Dest = st.nextToken("\n"); // pour le dernier champ, on prend jusqu'a la fin de la ligne
	      break;
	    default: 
	    }
	  }
	  if (Dest == null)
	    if (Fic == null)
	      Dest = "."; // par defaut dest = repertoire courant
	    else {
	      Dest = Fic;
	      Fic = null;
	    }
	  
	  if (Command == null)  // erreur
	    log.println(id + "|" +LCourante + "|" + CommandBack +"->syntax error");

	  int i;
	  for(i=0;i<Commands.length;i++) // recherche de la commande 
	    if (Command.toLowerCase().equals(Commands[i]))
	      break;
	  
	  if (i>= Commands.length) // la commande n'est pas reconnue
	    log.println(id + "|" + LCourante + "|" + CommandBack + "->syntax error");

	  switch (i) {
	  case 0: // commande get 
	    Res = commandGet(Source, Fic, Dest);
	    break;
          case 1: // commande put (quasi identique a get, mais non implemente pour un http ...)
	    Res = commandPut(Source, Fic , Dest);
	    break;
	  case 2: // command at
	    Res = commandAt(Source+" " + Fic+" " +Dest);
	    break;
	  default:
	  }
	}
	if (Res)
	  log.println(id + "|" + LCourante + "|" + CommandBack + "->executed");
	else
	  log.println(id + "|" + LCourante + "|" + CommandBack + "->failed");
      }
      
    } catch (IOException e) {
      log.println(id + "|" + LCourante + "|file not found");
      suspend();
    }
  }
}





