import java.awt.*;
import java.applet.*;

/**
 * The ControlPanel contains the buttons etc that allow the user to
 * change the group, current color, etc.
 */
class ControlPanel extends Panel implements Constants {
    Kali kali;
    String[] colorNames = {
      "black",
      "red",
      "yellow",
      "green",
      "lightBlue",
      "Blue"
      };
    Color[] colors = {
      Color.black,
      Color.red,
      Color.yellow,
      Color.green,
      new Color(173, 216, 230),	// light blue
      Color.blue
      };
    int currentColor;
    int currentGroup;
    String wallpaperString  = "Wallpaper Groups";
    String friezeString  = "Frieze Groups";
    String rosetteString  = "Rosette Groups";
    DrawPanel drawPanel;
    Panorama panorama;
    ImageButton[] groupButton = new ImageButton[SymmetryGroups.getCount()];
    ImageButton[] colorButton = new ImageButton[6];
    Panel groupPanel;
    Panel wallpaperPanel;
    Panel friezePanel;
    Panel rosettePanel;
    GridBagLayout gridbag = new GridBagLayout();

    public void setGroupType(int i) {
      groupPanel.removeAll();
      GridBagConstraints c = new GridBagConstraints();
      c.gridwidth = GridBagConstraints.REMAINDER;
      switch (i) {
        case GROUPTYPE_WALLPAPER:
	  gridbag.setConstraints(wallpaperPanel, c);
	  groupPanel.add(wallpaperPanel);
	  break;
        case GROUPTYPE_FRIEZE:
	  gridbag.setConstraints(friezePanel, c);
	  groupPanel.add(friezePanel);
	  break;
        case GROUPTYPE_ROSETTE:
	  gridbag.setConstraints(rosettePanel, c);
	  groupPanel.add(rosettePanel);
	  break;
	}
      groupPanel.validate();
      this.validate();
    }

    /**
     * Create a new control panel object.
     * @param	kali		The Kali object for this applet
     * @param	drawPanel	The DrawPanel object for this applet
     * @param	panorama	The Panorama object for this applet
     */
    public ControlPanel(Kali kali, DrawPanel drawPanel, Panorama panorama) {
	this.kali = kali;
	this.drawPanel = drawPanel;
	this.panorama = panorama;
	setBackground(Color.lightGray);
	int buttonRowCount = 7;
	int buttonsPerRow = 6;
	// NOTE: the button bitmaps are stored in a rectangular image
	// with 2*buttonRowCount rows and buttonsPerRow columns.  The
	// first buttonRowCount rows contain the "up" state images,
	// and the second buttonRowCount rows contain the "down" state
	// images, in the same order.  We load this image from the
	// server here, and chop it up into the individual buttons
	// below.
	Image bigimage = kali.getImageNow("buttons.gif");
	Image upImage, downImage;
	int w = 32, h = 32;
	Graphics g;
	int row = 0;
	int col = 0;
	Label label;
	GridBagConstraints c;
	setLayout(gridbag);
	Font groupLabelFont = new Font("TimesRoman",Font.BOLD,18);
	Font buttonLabelFont = new Font("TimesRoman",Font.PLAIN,12);

	//
	// First create all the widgets and subpanels
	//

	//
	//   Create the group buttons; each button gets put into
	//   its own little panel (the bPanel array) together with
	//   its label.  It's in this loop where we chop up the big
	//   image into individual ones for the buttons.
	//
	ImageButtonGroup groupButtons = new ImageButtonGroup();
	Panel[] bPanel = new Panel[groupButton.length];
	c = new GridBagConstraints();
	c.gridwidth = GridBagConstraints.REMAINDER;
	for (int i=0; i<36; ++i) {
	  upImage = kali.createImage(w, h);
	  g = upImage.getGraphics();
	  g.drawImage(bigimage, -col*w, -row*h, kali);
	  downImage = kali.createImage(w, h);
	  g = downImage.getGraphics();
	  g.drawImage(bigimage, -col*w, -(row+buttonRowCount)*h, kali);
	  groupButton[i] = new ImageButton(upImage, downImage, groupButtons);
	  bPanel[i] = new Panel();
	  bPanel[i].setLayout(gridbag);
	  gridbag.setConstraints(groupButton[i], c);
	  bPanel[i].add(groupButton[i]);
	  label = new Label(SymmetryGroups.group[i].name);
	  label.setAlignment(Label.CENTER);
	  label.setFont(buttonLabelFont);
	  gridbag.setConstraints(label, c);
	  bPanel[i].add(label);
	  ++col;
	  if (col >= buttonsPerRow) {
	    col = 0;
	    ++row;
	  }
	}
	currentGroup = 0;
	groupButtons.setCurrent(groupButton[currentGroup]);

	//
	//   Create the color buttons
	//
	ImageButtonGroup colorButtons = new ImageButtonGroup();
	for (int i=0; i<6; ++i) {
	  upImage = kali.createImage(w, h);
	  g = upImage.getGraphics();
	  g.drawImage(bigimage, -col*w, -row*h, kali);
	  downImage = kali.createImage(w, h);
	  g = downImage.getGraphics();
	  g.drawImage(bigimage, -col*w, -(row+buttonRowCount)*h, kali);
	  colorButton[i] = new ImageButton(upImage, downImage, colorButtons);
	  ++col;
	  if (col >= buttonsPerRow) {
	    col = 0;
	    ++row;
	  }
	}
	currentColor = 0;
	colorButtons.setCurrent(colorButton[currentColor]);
	drawPanel.setForeground(colors[currentColor]);

	//
	//   Create the color panel and layout the color
	//   buttons in it.
	//
	Panel colorPanel = new Panel();
	{
	  colorPanel.setLayout(new GridLayout(1,6));
	  for (int i=0; i<6; ++i) {
	    colorPanel.add(colorButton[i]);
	  }
	}
	Label colorLabel = new Label("Colors");
	colorLabel.setFont(groupLabelFont);

	//
	//   Create the group type chooser widget
	//
	Choice groupChooser = new Choice();
	groupChooser.setFont(groupLabelFont);
	groupChooser.addItem(wallpaperString);
	groupChooser.addItem(friezeString);
	groupChooser.addItem(rosetteString);

	//
	//   Create the wallpaper button panel and layout
	//   its buttons.
	//
	c = new GridBagConstraints();
	wallpaperPanel = new Panel();
	{
	  Panel first16 = new Panel();
	  first16.setLayout(new GridLayout(4, 4));
	  for (int i=0; i<16; ++i) {
	    first16.add(bPanel[i]);
	  }
	  c.gridwidth = GridBagConstraints.REMAINDER;
	  wallpaperPanel.setLayout(gridbag);
	  gridbag.setConstraints(first16, c);
	  wallpaperPanel.add(first16);
	  gridbag.setConstraints(bPanel[16], c);
	  wallpaperPanel.add(bPanel[16]);	// 17th button on last row by itself
	}

	//
	//   Create the frieze button panel and layout
	//   its buttons.
	//
	c = new GridBagConstraints();
	friezePanel = new Panel();
	{
	  Panel first6 = new Panel();
	  first6.setLayout(new GridLayout(2, 3));
	  for (int i=17; i<23; ++i) {
	    first6.add(bPanel[i]);
	  }
	  c.gridwidth = GridBagConstraints.REMAINDER;
	  friezePanel.setLayout(gridbag);
	  gridbag.setConstraints(first6, c);
	  friezePanel.add(first6);
	  gridbag.setConstraints(bPanel[23], c);
	  friezePanel.add(bPanel[23]);	// 7th button on last row by itself
	}
	friezePanel.invalidate();
	friezePanel.validate();

	//
	//   Create the rosette button panel and layout
	//   its buttons.
	//
	rosettePanel = new Panel();
	{
	  rosettePanel.setLayout(new GridLayout(2, 6));
	  for (int i=24; i<36; ++i) {
	    rosettePanel.add(bPanel[i]);
	  }
	}

	//
	//   Create the general group button panel; this is a
	//   placeholder panel into which we insert one of the actual
	//   group panels (wallpaper, frieze, or rosette) later.
	//
	groupPanel = new GrowPanel();

	//
	// Now layout the above items in the control panel
	//

	//   color label:
	c = new GridBagConstraints();
	c.gridwidth = GridBagConstraints.REMAINDER;
	c.insets = new Insets(0,5,0,5);	// top,left,bottom,right
	gridbag.setConstraints(colorLabel, c);
	add(colorLabel);

	//   color panel
	gridbag.setConstraints(colorPanel, c);
	add(colorPanel);

	//   group chooser widget
	Insets oldInsets = c.insets;
	c.insets = new Insets(40,5,15,5);	// top,left,bottom,right
	gridbag.setConstraints(groupChooser, c);
	add(groupChooser);
	c.insets = oldInsets;

	//   group panel
	gridbag.setConstraints(groupPanel, c);
	add(groupPanel);

	//   padding panel --- a panel with nothing in it, whose sole
	//     purpose is to take up the extra space at the bottom
	Panel padPanel = new Panel();
	c.gridheight = GridBagConstraints.REMAINDER;
	c.weighty = 1.0;
	c.weightx = 1.0;
	c.fill = GridBagConstraints.BOTH;
	gridbag.setConstraints(padPanel, c);
	add(padPanel);

	//
	// And finally, set the initial group type
	//
	setGroupType(GROUPTYPE_WALLPAPER);
    }

//    public void paint(Graphics g) {
//      Rectangle r = bounds();
//      g.setColor(Color.lightGray);
//      g.draw3DRect(0, 0, r.width, r.height, true);
//    }


    /**
     * Given a ImageButton id, return the group index for the
     * corresponding group.  (This makes use of the fact that the
     * group indices are consecutive integers starting with 0, in the
     * same order as the buttons.)
     */
    private int findGroup(ImageButton button) {
      for (int i=0; i<groupButton.length; ++i) {
	if (button == groupButton[i]) {
	  return i;
	}
      }
      return -1;
    }

    /**
     * Given a color button, return its index.
     */
    public int findColor(ImageButton button) {
      for (int i=0; i<colorNames.length; ++i) {
	if (button == colorButton[i]) {
	  return i;
	}
      }
      return -1;
    }

    /**
     * Handling method for button actions.
     */
    public boolean action(Event e, Object arg) {
	if (e.target instanceof Checkbox) {
	  // target.setForeground(((Component)e.target).getBackground());
	} else if (e.target instanceof Choice) {
	  String choice = (String)arg;
	  if (choice.equals(wallpaperString)) {
	    setGroupType(GROUPTYPE_WALLPAPER);
	  } else if (choice.equals(friezeString)) {
	    setGroupType(GROUPTYPE_FRIEZE);
	  } else if (choice.equals(rosetteString)) {
	    setGroupType(GROUPTYPE_ROSETTE);
	  }
	} else if (e.target instanceof ImageButton) {
	  ImageButton button = (ImageButton)e.target;
	  int i = findGroup(button);
	  if (i>=0) {
	    currentGroup = i;
	    panorama.setGroup(i);
	    drawPanel.clear();
	    drawPanel.repaint();
	  } else {
	    i = findColor(button);
	    if (i>=0) {
	      currentColor = i;
	      drawPanel.setForeground(colors[currentColor]);
	    } else {
	      System.out.println("unknown image button pressed");
	    }
	  }
	}
	return true;
    }


}

/**
 * The GrowPanel object is somewhat of a kludge (OK, it's totally a
 * kludge).  The idea is that I want to force the groupPanel, which is
 * the subpanel of the ControlPanel which contains the group buttons,
 * to have a fixed size, so that it doesn't change size (and hence
 * cause the entire ControlPanel's layout to change) when the
 * particular group button panel (wallpaper/frieze/rosette) that it
 * contains changes.  So, in theory, I'd like a way of telling the
 * layout manager to force the groupPanel to have a certain size, and
 * that size should be computed at run-time to be the max size of the
 * 3 group button panels.  In practice, however, I cannot figure out
 * how to compute the actual sizes of these (or any) panels until they
 * are actually painted to the screen.  SO, the kludge is to use a
 * Panel whose size (as returned by its preferredSize() and
 * minimumSize() methods) is always the max of its previous size and
 * its current size.  Then, as long as we start out with the largest
 * of the 3 possibilities (wallpaper), things should be OK.
 *
 * <p>
 * If you know of a better way to do this
 * <a href="http://www.geom.umn.edu/admin/bin/make-mailform.cgi?to=mbp@geom.umn.edu">
 * let me know</a>!
 */
class GrowPanel extends Panel {
  /**
   * This holds the previous size.
   */
  Dimension size = new Dimension(0,0);

  public Dimension preferredSize() {
    // We call the superclass preferredSize() method to compute the
    // "current" size.
    Dimension s = super.preferredSize();
    size.width = Math.max(size.width,s.width);
    size.height = Math.max(size.height,s.height);
    return size;
  }
  public Dimension minimumSize() {
    return preferredSize();
  }
}
