/// Evolver Copyright 2000, Morgan McGuire
/// matrix@alum.mit.edu   http://graphics3d.com/matrix
import javax.swing.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;

public class AnimationPlayer extends JPanel
{
  private static final GeneticParameters geneticParameters = new GeneticParameters(".");
  private static final String STOPPED = "STOPPED";
  private static final String PLAYING = "PLAYING";

  private Animation animation;

  private ELMatrix display;
  private JLabel displayHolder;

  // Time when playing started, in milliseconds.
  private long time0;
  private String state = STOPPED;
  private ELEnvironment environment;
  
  private File file;

  // To stop the render thread, set the state to something other than
  // PLAYING.
  private Thread renderThread;

  static public void main(String arg[])
  {
    if (arg[0].equals("help")) {
      System.out.println("java AnimationPlayer <animation file> [zoom] [resolution]");
      System.exit(0);
    }

    double zoom = 2.0;
    if (arg.length >= 2) {
      zoom = Double.parseDouble(arg[1]);
    }

    int resolution = 128;
    if (arg.length >= 3) {
      resolution = Integer.parseInt(arg[2]);
    }

    System.out.print("Loading animation player...");
    AnimationPlayer player = new AnimationPlayer(resolution, zoom);
    player.load(arg[0]);
    
    JFrame frame = new JFrame();
    frame.getContentPane().add(player);
    frame.pack();
    frame.setVisible(true);
    frame.setIconImage(new ImageIcon("icon.jpg").getImage());
    frame.setTitle(arg[0] + " - Animation Player");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    System.out.print("done");
  }

  public AnimationPlayer(int resolution, double zoom)
  {
    animation = new Animation(geneticParameters);
    display = ELMatrix.alloc(resolution, resolution);
    displayHolder = new JLabel(new ScaleIcon(zoom, display)) {
        public void paintComponent(Graphics g) {
          super.paintComponent(g);
          String s = Animation.formatTime(environment.getTime());
          g.setColor(Color.black);
          g.drawString(s,1,11);
          g.setColor(Color.white);
          g.drawString(s,0,10);
        }
      };
    add(displayHolder);
    environment = new ELEnvironment();

    displayHolder.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent event) {
          if (event.isShiftDown()) {
            stop();
            load(file); // reload
          } else {
            stop();
            play();
            System.out.println("Playing...");
          }
        }
      });
  }

  public void play()
  {
    if (state != PLAYING) {
      if (state == STOPPED) {
        // Reset the timer
        time0 = System.currentTimeMillis();
      }

      state = PLAYING;
      
      renderThread = new Thread() {
          public void run() {
            int hint = 0;
            while (state == PLAYING) {
              double time = (System.currentTimeMillis() - time0) / 1000.0;

              // System.out.println(time);
              
              // Update state
	      environment.clock(time, display);

              int chunk = animation.renderFrame(environment, display, hint);

              // Clean the matrix up on our thread so that cleaning is
              // not out of sync with rendering.
              display.forceClean();

              // Tell swing to redraw
              displayHolder.repaint();

              // TODO: change the expression and time display

              hint = chunk;
              // Yield to give swing a chance to render
              //Thread.yield();
            }
          }
        };

      renderThread.start();
    }
  }

  public void stop()
  {
    state = STOPPED;
    killRenderThread();
  }

  private void killRenderThread()
  {
    if ((renderThread != null) && renderThread.isAlive()) {
      try {
        renderThread.join();
        // Allow GC
        renderThread = null;
      } catch (InterruptedException e) {
        throw new Error(e.toString());
      }
    }
  }

  public void load(String filename) 
  {
    load(new File(filename));
  }

  public void load(File file)
  {
    this.file = file;
    System.out.print("Loading " + file.getName() + "...");

    stop();
    animation.load(file, geneticParameters);

    // Preload the animation sequence so that lazy loading expressions
    // don't stall us.
    int numChunks = animation.numChunks();
    ELMatrix temp = ELMatrix.alloc(8, 8);
    int hint = 0;
    for(int i = 0; i < numChunks; i++) {
      environment.setTime(animation.getStartTime(i));
      hint = animation.renderFrame(environment, temp, hint);
    }

    System.out.println("done");
  }
  
}
