/*
   Copyright (C) 2013-2020 Nicola L.C. Talbot
   www.dickimaw-books.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package com.dickimawbooks.makeglossariesgui;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Arrays;

import javax.swing.*;
import javax.swing.event.*;

public class PropertiesDialog extends JDialog
     implements ActionListener,ItemListener,ChangeListener
{
  public PropertiesDialog(MakeGlossariesGUI application)
  {
     super(application, application.getLabel("properties.title"), true);

     app = application;

     JTabbedPane tabbedPane = new JTabbedPane();
     getContentPane().add(tabbedPane, "Center");

     properties = app.getProperties();

     File file = new File(properties.getDefaultDirectory());

     String setting = properties.getDefaultDirectorySetting();

     if (setting == null) setting = "home";

     fileChooser = new JFileChooser(getDir());

     Box box = Box.createVerticalBox();
     newTab(tabbedPane, box, "properties.start_dir");

     Box startDirBox = Box.createVerticalBox();
     startDirBox.setAlignmentX(Component.LEFT_ALIGNMENT);
     box.add(startDirBox);

     ButtonGroup bg = new ButtonGroup();

     homeButton = createRadioButton("properties.dir.home", "disablecustom",
       bg);
     startDirBox.add(homeButton);

     lastButton = createRadioButton("properties.dir.last", "disablecustom",
       bg);
     startDirBox.add(lastButton);

     Box panel = Box.createHorizontalBox();
     panel.setAlignmentX(Component.LEFT_ALIGNMENT);
     panel.setAlignmentY(Component.TOP_ALIGNMENT);
     startDirBox.add(panel);

     customButton = createRadioButton("properties.dir.custom", "enablecustom",
       bg);
     customButton.setAlignmentY(Component.TOP_ALIGNMENT);
     panel.add(customButton);

     customField = new FileField(app, this, fileChooser,
       JFileChooser.DIRECTORIES_ONLY);
     customField.setOpaque(false);
     customField.setAlignmentY(Component.TOP_ALIGNMENT);
     panel.add(customField);

     if (setting.equals("home"))
     {
        homeButton.setSelected(true);
        customField.setEnabled(false);
     }
     else if (setting.equals("last"))
     {
        lastButton.setSelected(true);
        customField.setEnabled(false);
     }
     else if (setting.equals("custom"))
     {
        customButton.setSelected(true);
        customField.setFileName(file.getAbsolutePath());
     }

     box = Box.createVerticalBox();
     newTab(tabbedPane, box, "properties.diagnostics");

     docDefCheckBox = createCheckBox("properties", "docdefcheck",
        properties.isDocDefsCheckOn());
     box.add(docDefCheckBox);

     missingLangModBox = createCheckBox("properties", "langcheck",
        properties.isMissingLangCheckOn());
     box.add(missingLangModBox);

     box = Box.createVerticalBox();
     newTab(tabbedPane, box, "properties.applications");

     Box makeindexBox = Box.createVerticalBox();
     makeindexBox.setBorder(BorderFactory.createEtchedBorder());
     box.add(makeindexBox);

     panel = Box.createHorizontalBox();
     panel.setAlignmentX(Component.LEFT_ALIGNMENT);
     makeindexBox.add(panel);

     JLabel makeindexLabel = createLabel("properties.makeindex");
     panel.add(makeindexLabel);

     makeindexField = new FileField(app, this,
       properties.getMakeIndexApp(),
       fileChooser);
     makeindexLabel.setLabelFor(makeindexField.getTextField());
     panel.add(makeindexField);
     makeindexField.setOpaque(false);

     Box xindyBox = Box.createVerticalBox();
     xindyBox.setBorder(BorderFactory.createEtchedBorder());
     box.add(xindyBox);

     panel = Box.createHorizontalBox();
     panel.setAlignmentX(Component.LEFT_ALIGNMENT);
     xindyBox.add(panel);

     JLabel xindyLabel = createLabel("properties.xindy");
     panel.add(xindyLabel);

     JPanel xindyDefaultsPanel = new JPanel();
     xindyDefaultsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
     xindyBox.add(xindyDefaultsPanel);
     xindyDefaultsPanel.setOpaque(false);

     langLabel = createLabel("properties.language");
     xindyDefaultsPanel.add(langLabel);

     String[] languages = XindyModule.getKnownLanguages();
     Arrays.sort(languages);

     languageBox = new JComboBox<String>(languages);
     langLabel.setLabelFor(languageBox);

     languageBox.setSelectedItem(properties.getDefaultLanguage());
     languageBox.addItemListener(this);

     xindyDefaultsPanel.add(languageBox);

     xindyModuleLayout = new CardLayout();
     modulesPanel = new JPanel(xindyModuleLayout);
     modulesPanel.setOpaque(false);

     initVariants((String)languageBox.getSelectedItem(),
       properties.getDefaultXindyVariant(),
       properties.getDefaultCodePage());

     xindyDefaultsPanel.add(modulesPanel);

     overrideBox = createCheckBox("properties", "override",
        properties.isOverride());
     xindyBox.add(overrideBox);

     xindyField = new FileField(app, this,
       properties.getXindyApp(), fileChooser);
     xindyLabel.setLabelFor(xindyField.getTextField());
     panel.add(xindyField);
     xindyField.setOpaque(false);

     box = Box.createVerticalBox();
     newTab(tabbedPane, box, "properties.gui");

     JPanel lfPanel = new JPanel();
     lfPanel.setOpaque(false);
     box.add(lfPanel);

     JLabel lfLabel = createLabel("properties.look_and_feel");
     lfPanel.add(lfLabel);

     UIManager.LookAndFeelInfo[] lfInfo = UIManager.getInstalledLookAndFeels();

     lookAndFeelBox = new JComboBox<UIManager.LookAndFeelInfo>(lfInfo);
     lookAndFeelBox.setRenderer(new LookAndFeelCellRenderer());

     String currentLF = properties.getLookAndFeel();

     if (currentLF != null)
     {
        for (int i = 0; i < lfInfo.length; i++)
        {
           if (lfInfo[i].getClassName().equals(currentLF))
           {
              lookAndFeelBox.setSelectedIndex(i);
              break;
           }
        }
     }

     lfPanel.add(lookAndFeelBox);
     lfLabel.setLabelFor(lookAndFeelBox);

     JLabel restartLabel = new JLabel(app.getLabel("properties.restart"));
     lfPanel.add(restartLabel);

     JPanel fontPanel = new JPanel();
     fontPanel.setOpaque(false);
     box.add(fontPanel);

     JLabel fontLabel = createLabel("properties.font");
     fontPanel.add(fontLabel);

     GraphicsEnvironment env =
        GraphicsEnvironment.getLocalGraphicsEnvironment();

     fontBox = new JComboBox<String>(env.getAvailableFontFamilyNames());
     fontBox.setSelectedItem(properties.getFontName());
     fontBox.addItemListener(this);
     fontPanel.add(fontBox);

     int style = properties.getFontStyle();

     boldBox = createCheckBox("properties.bold", "font",
       (style & Font.BOLD) != 0);
     fontPanel.add(boldBox);

     italicBox = createCheckBox("properties.italic", "font",
       (style & Font.ITALIC) != 0);
     fontPanel.add(italicBox);

     JLabel fontSizeLabel = createLabel("properties.size.font");
     fontPanel.add(fontSizeLabel);

     fontSizeBox = new JSpinner(
        new SpinnerNumberModel(properties.getFontSize(), 2,100,1));
     fontPanel.add(fontSizeBox);
     fontSizeLabel.setLabelFor(fontSizeBox);
     fontSizeBox.addChangeListener(this);

     JPanel p = new JPanel();
     box.add(p);

     fontSample = new JLabel(app.getLabel("properties.font.sample"));
     p.setBackground(Color.white);
     p.setOpaque(true);
     p.setBorder(BorderFactory.createEtchedBorder());
     p.add(fontSample);

     updateFontSample();

     JPanel buttonPanel = new JPanel();
     getContentPane().add(buttonPanel, "South");

     buttonPanel.add(app.createOkayButton(this));
     buttonPanel.add(app.createCancelButton(this));

     JButton helpButton = new JButton(app.getLabel("button.help"));
     helpButton.setMnemonic(app.getMnemonic("button.help"));
     app.enableHelpOnButton(helpButton, "properties");
     buttonPanel.add(helpButton);

     pack();

     setLocationRelativeTo(app);

     updateOverride();
  }

  private void newTab(JTabbedPane tabbedPane, JComponent comp,
    String label)
  {
     tabbedPane.add(app.getLabel(label), comp);
     tabbedPane.setMnemonicAt(tabbedPane.getTabCount()-1,
        app.getMnemonic(label));
  }

  private JRadioButton createRadioButton(String label, String action,
    ButtonGroup bg)
  {
     JRadioButton button = new JRadioButton(app.getLabel(label));
     button.setMnemonic(app.getMnemonic(label));
     button.setActionCommand(action);
     button.addActionListener(this);
     bg.add(button);
     button.setOpaque(false);

     return button;
  }

  private JCheckBox createCheckBox(String parent, String label, boolean set)
  {
     JCheckBox box = new JCheckBox(app.getLabel(parent, label), set);
     box.setMnemonic(app.getMnemonic(parent, label));
     box.setActionCommand(label);
     box.addActionListener(this);
     box.setOpaque(false);

     return box;
  }

  private JLabel createLabel(String label)
  {
     JLabel comp = new JLabel(app.getLabel(label));
     comp.setDisplayedMnemonic(app.getMnemonic(label));

     return comp;
  }

  private File getDir()
  {
     String makeindexApp = app.getMakeIndexApp();
     String xindyApp = app.getXindyApp();

     if ((makeindexApp == null || makeindexApp.equals(""))
       &&(xindyApp == null || xindyApp.equals("")))
     {
        try
        {
           Process p = Runtime.getRuntime().exec("kpsewhich texmf.cnf");

           if (p.waitFor() == 0)
           {
              BufferedReader in = null;

              try
              {
                 in = new BufferedReader(
                    new InputStreamReader(p.getInputStream(),
                      app.getEncoding()));

                 String line = in.readLine();

                 if (line != null)
                 {
                    File dir = (new File(line)).getParentFile();

                    if (dir.isDirectory())
                    {
                       return dir;
                    }
                 }
              }
              finally
              {
                 if (in != null)
                 {
                    in.close();
                 }
              }
           }
        }
        catch (Exception e)
        {
        }
     }

     return new File(properties.getDefaultDirectory());
  }

  public void actionPerformed(ActionEvent e)
  {
     String action = e.getActionCommand();

     if (action == null) return;

     if (action.equals("disablecustom"))
     {
        customField.setEnabled(false);
     }
     else if (action.equals("enablecustom"))
     {
        customField.setEnabled(true);
        customField.requestFocusInWindow();
     }
     else if (action.equals("font"))
     {
        updateFontSample();
     }
     else if (action.equals("cancel"))
     {
        setVisible(false);
     }
     else if (action.equals("override"))
     {
        updateOverride();
     }
     else if (action.equals("okay"))
     {
        properties.setMakeIndexApp(makeindexField.getFileName());
        properties.setXindyApp(xindyField.getFileName());
        properties.setDefaultLanguage((String)languageBox.getSelectedItem());
        properties.setDefaultCodePage(currentModule.getSelectedCodePage());

        String variant = currentModule.getSelectedVariant();

        if (variant != null)
        {
           properties.setDefaultXindyVariant(variant);
        }

        properties.setOverride(overrideBox.isSelected());
        properties.setDocDefsCheck(docDefCheckBox.isSelected());
        properties.setMissingLangCheck(missingLangModBox.isSelected());

        if (homeButton.isSelected())
        {
           properties.setDefaultHomeDir();
        }
        else if (lastButton.isSelected())
        {
           properties.setDefaultLastDir();
        }
        else
        {
           String fileName  = customField.getFileName();

           if (fileName.equals(""))
           {
              app.error(this, app.getLabel("error.missing_dir_name"));
              return;
           }

           File file = new File(fileName);

           if (!file.isDirectory())
           {
              app.error(this,
               app.getLabelWithValues("error.no_such_directory", fileName));
           }

           properties.setDefaultCustomDir(fileName);
        }

        boolean fontChanged=false;
        String oldFont = properties.getFontName();
        String newFont = (String)fontBox.getSelectedItem();
        int oldStyle = properties.getFontStyle();
        int newStyle = Font.PLAIN;
        int oldSize = properties.getFontSize();
        int newSize = ((Number)fontSizeBox.getValue()).intValue();

        if (boldBox.isSelected())
        {
           newStyle = Font.BOLD;
        }

        if (italicBox.isSelected())
        {
           newStyle = newStyle | Font.ITALIC;
        }

        if (!oldFont.equals(newFont))
        {
           fontChanged = true;
           properties.setFontName(newFont);
        }

        if (oldStyle != newStyle)
        {
           fontChanged = true;
           properties.setFontStyle(newStyle);
        }

        if (oldSize != newSize)
        {
           fontChanged = true;
           properties.setFontSize(newSize);
        }

        if (fontChanged)
        {
           app.updateFont();
        }

        properties.setLookAndFeel(
          (UIManager.LookAndFeelInfo)lookAndFeelBox.getSelectedItem());

        setVisible(false);
     }
  }

  public void display()
  {
     setVisible(true);
  }

  private void updateOverride()
  {
     boolean enable = overrideBox.isSelected();

     langLabel.setEnabled(enable);
     languageBox.setEnabled(enable);
     currentModule.setEnabled(enable);
  }

  public void setXindy(File path)
  {
     xindyField.setFile(path);
  }

  public void setMakeIndex(File path)
  {
     makeindexField.setFile(path);
  }

  private void initVariants(String lang, String defVariant, String defCode)
  {
     HashMap<String,XindyModule> modules = XindyModule.getKnownModules();

     for (Iterator<String> it = modules.keySet().iterator(); it.hasNext();)
     {
        String key = it.next();
        XindyModule mod = modules.get(key);
        XindyModulePanel panel;

        if (lang.equals(key))
        {
           panel = new XindyModulePanel(app, mod, defVariant, defCode);
           currentModule = panel;
        }
        else
        {
           panel = new XindyModulePanel(app, mod);
        }

        modulesPanel.add(panel);
        xindyModuleLayout.addLayoutComponent(panel, key);
     }

     xindyModuleLayout.show(modulesPanel, lang);
  }

  public void itemStateChanged(ItemEvent e)
  {
     Object source = e.getSource();

     if (source == languageBox)
     {
        updateXindyModule();
     }
     else if (source == fontBox)
     {
        updateFontSample();
     }
  }

  public void stateChanged(ChangeEvent e)
  {
     if (e.getSource() == fontSizeBox)
     {
        updateFontSample();
     }
  }

  private void updateFontSample()
  {
     int style = Font.PLAIN;

     if (boldBox.isSelected())
     {
        style = Font.BOLD;
     }

     if (italicBox.isSelected())
     {
        style = style | Font.ITALIC;
     }

     fontSample.setFont(new Font((String)fontBox.getSelectedItem(),
       style, ((Number)fontSizeBox.getValue()).intValue()));

     fontSample.repaint();
  }

  public void updateFontSize(int size)
  {
     fontSizeBox.setValue(size);
  }

  private void updateXindyModule()
  {
     String language = (String)languageBox.getSelectedItem();

     for (int i = 0, n = modulesPanel.getComponentCount(); i < n; i++)
     {
        Component comp = modulesPanel.getComponent(i);

        if (comp.getName().equals(language))
        {
           currentModule = (XindyModulePanel)comp;
           xindyModuleLayout.show(modulesPanel, language);
           return;
        }
     }

     app.debug("Can't find module panel for language "+language);
     currentModule = (XindyModulePanel)modulesPanel.getComponent(0);
     xindyModuleLayout.first(modulesPanel);
  }

  private MakeGlossariesGUI app;

  private CardLayout xindyModuleLayout;

  private JPanel modulesPanel;

  private JLabel langLabel, fontSample;

  private XindyModulePanel currentModule;

  private JRadioButton homeButton, lastButton, customButton;

  private JComboBox<String> languageBox, fontBox;

  private JComboBox<UIManager.LookAndFeelInfo> lookAndFeelBox;

  private JSpinner fontSizeBox;

  private FileField customField, makeindexField, xindyField;

  private JFileChooser fileChooser;

  private JCheckBox overrideBox, docDefCheckBox, missingLangModBox,
    boldBox, italicBox;

  private MakeGlossariesProperties properties;
}

class XindyModulePanel extends JPanel
{
  public XindyModulePanel(MakeGlossariesGUI app, XindyModule module)
  {
     this(app, module, null, null);
  }

  public XindyModulePanel(MakeGlossariesGUI app,
     XindyModule module, String defVar, String defCode)
  {
     super();
     setName(module.getLanguage());
     this.module = module;
     setOpaque(false);

     encodingLabel = new JLabel(app.getLabel("properties.encoding"));
     encodingLabel.setDisplayedMnemonic(app.getMnemonic("properties.encoding"));
     add(encodingLabel);

     codePageBox = new JComboBox<String>(module.getCodePages());
     add(codePageBox);

     if (defCode != null)
     {
        codePageBox.setSelectedItem(defCode);
     }
     else
     {
        codePageBox.setSelectedIndex(codePageBox.getItemCount()-1);
     }

     encodingLabel.setLabelFor(codePageBox);

     if (module.hasVariants())
     {
        variantBox = new JComboBox<String>(module.getVariants());
        variantBox.setSelectedItem(
          defVar == null ? module.getDefaultVariant() : defVar);
        add(variantBox);
     }
  }

  public String getSelectedVariant()
  {
     return variantBox == null ? null :
      (String)variantBox.getSelectedItem();
  }

  public String getSelectedCodePage()
  {
     return (String)codePageBox.getSelectedItem();
  }

  public void setEnabled(boolean enabled)
  {
     if (variantBox != null)
     {
        variantBox.setEnabled(enabled);
     }

     encodingLabel.setEnabled(enabled);
     codePageBox.setEnabled(enabled);
  }

  private XindyModule module;
  private JComboBox<String> variantBox = null;
  private JComboBox<String> codePageBox = null;
  private JLabel encodingLabel;
}

class LookAndFeelCellRenderer extends DefaultListCellRenderer
{
  public Component getListCellRendererComponent(JList<?> list,
   Object value, int index, boolean isSelected, boolean cellHasFocus)
  {
     Component comp = super.getListCellRendererComponent(list, value, index,
       isSelected, cellHasFocus);

     if (value instanceof UIManager.LookAndFeelInfo)
     {
        setText(((UIManager.LookAndFeelInfo)value).getName());
     }

     return comp;
  }
}