package nxm.sys.intr;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;

import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.MidasException;
import nxm.sys.lib.StringUtil;
import nxm.sys.libg.Theme;


/** A tool for changing the look and feel for all Swing components used by the system.

    @author  J. Czechowski IV
    @since   NeXtMidas 2.1.0 (Moved from UCL)
 */
@SuppressWarnings({"rawtypes","unchecked"}) // <-- NOTE: JDK7 has some rawtypes & unchecked Xlint warnings that CANNOT be fixed since we still support JDK6
public final class Lookandfeel extends Intrinsic {
  /** The status of the primitive. */
  private int currentStatus;

  private PlafChooser plafChooser;

  /** default constructor */
  public Lookandfeel () {}

  public int open () {
    currentStatus = NOOP;
    final String lookAndFeel = MA.getS("LAF", "").trim();

    if (MA.getState("/GUI")) {
      pollTime = args.getD("/POLL", 0.25); // default to a quarter second poll time, since nothing is done in process()
      Theme.initialize(); // 2011-03-23 changes to Theme class requires us to call this first to get the correct initial theme for the GUI
      plafChooser = new PlafChooser();
      plafChooser.setVisible(true);
      //(new PlafChooser()).setVisible(true);
    }
    else if (lookAndFeel.trim().length() == 0) {
      String[] themeNames = Theme.getThemeNames();
      M.type("Current Theme:     "+Theme.getGlobalTheme());
      M.type("Available Themes:");
      for (String themeName : themeNames) {
        String description = Theme.getTheme(themeName).getDescr();
        M.type("  " + StringUtil.padRight(themeName,10) + " - " + description);
      }
      return FINISH;
    }
    else {
      setLookAndFeel(lookAndFeel);
      return FINISH;
    }

    return NORMAL;
  }

  public int process () {
    return currentStatus;
  }

  /** Sets the look and feel.
      @param name The name of the look and feel.
  */
  private void setLookAndFeel (String name) {
    try {
      Theme.setGlobalTheme(name);
      if (MA.getState("/GUI")) {
        plafChooser.setThemeColorPanel();
      }
    }
    catch (IllegalArgumentException e) {
       throw e;
    }
    catch (UnsupportedLookAndFeelException e) {
      throw new MidasException("The '" + name + "' Look & Feel is not supported on this platform.");
    }
    catch (Exception e) {
      throw new MidasException("Unable to set Look & Feel to '" + name + "': " + e);
    }
  }


  /****************************************************************************/
  /* GUI SELECTOR - Most of this was auto-generated.                          */
  /****************************************************************************/

  /** The GUI used for choosing the Look &amp; Feel. Plaf = Pluggable look-and-feel. */
  @SuppressWarnings("serial")
  private class PlafChooser extends JFrame implements ActionListener, WindowListener {

    private ArrayList<String> warnings = new ArrayList<>();

    /** Creates new instance. */
    public PlafChooser () {
      super("Choose a Look & Feel");
      warnings.clear();
      initComponents();
      checkVisibilityTollerance();

      this.lookAndFeels.setModel(new DefaultComboBoxModel(Theme.getThemeNames()));
      this.lookAndFeels.addActionListener(this);
      this.doneBtn.addActionListener(this);
      addWindowListener(this);
    }

    public void actionPerformed (ActionEvent evt) {
      if (evt.getSource() == lookAndFeels) {
        String lookAndFeel = lookAndFeels.getSelectedItem().toString();

        try {
          //Clear Warnings
          warnings.clear();
          twLabel.setText("");

          setLookAndFeel(lookAndFeel);
          checkVisibilityTollerance();
          SwingUtilities.updateComponentTreeUI(this);
          pack();

        } catch(Exception e) {
          final String msg = "Error setting Look & Feel: " + e;
          M.warnStackTrace("Error setting Look & Feel "+lookAndFeel, e);
          JOptionPane.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE);
        }
      } else if (evt.getSource() == this.doneBtn) {
        setVisible(false);
        dispose();
        currentStatus = FINISH;
      }
    }

    public void setThemeColorPanel()
    {
      Theme gblTheme = (Theme) Theme.getTheme(Theme.getGlobalTheme());

      themeColor_cbg.updateColor(gblTheme.cbg);
      themeColor_cfg.updateColor(gblTheme.cfg);
      themeColor_cwbg.updateColor(gblTheme.cwbg);
      themeColor_cwfg.updateColor(gblTheme.cwfg);
      themeColor_cwti.updateColor(gblTheme.cwti);
      themeColor_cwfh.updateColor(gblTheme.cwfh);
      themeColor_cwbh.updateColor(gblTheme.cwbh);
      themeColor_cwts.updateColor(gblTheme.cwts);
      themeColor_cwbs.updateColor(gblTheme.cwbs);
      themeColor_cwms.updateColor(gblTheme.cwms);
      themeColor_cbgx.updateColor(gblTheme.cbgx);
    }

    private void checkVisibilityTollerance() {

      Theme curTheme = (Theme) Theme.getTheme(Theme.getGlobalTheme());
      List<String> themeWarnings = curTheme.warnThemeColorVisibility(M, true);

      Iterator<String> iter = themeWarnings.iterator();
      while (iter.hasNext()) {
        String warning = iter.next();
        addWarning(warning);
      }
    }

    private void addWarning(String newWarning) {

      warnings.add(newWarning);

      StringBuilder sb = new StringBuilder();
      Iterator<String> iter = warnings.iterator();
      while (iter.hasNext()) {
        String warning = iter.next();
        sb.append(warning + "\n");
      }

      if ( twLabel != null) {
        twLabel.setText(sb.toString());
      }
    }

    /** This method is called from within the constructor to initialize the form. */
    private void initComponents() {

      getContentPane().setPreferredSize(new Dimension(400, 620));

      jPanel0 = new javax.swing.JPanel();
      jPanel0.setBounds(new Rectangle(2, 2, 324, 40));
      jLabel1 = new javax.swing.JLabel();
      jLabel1.setBounds(0, 0, 110, 30);

      lookAndFeels = new javax.swing.JComboBox();
      lookAndFeels.setBounds(110, 0, 210, 30);

      samplejPanel = new javax.swing.JPanel();
      samplejPanel.setBounds(10, 50, 380, 120);

      themeColorJPanel = new javax.swing.JPanel();
      themeColorJPanel.setBounds(10, 190, 380, 260);

      warningJPanel = new javax.swing.JPanel();
      warningJPanel.setBounds(10, 460, 380, 120);

      doneBtn = new javax.swing.JButton();
      doneBtn.setBounds(168, 585, 68, 25);

      jButton2 = new javax.swing.JButton();
      jButton2.setBounds(10, 20, 130, 25);
      jButton2.setToolTipText("a tool tip for a text button (JButton)");
      jCheckBox1 = new javax.swing.JCheckBox();
      jCheckBox1.setBounds(170, 20, 103, 23);
      jCheckBox1.setToolTipText("a tool tip for a check box (JCheckBox)");
      jRadioButton1 = new javax.swing.JRadioButton();
      jRadioButton1.setBounds(170, 50, 150, 23);
      jRadioButton1.setToolTipText("a tool tip for a radio button (JRadioButton)");
      jComboBox2 = new javax.swing.JComboBox();
      jComboBox2.setBounds(10, 50, 130, 24);
      jTextField1 = new javax.swing.JTextField();
      jTextField1.setBounds(10, 80, 130, 20);
      jTextField1.setToolTipText("a tool tip for a text field (JTextField)");

      setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
      setResizable(false);
      getContentPane().setLayout(null);
      doneBtn.setText("Done");
      getContentPane().add(doneBtn);
      jPanel0.setLayout(null);

      jLabel1.setText("Look And Feel:");
      jPanel0.add(jLabel1);

      jPanel0.add(lookAndFeels);

      getContentPane().add(jPanel0);

      initSampleJPanel();
      initThemeColorJPanel();

      twLabel = new javax.swing.JTextArea();
      twLabel.setBounds(10 ,20, 360, 90);
      twLabel.setWrapStyleWord(true);
      twLabel.setLineWrap(true);
      twLabel.setText("");
      warningJPanel.setBorder(new javax.swing.border.TitledBorder("Warnings"));
      warningJPanel.setLayout(null);
      warningJPanel.add(twLabel);
      getContentPane().add(warningJPanel);

      pack();
    }

    private void initSampleJPanel() {

      samplejPanel.setBorder(new javax.swing.border.TitledBorder("Sample"));
      samplejPanel.setLayout(null);
      jButton2.setText("A Button");
      samplejPanel.add(jButton2);

      jCheckBox1.setText("A Check Box");

      samplejPanel.add(jCheckBox1);

      jRadioButton1.setText("A Radio Button");
      samplejPanel.add(jRadioButton1);

      jComboBox2.setModel(new javax.swing.DefaultComboBoxModel(
                                               new String[] { "A Combo Box" }));
      samplejPanel.add(jComboBox2);

      jTextField1.setText("A Text Field");
      samplejPanel.add(jTextField1);
      getContentPane().add(samplejPanel);
    }

    private void initThemeColorJPanel() {

      themeColorJPanel.setBorder(new javax.swing.border.TitledBorder("Colors"));
      themeColorJPanel.setLayout(null);

      int tcWidth = 360;

      themeColor_cbg = new ThemeColorTypeJPanel("cbg");
      themeColor_cbg.setBounds(10, 20, tcWidth, 20);
      themeColorJPanel.add(themeColor_cbg);

      themeColor_cfg = new ThemeColorTypeJPanel("cfg");
      themeColor_cfg.setBounds(10, 40, tcWidth, 20);
      themeColorJPanel.add(themeColor_cfg);

      themeColor_cwbg = new ThemeColorTypeJPanel("cwbg");
      themeColor_cwbg.setBounds(10 ,60, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwbg);

      themeColor_cwfg = new ThemeColorTypeJPanel("cwfg");
      themeColor_cwfg.setBounds(10, 80, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwfg);

      themeColor_cwti = new ThemeColorTypeJPanel("cwti");
      themeColor_cwti.setBounds(10, 100, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwti);

      themeColor_cwfh = new ThemeColorTypeJPanel("cwfh");
      themeColor_cwfh.setBounds(10, 120, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwfh);

      themeColor_cwbh = new ThemeColorTypeJPanel("cwbh");
      themeColor_cwbh.setBounds(10, 140, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwbh);

      themeColor_cwts = new ThemeColorTypeJPanel("cwts");
      themeColor_cwts.setBounds(10, 160, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwts);

      themeColor_cwbs = new ThemeColorTypeJPanel("cwbs");
      themeColor_cwbs.setBounds(10, 180, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwbs);

      themeColor_cwms = new ThemeColorTypeJPanel("cwms");
      themeColor_cwms.setBounds(10, 200, tcWidth, 20);
      themeColorJPanel.add(themeColor_cwms);

      themeColor_cbgx = new ThemeColorTypeJPanel("cbgx");
      themeColor_cbgx.setBounds(10, 220, tcWidth, 20);
      themeColorJPanel.add(themeColor_cbgx);

      getContentPane().add(themeColorJPanel);
    }

    public void windowOpened(WindowEvent e) {
      // ignore
    }

    public void windowClosing(WindowEvent e) {
      currentStatus = FINISH;
    }

    public void windowClosed(WindowEvent e) {
      // ignore
    }

    public void windowIconified(WindowEvent e) {
      // ignore
    }

    public void windowDeiconified(WindowEvent e) {
      // ignore
    }

    public void windowActivated(WindowEvent e) {
      // ignore
    }

    public void windowDeactivated(WindowEvent e) {
      // ignore
    }

    // Variables declaration - do not modify
    private javax.swing.JButton doneBtn;
    private javax.swing.JButton jButton2;
    private javax.swing.JCheckBox jCheckBox1;
    private javax.swing.JComboBox jComboBox2;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel0;
    private javax.swing.JPanel samplejPanel;
    private javax.swing.JRadioButton jRadioButton1;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JComboBox lookAndFeels;

    private ThemeColorTypeJPanel themeColor_cbg;
    private ThemeColorTypeJPanel themeColor_cfg;
    private ThemeColorTypeJPanel themeColor_cwbg;
    private ThemeColorTypeJPanel themeColor_cwfg;
    private ThemeColorTypeJPanel themeColor_cwti;
    private ThemeColorTypeJPanel themeColor_cwfh;
    private ThemeColorTypeJPanel themeColor_cwbh;
    private ThemeColorTypeJPanel themeColor_cwts;
    private ThemeColorTypeJPanel themeColor_cwbs;
    private ThemeColorTypeJPanel themeColor_cwms;
    private ThemeColorTypeJPanel themeColor_cbgx;

    private javax.swing.JPanel themeColorJPanel;
    private javax.swing.JTextArea twLabel;
    private javax.swing.JPanel warningJPanel;


    // End of variables declaration

    /** An inner-class of {@link PlafChooser} which applies colors to themes */
    class ThemeColorTypeJPanel extends JPanel {

      public javax.swing.JLabel typeLabel= new javax.swing.JLabel();
      public javax.swing.JLabel hexLabel= new javax.swing.JLabel();
      public MyCanvas dispColor = new MyCanvas();

      /** ThemeColorTypeJPanel Constructor
          @param themeCompoment theme component to be analyzed
       */
      public ThemeColorTypeJPanel (String themeComponent) {

        Theme gblTheme = (Theme) Theme.getTheme(Theme.getGlobalTheme());
        Color clr = gblTheme.getColor(themeComponent);
        String label = gblTheme.getComponentDescription(themeComponent);

        setLayout(null);
        this.typeLabel.setText(label);
        updateColor(clr);

        typeLabel.setBounds(0,0, 240, 20);
        hexLabel.setBounds(250,0, 80, 20);
        dispColor.setBounds(340,5, 10, 10);
        add(typeLabel);
        add(hexLabel);
        add(dispColor);
      }


      /** Updates the Theme Color
          @param clr the Theme Color
       */
      public void updateColor(Color clr) {
        try {
          hexLabel.setText(String.format("#%06X", 0xFFFFFF & clr.getRGB()));
        } catch ( Exception e)
        {
          addWarning("No color found for " + typeLabel.getText());
        }

        dispColor.setForeground(clr);
      }

    }

    /** An inner-class of {@link PlafChooser} which provides the canvas for the Plaf GUI */
    class MyCanvas extends JComponent {
      protected void paintComponent(Graphics g) {
        g.fillRect(0, 0, getWidth(),getHeight());
      }
    }
  } // end private class PlafChooser

}
