import java.awt.*;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.IOException;

/**
 * class OperationPanel
 * @version        0.1 Feb 1997 
 * @author         Ebele Nicolas, Mallet Frederic
 * Date : 26/03/97  21:18
 */
class OperationPanel extends Panel {
  private static final int OPERATION_VIEW = 0, TRASH_VIEW = 1, RESULT_VIEW = 2;
  private static String ClassName = "OperationPanel";
  private Button b_exe, b_rm, b_undel;
  public OperationList ol;
  private List l_trash, l_op, l_res;
  private Frame top;
  private boolean TrashEmpty = true;
  private int View = OPERATION_VIEW;
  private String title = "Operations", ExeFile=".exe";
  private Color BackGround = Color.white;
  private String LogFileName = "Log";
  private int width, height;
  private String Trash = "trash", Ext = ".jpg", ImPath="im", Desk = "desk.jpg";
  private int numImage = 4, Delay = 100;
  private TrashAnimator ta;
  private int nextNumberFile = 0;
  private PrintStream Log = null;
  private Image IDesk;

/**
 * Constructeur
 * @param parent Frame principale contenant l'application
 */
  public OperationPanel(Frame parent) {
    Init.println(ClassName, "constructeur");
    top = parent;
    Initialise();
    setBackground(BackGround);
    setLayout(null);
    IDesk = Toolkit.getDefaultToolkit().getImage(ImPath+File.separator+Desk);
    l_trash = new List();
    add(ol = new OperationList());
    add(b_exe = new Button("Execute"));
    add(b_rm = new Button("Remove"));
    add(b_undel = new Button("Undelete"));
    add(ta = new TrashAnimator(ImPath+File.separator+Trash, Ext, numImage, Delay));
  }

/**
 * Lecture des parametres d'initialisation
 */
  public void Initialise() {
    Init.println(ClassName, "init");
    String param;

    param = (String)Init.get(ClassName, "Color", "BackGround");
    BackGround = param==null?BackGround:Init.getColor(param);

    param = (String)Init.get(ClassName, "Icones", "Trash");
    Trash = param==null?Trash:param;

    param = (String)Init.get(ClassName, "Icones", "Extension");
    Ext = param==null?Ext:("."+param);

    param = (String)Init.get(ClassName, "Icones", "Number Image");
    numImage = param==null?numImage:Integer.parseInt(param);

    param = (String)Init.get(ClassName, "Icones", "Delay");
    Delay = param==null?Delay:Integer.parseInt(param);

    param = (String)Init.get(ClassName, "Icones", "Image Path");
    ImPath = param==null?ImPath:param;

    param = (String)Init.get(ClassName, "Icones", "Desk");
    Desk = param==null?Desk:param;

    param = (String)Init.get(ClassName, "Layout", "Title");
    title = param==null?title:param;

    param = (String)Init.get(ClassName, "Report", "LogFileName");
    LogFileName = param==null?LogFileName:param;

    param = (String)Init.get(ClassName, "Report", "ExeFile");
    ExeFile = param==null?ExeFile:param;

    Init.println(ClassName, "initialized successfully");
  }

/**
 * Recuperation des evenements
 */
  public boolean handleEvent(Event ev) {
    if (ev.id == Event.ACTION_EVENT)
      if (ev.target == b_exe)        // Bouton execute
        Execute(ol);
      else if (ev.target == b_rm)    // Bouton Remove
        RemoveEntry();
      else if (ev.target == b_undel) // Bouton undelete
        Undelete();
    return super.handleEvent(ev);	
  }

// Construit le fichier contenant les executions a effectuer
  private void Execute(OperationList ol) {
    int id = nextNumberFile++;
    Init.println(ClassName, "opening:"+ExeFile+id);
    try {
      FileOutputStream fos = new FileOutputStream(ExeFile+id);
      PrintStream ps = new PrintStream(fos);
      for (int i=0; i<ol.countItems(); i++)
        ps.println(ol.getItem(i));
// Si le fichier de log n'existe pas, on le cree
      if (Log == null) {
        fos = new FileOutputStream(LogFileName);
        Log = new PrintStream(fos);
      }
      new executeThread(id, ExeFile+id, Log).start(); 
    } catch (IOException ioe) {
      System.out.println("Erreur opening execute file.");
    }
  }

/**
 * Affiche les resultats des operations lancees 
 */
  public void Results() {
    Init.println(ClassName, "results");
    if (View == RESULT_VIEW)
      return;
    String configFile [] = new String [nextNumberFile];
    for (int i=0; i<nextNumberFile; i++)
      configFile[i] = ExeFile+i;
    l_res = new List();  
    Hashtable res = ((new Diff(LogFileName, configFile)).makeDiff());
    for (Enumeration en = res.keys(); en.hasMoreElements(); ) {
      Object tmp = en.nextElement();
      if (tmp instanceof String) {
        l_res.addItem(tmp+":"+res.get(tmp));
        continue;
      }
      l_res.addItem("AT "+tmp);
      Vector actions = (Vector)res.get(tmp);
      for (Enumeration e = actions.elements(); e.hasMoreElements(); )
        l_res.addItem(e.nextElement().toString());
    }
    if (View == TRASH_VIEW)
      l_trash = ol.swapList(l_res);
    else
      l_op = ol.swapList(l_res);
    View = RESULT_VIEW;
    repaint();
  }

/**
 * Gestion du click de souris
 */
  public boolean mouseDown(Event ev, int x, int y) {
// Si on click sur la poubelle
    if ((View != RESULT_VIEW || !TrashEmpty) &&
      x>width-130 && y>height-100 && x<width-70 && y<height-50) {
      if (View == TRASH_VIEW) {
        l_trash = swapList(l_op);
        View = OPERATION_VIEW;
      } else {
        l_op = swapList(l_trash);
        View = TRASH_VIEW;
        ta.t.resume();
      }
      repaint();
    }
    if (View == RESULT_VIEW && x>width-130 && x<width-100 && y>0 && y<32) {
      l_res = swapList(l_op);
      View = OPERATION_VIEW;
      repaint();
    }
    return super.mouseDown(ev, x, y);
  }

// Enleve une action a effectuee dans la liste des actions
  private void RemoveEntry() {
    int ind = ol.getSelectedIndex();
    if (ind != -1)
      switch (View) {
      case TRASH_VIEW : 
        Init.println(ClassName, "DeleteEntry:"+ol.getSelectedItem());
        ol.delItem(ind);
        TrashEmpty = (ol.countItems()==0);
        repaint();
        break;
      case OPERATION_VIEW:
        Init.println(ClassName, "RemoveEntry:"+ol.getSelectedItem());
        l_trash.addItem(ol.getSelectedItem());
        ol.delItem(ind);
        TrashEmpty = false;
        repaint();
        break;
      default:
        System.out.println("This message should never be shown.");
      }
  }

// Remet dans la liste des operations a effectuee une operation
// qui avait ete effacee
  private void Undelete() {
    int ind = ol.getSelectedIndex();
    if (ind != -1) {
      Init.println(ClassName, "Undelete:"+ol.getSelectedItem());
      l_op.addItem(ol.getSelectedItem());
      ol.delItem(ind);
      TrashEmpty = (ol.countItems()==0);
      repaint();
    }
  }

/**
 * Ajoute une ligne contenant la date des operations a effectuer
 * @at Date a laquelle les operations devront etre effectuees
 */
  public void newDate(Date at) {
// cherche la place ou la date va etre inseree
    if (ol.searchPlace(at) == -1)
      return;     // cette date existe deja, il ne faut en rajouter une

// Construit la ligne representant la Date
    String date = "", tmp;
    StringTokenizer st = new StringTokenizer(at.toString());
    int pos = 0;

// On enleve de la date tout ce qui nous sert pas.
    for (int ct = st.countTokens(); st.hasMoreTokens(); ct--, pos++) {
      tmp = st.nextToken();
      Init.println(ClassName, tmp+"|"+ct+"|"+pos);
      date += ((pos>3 && ct>1)?"":tmp)+" ";
    }
    if (View == OPERATION_VIEW) {
      ol.addItem("AT "+date);
      repaint();
    } else
      l_op.addItem("AT "+date);
  }

/**
 * Ajoute une operation a effectuer
 * @param from url source (avec le nom du fichier)
 * @param to url destination 
 * @param op type de l'operation (Put ou Get)
 */
  public void addOperation(String from, String to, String op) {
    if (View == OPERATION_VIEW) {
      ol.addOperation(Passwd.encryptUrl(from), Passwd.encryptUrl(to), op);
      repaint();
    } else
      l_op.addItem(op.toUpperCase()+" "+Passwd.encryptUrl(from)+" "+Passwd.encryptUrl(to));
  }

/**
 * Change la liste affichee
 * @param l nouvelle liste
 * @return ancienne liste
 */
  public List swapList(List l) {
    List tmp = ol.swapList(l);
    repaint();
    return tmp;
  }

  public Dimension preferredSize() {
    Dimension tmp = top.size();
    return new Dimension(tmp.width, tmp.height/2-50);  
  }

  public void paint(Graphics g) {
    update(g);
  }

  public void update(Graphics g) {
    Dimension tmp = size();
    Font normal = g.getFont();
    Font bold = new Font(normal.getName(), Font.BOLD, normal.getSize());
    height = tmp.height;
    width = tmp.width;

// Affiche le titre
    g.setColor(BackGround);
    FontMetrics fm = g.getFontMetrics(bold);
    g.fillRect(10, 0, width-20, 18);
    g.fillRect(width-130, 0, 32, 32); // Efface l'image Desk
    g.setFont(bold);
    g.setColor(Color.black);

// Mise en place de la liste contenant le nom des fichiers
    ol.move(20, 20);
    ol.resize(width-150, height-110);
    ol.repaint();

// Mise en place de la poubelle
    ta.move(width-130, height-100);
    ta.resize(125, 48);

// Mise en place du bouton undelete
    b_undel.move(width/2-190, height-80);
    b_undel.resize(70, 30);

// Mise en place du bouton Execute
    b_exe.move(width/2-190, height-80);
    b_exe.resize(70, 30);

// Mise en place du bouton Remove
    b_rm.move(width/2+20, height-80);
    b_rm.resize(60, 30);

    switch (View) {
    case OPERATION_VIEW:
      g.drawString(title, (width-fm.stringWidth(title))/2, 15);
      ta.view(false, TrashEmpty);
      ta.repaint();
      if (ol.countItems()!=0) {
        b_exe.show();
        b_rm.show();
      } else {
        b_exe.hide();
        b_rm.hide();
      }
      b_undel.hide();
      break;
    case TRASH_VIEW:
      g.drawString("Trash", (width-fm.stringWidth("Trash"))/2, 15);
      ta.view(true, TrashEmpty);
      ta.repaint();
      if (TrashEmpty) {
        b_undel.hide();
        b_rm.hide();
      } else {
        b_undel.show();
        b_rm.show();
      }
      b_exe.hide();
      break;
    case RESULT_VIEW:
      g.drawString("Results", (width-fm.stringWidth("Results"))/2, 15);
      g.drawImage(IDesk, width-130, 0, this);
      b_undel.hide();
      b_rm.hide();
      b_exe.hide();
    default:
      break;
    }
  }
}

/**
 * Class TrashAnimator
 * --
 * Animation de la poubelle
 */
class TrashAnimator extends Canvas implements Runnable {
  public Thread t;
  private Image [] im;    
  private Image ITrash, ITrashFull;
  private int numImage, Delay;
  private boolean TrashView = false, TrashEmpty = false;

/**
 * si Trash="essai", Ext="jpg", ni=11, d=400
 * les images essai.jpg, essai0.jpg, essai1.jpg, ...., essai9.jpg
 * seront animees au rythme de 1 image toutes les 400 ms
 * @param Trash nom du fichier contenant les images
 * @param ext extension de ce fichier
 * @param ni nombre d'images
 * @param d delai entre chaque image
 */
  public TrashAnimator (String Trash, String ext, int ni, int d) {
    t = new Thread(this);
    t.start();
    numImage = ni;
    Delay = d;
    Toolkit tk = Toolkit.getDefaultToolkit();
    ITrash = tk.getImage(Trash+ext);
    im = new Image[numImage];
    for (int i=0; i<numImage; i++) 
      im[i] = tk.getImage(Trash+i+ext);
    ITrashFull = im[0];
  }

/**
 * @param v si true, vue de la poubelle
 * @param empty si true, la poubelle est vide
 */
  public void view(boolean v, boolean empty) {
    TrashView = v;
    TrashEmpty = empty;
  }

  public void run() {
    try {
      while (true) {
        t.suspend();      // on attend d'avoir quelquechose a faire
// puis on affiche chaque image 1 fois pendant Delay ms
        for (int i=0; i<numImage; i++) { 
          ITrashFull = im[i];
          repaint();
          t.sleep(Delay);
        }
        ITrashFull = im[0];
      }
    } catch (InterruptedException ie) {
      System.out.println("Interrupted Exception");
    }
  }

  public void paint(Graphics g) {
    update(g);
  }

  public void update(Graphics g) {
    if (ITrash.getHeight(this)==-1) {
      g.setColor(Color.black);
      g.drawString("trash", 10, 30);
    }
    g.setColor(Color.white);  
    if (TrashEmpty)
      g.fillRect(39, 0, 80, 48);
    g.drawImage(TrashEmpty?ITrash:ITrashFull, 0, 0, TrashEmpty?39:117, 48, this);
  }
}
