import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.Border;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Random;

public class ControlPanel extends JPanel implements ActionListener, ItemListener
{ public static final Border RAISEDBORDER = BorderFactory.createRaisedBevelBorder();
  public static final Border ETCHEDBORDER = BorderFactory.createEtchedBorder();
  public static final Color DIALOG_BACKGROUND = new Color(238,238,238); 
  public static final NumberFormat floatNumFormat = new DecimalFormat("#,###,##0.00");
  
  private GeneticImageFrame frame;

   private JToggleButton but_pause;
   private JButton but_next, but_undo, but_reset;
   private JLabel label_fitnessValue;
   
   private JCheckBox cbox_animate;
   private JSlider slider_speed;
   
   public JComboBox  choice_sourceImageList;
   private static final String[] CHOICE_SOURCE_IMAGE_LIST =
   { "MonaLisa-512x413.png", "MonaLisa-soft.png", 
     "PoppyFields-512x384.png", "PoppyFields-soft.png",
     "GreatWaveOffKanagawa-512x352.png", "GreatWaveOffKanagawa-soft.png"
   };
   
   
   public ControlPanel(GeneticImageFrame frame)
   { this.frame = frame;
     int panelWidth = 1000;
     int panelHeight = 70;
     this.setSize(panelWidth, panelHeight);
     
     //I always set the Layout to null because I do not like the auto layouts.
     //However, if you set the layout to null, then you MUST call .setBounds()
     //on EVERY button, label and other component you add. If you do not, the
     //the component's default width and height is zero, which is hard to see.
     this.setLayout(null);
     
     choice_sourceImageList = addComboBox(CHOICE_SOURCE_IMAGE_LIST);
   
     but_reset    = addButton("Reset");
     but_pause    = addToggleButton("Start");
     but_next    = addButton("next");
     but_undo    = addButton("undo");
     but_pause.setSelected(true);
     
     cbox_animate = this.addCheckBox("Animate", false);
     
     JLabel label_fitness = addLabel("Fitness:");
     label_fitnessValue = addLabel("0");
     label_fitnessValue.setHorizontalAlignment(JLabel.RIGHT);
     label_fitnessValue.setBorder(ETCHEDBORDER);
     

     Font defaultFont = new Font("SansSerif", Font.BOLD, 14);
     FontMetrics fm = this.getFontMetrics(defaultFont);
     int fontWidth = fm.stringWidth("X");
     int fontHeight = fm.getHeight();
     
     int edge = 5;
     int boxH  = fontHeight+8;

     int butWidth = fontWidth*10;
     int row1 =  edge;
     
     int choiceWidth = fontWidth*30;
     int col2 = edge + choiceWidth+ edge;
     int col3 = col2 + butWidth + edge;
     int col4 = col3 + butWidth + edge;
     int col5 = col4 + butWidth + edge;
     int col6 = col5 + butWidth + edge;
     
     choice_sourceImageList.setBounds(edge, row1, choiceWidth, boxH);
     but_reset.setBounds(col2,   row1, butWidth, boxH);
     but_pause.setBounds(col3,   row1, butWidth, boxH);
     but_undo.setBounds(col4,   row1, butWidth, boxH);
     but_next.setBounds(col5,   row1, butWidth, boxH);
     cbox_animate.setBounds(col6,   row1, butWidth, boxH);
     
     label_fitness.setBounds(panelWidth-fontWidth*22,   row1, butWidth, boxH);
     label_fitnessValue.setBounds(panelWidth-fontWidth*15,   row1, butWidth, boxH);
     
     int row2 =  row1 + boxH + edge;
     JLabel label_slow = addLabel("slow");
     JLabel label_fast = addLabel("fast");
     
     slider_speed = addSlider(0, 100);
     
     int slowWidth = fontWidth*5; 
     int sliderLeft = slowWidth;
     int fastLeft = panelWidth - edge - slowWidth;
     int sliderWidth = (fastLeft - sliderLeft) - edge;
     label_slow.setBounds(edge, row2, slowWidth, boxH);
     label_fast.setBounds(fastLeft, row2, slowWidth, boxH);
     
     slider_speed.setBounds(sliderLeft, row2, sliderWidth, boxH);
   }
    
   
   public void setFitness(double fitness)
   { String str = floatNumFormat.format(fitness)+" ";
     label_fitnessValue.setText(str);
   }
   
   
   private JCheckBox addCheckBox(String str, boolean state)
   { JCheckBox id = new JCheckBox(str, state);
     this.add(id);
     id.setForeground(Color.BLACK);
     id.setBackground(DIALOG_BACKGROUND);
     id.addItemListener(this);
     return id;
  }
   
   private JComboBox addComboBox(String[] strArray)
   { JComboBox comboBox;
     if (strArray == null) comboBox = new JComboBox();
     else comboBox = new JComboBox(strArray);
     comboBox.setBackground(DIALOG_BACKGROUND);
     comboBox.setForeground(Color.BLACK);
     this.add(comboBox);
     comboBox.addActionListener(this);
     return comboBox;
   }
   
   

   private JLabel addLabel(String str)
   { return addLabel(str, Label.RIGHT);
   }
   
   private JLabel addLabel(String str, int align)
   { JLabel id = new JLabel(str, align);
     this.add(id);
     //id.setFont(fontNormal);
     id.setBackground(DIALOG_BACKGROUND);
     id.setForeground(Color.BLACK);
     return id;
   }


   private JButton addButton(String str)
   { JButton id = new JButton(str);
     this.add(id);
     id.setBackground(DIALOG_BACKGROUND);
     id.setForeground(Color.BLACK);
     id.setBorder(RAISEDBORDER);
     id.addActionListener(this);
     return id;
  }
   
   private JToggleButton addToggleButton(String str)
   { JToggleButton id = new JToggleButton(str);
     this.add(id);
     id.setBackground(DIALOG_BACKGROUND);
     id.setForeground(Color.BLACK);
     id.setBorder(RAISEDBORDER);
     id.addActionListener(this);
     return id;
  }
   
   public JSlider addSlider(int min, int max)
   { JSlider mySlider = new JSlider(JSlider.HORIZONTAL, min, max, max);
     this.add(mySlider);
     mySlider.setBackground(DIALOG_BACKGROUND);
     mySlider.setForeground(Color.BLACK);
     return mySlider;
   }
   
   public int getMilliSecDelay()
   { //Returns a delay in the range [0, 5000] milliseconds.
     //Goal:
     // Slider Value    millisec delay
     //      0               10
     //     10              150
     //    100             5000
     double x = 100 - slider_speed.getValue();
     int delay = (int)((359.0/900.0)*x*x + (901.0/90.0)*x +10.0);
     
     return delay;
   }
   
   
   public void actionPerformed(ActionEvent evt)
   { Object source = evt.getSource();
     if (source == choice_sourceImageList)
     { but_pause.setSelected(true);
       int idx = choice_sourceImageList.getSelectedIndex();
       BufferedImage sourceImage = GeneticImagePanel.loadImage(CHOICE_SOURCE_IMAGE_LIST[idx], this);
       frame.setSourceImage(sourceImage);
     }
     else if( source == but_reset)
     { frame.reset();
       but_pause.setSelected(true);
     }
     else if( source == but_next) frame.nextGeneration();
     else if( source == but_undo) frame.lastGeneration();

     //This is not in an else if because pause can be set by
     //but_reset and by choosing a new image.
     if (but_pause.isSelected()) 
     { but_pause.setText("Start");
       but_next.setEnabled(true);
       but_undo.setEnabled(true);
       frame.pause();
     }
     else 
     { but_pause.setText("Pause");
       but_next.setEnabled(false);
       but_undo.setEnabled(false);
       frame.start();
     }
   }
   
   public void itemStateChanged(ItemEvent e) 
   { Object source = e.getItemSelectable();
     if (source == cbox_animate)
     { frame.setAnimate(cbox_animate.isSelected());
     }
   }
    
}
