/*
   Source code by David Eck
                  Department of Mathematics and Computer Science
                  Hobart and William Smith Colleges
                  Geneva, NY 14456
                  eck@hws.edu
                  
   This Java source code file can be used IN UNMODIFIED FORM for
   any purpose.  You can also make and distribute modified versions,
   as long as you include an acknowledgement of the original
   author in the modified version.
*/

package xca;

import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.applet.Applet;
import java.net.URL;
import java.util.Date;

public class Menus extends JMenuBar {

   private WorldPane worldPane;
   
   private xMenu fileMenu, controlMenu;
   
   private int currentSpeedIndex;
   
   private NewWorldDialog newWorldDialog;
   
   private JFileChooser fileDialog;
   
   private Frame containingFrame;
   
   private int[][] palettes = new int[33][];

   public Menus(WorldPane w, boolean addFileOperations, Frame containingFrame) {
      this.worldPane = w;
      this.containingFrame = containingFrame;
      fileMenu = makeFileMenu(addFileOperations);
      add(fileMenu);
      controlMenu = makeControlMenu();
      add(controlMenu);
      setSpeed(2);
   }
   
   public void addExamplesMenu(String[] fileList) {
      addExamplesMenu(fileList,null);
   }

   public void addExamplesMenu(final String[] fileList, final Applet appletForParams) {
      xMenu menu = new xMenu("Examples") {
             protected void doCommand(int itemNum) {
                boolean running = worldPane.isRunning();
                worldPane.stop();
                String fileName = fileList[itemNum - 1];
                Reader in;
                try {
                   if (appletForParams == null)
                      in = new BufferedReader(new FileReader(fileName));
                   else {
                      URL url = new URL(appletForParams.getDocumentBase(), fileName);
                      InputStream stream = url.openStream();
                      in = new BufferedReader(new InputStreamReader(stream));
                   }
                }
                catch (Exception e) {
                   JOptionPane.showMessageDialog(worldPane,"Sorry, but an error occurred while trying to open the file:\n" + e);
                   if (running)
                      worldPane.start(100);
                   return;
                }
                try {
                    worldPane.readWorld(in);
                }
                catch (Exception e) {
                   JOptionPane.showMessageDialog(worldPane,"Sorry, but an error occurred while trying to read the data:\n" + e);
                   if (running)
                      worldPane.start(100);
                }
             }
         };
      menu.add(fileList);
      add(menu);
   }
   
   private xMenu makeFileMenu(final boolean addFileOperations) {
      xMenu menu = new xMenu(containingFrame == null ? "Create" : "File") {
         protected void doCommand(int itemNum) {
            switch (itemNum) {
               case 1:  // New
                  doNew();
                  break;
               case 2:  // New with Same Settings
                  doNewSameSettings();
                  break;
               case 4:  // Open or Quit
                  if (addFileOperations)
                     doOpen();
                  else
                     containingFrame.dispose();
                  break;
               case 5:  // Save
                  doSave();
                  break;
               case 6:  // Same Image
                  doSaveImage();
                  break;
               case 8:
                  System.exit(0);
                  break;
            }
         }
      };
      menu.add("New...", "control N");
      menu.add("New with Same Settings", "control shift N");
      if (addFileOperations) {
         menu.addSeparator();
         menu.add("Open...", "control O");
         menu.add("Save...", "control S");
         menu.add("Save Image...", "control I");
      }
      if (containingFrame != null) {
         menu.addSeparator();
         if (addFileOperations)
            menu.add("Quit", "control Q");
         else
            menu.add("Close", "control Q");
      }
      return menu;
   }
   
   private xMenu makeControlMenu() {
      xMenu menu = new xMenu("Control") {
         public void update() {
            if (worldPane.isRunning()) {
               setItemName(1,"Pause");
               disableItem(2);
            }
            else {
               setItemName(1,"Go");
               enableItem(2);
            }
         }
         protected void doCommand(int itemNum) {
            switch (itemNum) {
               case 1:
                  if (worldPane.isRunning())
                     worldPane.stop();
                  else
                     worldPane.start(0);
                  break;
               case 2:
                  if (!worldPane.isRunning()) {
                     worldPane.next();
                     worldPane.paintImmediately(0,0,worldPane.getWidth(),worldPane.getHeight());
                  }
                  break;
               case 4:
                  setSpeed(1);
                  break;
               case 5:
                  setSpeed(2);
                  break;
               case 6:
                  setSpeed(3);
                  break;
               case 7:
                  setSpeed(4);
                  break;
               case 8:
                  setSpeed(5);
                  break;
               case 10:
                  worldPane.restart();
                  break;
               case 11:
                  worldPane.firstImage();
                  break;
               case 12:
                  worldPane.restartRandom();
                  break;
               case 13:
                  worldPane.restartRandomClump();
                  break;
               case 14:
                  worldPane.restartSingleDot();
                  break;
               case 16:
                  doColors();
                  break;
            }
         }
      };
      menu.add("Pause", "control P");
      menu.add("Next Generation", "control G");
      menu.disableItem(2);
      menu.addSeparator();
      menu.addRadioGroup( new String[] { "Very Fast", "Fast", "Moderate Speed",
               "Slow", "Very Slow" }, 1 );
      ((JMenuItem)menu.getMenuComponent(3)).setAccelerator(KeyStroke.getKeyStroke("control 1"));
      ((JMenuItem)menu.getMenuComponent(4)).setAccelerator(KeyStroke.getKeyStroke("control 2"));
      ((JMenuItem)menu.getMenuComponent(5)).setAccelerator(KeyStroke.getKeyStroke("control 3"));
      ((JMenuItem)menu.getMenuComponent(6)).setAccelerator(KeyStroke.getKeyStroke("control 4"));
      ((JMenuItem)menu.getMenuComponent(7)).setAccelerator(KeyStroke.getKeyStroke("control 5"));
      menu.addSeparator();
      menu.add("Start World Again", "control A");
      menu.add("First Image then Stop", "control F");
      menu.add("New Random World", "control R");
      menu.add("New Random Clump");
      menu.add("Start With One Dot");
      menu.addSeparator();
      menu.add("Colors...", "control C");
      return menu;
   }
   
   private void setSpeed(int x) {
      if (x == currentSpeedIndex)
         return;
      currentSpeedIndex = x;
      controlMenu.selectRadioItem(x+3);
      switch (x) {
         case 1: worldPane.setDelay(1); break;
         case 2: worldPane.setDelay(20); break;
         case 3: worldPane.setDelay(50); break;
         case 4: worldPane.setDelay(200); break;
         case 5: worldPane.setDelay(1000); break;
      }
   }
   
   private void doNew() {
      boolean running = worldPane.isRunning();
      worldPane.stop();
      if (newWorldDialog == null) {
         newWorldDialog = new NewWorldDialog(containingFrame);
      }
      newWorldDialog.show(worldPane.getWidth(),worldPane.getAvailableHeight());
      World newWorld = newWorldDialog.getWorld();
      if (newWorld == null) {
         if (running)
            worldPane.start(300);
         return;
      }
      worldPane.installWorld(newWorld,newWorldDialog.getImageHeight(),
                                         palettes[newWorld.getNumberOfStates()]);
      worldPane.start(300);
   }
   
   private void doNewSameSettings() {
      worldPane.stop();
      World world = worldPane.getWorld();
      Dimension size = worldPane.getCanvasSize();
      World newWorld = new World(
              world.getNeighborhoodSize(),
              world.getNumberOfStates(),
              world.getWorldSize(),
              world.isIsotropic(),
              world.isWrapped()
         );
      worldPane.installWorld(newWorld,size.height, palettes[newWorld.getNumberOfStates()]);
      worldPane.start(300);
   }
   
   private void doOpen() {
      boolean running = worldPane.isRunning();
      worldPane.stop();
      if (fileDialog == null)      
         fileDialog = new JFileChooser();  // Selects home directory.
      fileDialog.setDialogTitle("Read Parameters from File");
      int option = fileDialog.showOpenDialog(worldPane);
      if (option != JFileChooser.APPROVE_OPTION)
         return;  // user canceled
      File selectedFile = fileDialog.getSelectedFile();
      Reader in;
      try {
          in = new BufferedReader(new FileReader(selectedFile));
      }
      catch (Exception e) {
         JOptionPane.showMessageDialog(worldPane,"Sorry, but an error occurred while trying to open the file:\n" + e);
         if (running)
            worldPane.start(100);
         return;
      }
      try {
          worldPane.readWorld(in);
      }
      catch (Exception e) {
         JOptionPane.showMessageDialog(worldPane,"Sorry, but an error occurred while trying to read the data:\n" + e);
         if (running)
            worldPane.start(100);
      }
   }
   
  
   private void doSave() {
      boolean running = worldPane.isRunning();
      worldPane.stop();
      if (fileDialog == null)      
         fileDialog = new JFileChooser();  // Selects home directory.
      File selectedFile = new File("ca_params.txt");
      fileDialog.setSelectedFile(selectedFile);  // Go to directory from last save image operation
      fileDialog.setDialogTitle("Save Parameters for this Example");
      int option = fileDialog.showSaveDialog(worldPane);
      if (option != JFileChooser.APPROVE_OPTION)
         return;  // user canceled
      selectedFile = fileDialog.getSelectedFile();
      if (selectedFile.exists()) {
         int response = JOptionPane.showConfirmDialog(worldPane,"The file \"" + selectedFile.getName()
                                 + "\" already exists.\nDo you want to replace it?", "Confirm Save",
                                 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
         if (response == JOptionPane.NO_OPTION)
            return;
      }
      try {
          PrintWriter out = new PrintWriter(new FileWriter(selectedFile));
          World world = worldPane.getWorld();
          out.println("# Parameters for EdgeOfChaosCA, created " + (new Date()));
          out.println("RandomSeed " + world.getRandomSeed());
          out.println("NumberOfStates " + world.getNumberOfStates());
          out.println("NeighborhoodSize " + world.getNeighborhoodSize());
          out.println("Isotropic " + world.isIsotropic());
          out.println("CircularWorld " + world.isWrapped());
          out.println("WorldSize " + world.getWorldSize());
          out.println("ImageHeight " + worldPane.getCanvasSize().height);
          out.println("RulesUsed " + world.getRulesUsed());
          int[] palette = worldPane.getPalette();
          out.print("Palette ");
          for (int i = 0; i < palette.length; i++) {
             if (i % 6 == 5) {
                out.println();
                out.print("   ");
             }
             String s = Integer.toHexString(palette[i] & 0xFFFFFF);
             while (s.length() < 6)
                s = "0" + s;
             s = s.toUpperCase();
             s = "0x" + s;
             out.print(s);
             if (i < palette.length-1)
                out.print(",");
          }
          out.println();
          out.print("InitialWorld ");
          int[] initialWorld = worldPane.getInitialWorld();
          for (int i = 0; i < initialWorld.length; i++) {
             if (i % 25 == 20) {
                out.println();
                out.print("   ");
             }
             out.print(initialWorld[i]);
             if (i < initialWorld.length-1)
                out.print(",");
          }
          out.println();
          out.close();
          if (out.checkError())
             throw new Exception("File was opened successfulley, but an error occurred during output.");
      }
      catch (Exception e) {
          JOptionPane.showMessageDialog(worldPane,"Sorry, but an error occurred while trying to save the data:\n" + e);
      }
      if (running)
         worldPane.start(100);
   }
   
   private void doSaveImage() {
      boolean running = worldPane.isRunning();
      worldPane.stop();
      if (fileDialog == null)      
         fileDialog = new JFileChooser();  // Selects home directory.
      File selectedFile = new File("image.png");
      fileDialog.setSelectedFile(selectedFile);  // Go to directory from last save image operation
      fileDialog.setDialogTitle("Save Image");
      int option = fileDialog.showSaveDialog(worldPane);
      if (option != JFileChooser.APPROVE_OPTION)
         return;  // user canceled
      selectedFile = fileDialog.getSelectedFile();
      if (selectedFile.exists()) {
         int response = JOptionPane.showConfirmDialog(worldPane,"The file \"" + selectedFile.getName()
                                 + "\" already exists.\nDo you want to replace it?", "Confirm Save",
                                 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
         if (response == JOptionPane.NO_OPTION)
            return;
      }
      try {
         if ( ! ImageIO.write(worldPane.getImage(),"PNG",selectedFile) )
            JOptionPane.showMessageDialog(worldPane,"Sorry, but the PNG image format is not available\n" 
                                                            + "on this system.  Image cannot be saved.");
      }
      catch (Exception e) {
          JOptionPane.showMessageDialog(worldPane,"Sorry, but an error occurred while trying to save the image:\n" + e);
      }
      if (running)
         worldPane.start(100);
   }
   
   private void doColors() {
      boolean running = worldPane.isRunning();
      worldPane.stop();
      ColorDialog cd = new ColorDialog(containingFrame,worldPane.getPalette());
      int[] newPalette = cd.getPalette();
      if (newPalette != null) {
         palettes[newPalette.length] = newPalette;
         worldPane.setPalette(newPalette);
      }
      if (running)
         worldPane.start(100);
   }

}

