/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/10/30 16:39:42
!  AUTHOR(S): KOGA, Junichiro
!  File : ScriptEditorPanel.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */
package ciss.phase_viewer.scripting.scripteditor;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import org.apache.log4j.Logger;

import ciss.phase_viewer.inputinterface.InputInterface;
import ciss.phase_viewer.inputinterface.InputInterfaceData;
import ciss.phase_viewer.inputinterface.InputInterfacePrimitiveEntry;
import ciss.phase_viewer.scripting.ScriptDescriptionPanel;
import ciss.phase_viewer.scripting.ScriptUtils;

/**
 * ScriptEditorGUIの, GUI部.
 * 
 * @author KOGA, Junichirou
 */
public class ScriptEditorPanel extends JPanel {
    private Logger logger = Logger.getLogger(ScriptEditorPanel.class.getName());

    private String script;

    private ScriptEditorCaller parentFrame;

    private InputInterface inputInterface;

    private boolean hasEditor = true;

    private boolean hasDescription = true;

    public ScriptEditorPanel(ScriptEditorCaller parentFrame) {
        this.parentFrame = parentFrame;
        this.script = parentFrame.getScriptPath();
        init();
    }

    public ScriptEditorPanel(ScriptEditorCaller parentFrame, boolean hasEditor,
            boolean hasDescription) {
        this.parentFrame = parentFrame;
        this.script = parentFrame.getScriptPath();
        this.hasEditor = hasEditor;
        this.hasDescription = hasDescription;
        init();
    }

    public void save() {
        if (tabbedPane != null) {
            if (tabbedPane.getSelectedIndex() == 0) {
                ScriptUtils.saveScript(script, inputInterface,
                        descriptionPanel.getScriptDescriptionText());
                scriptEditor.reload();
            } else if (tabbedPane.getSelectedIndex() == 1) {
                scriptEditor.save();
                inputInterface = ScriptUtils.getInputInterfaceFrom(script);
                if (inputInterface == null) {
                    return;
                }
                tabbedPane.removeTabAt(0);
                tabbedPane.insertTab("edit script input", null,
                        createInputEditors(), "", 0);
            }
        } else {
            ScriptUtils.saveScript(script, inputInterface,
                    descriptionPanel.getScriptDescriptionText());
        }
        if (scriptEditor != null)
            scriptEditor.reload();
    }

    private void init() {
        inputInterface = ScriptUtils.getInputInterfaceFrom(script);
        if (inputInterface == null) {
            return;
        }
        createGUI();
        revalidate();
    }

    private JTabbedPane tabbedPane;

    private ScriptEditor scriptEditor;

    private void createGUI() {
        if (hasEditor) {
            tabbedPane = new JTabbedPane();
            tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
            tabbedPane.addTab("edit script input", createInputEditors());
            scriptEditor = new ScriptEditor(script);
            tabbedPane.addTab("edit the script itself", scriptEditor);
            add(tabbedPane);
        } else {
            add(createInputEditors());
        }
    }

    private ScriptDescriptionPanel descriptionPanel;

    private JPanel createInputEditors() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        descriptionPanel = new ScriptDescriptionPanel(script);
        descriptionPanel.setEditable(true);
        String[] blocks = inputInterface.getBlockTags();
        if (blocks == null || blocks.length == 0) {
            JPanel noeditableInput = new JPanel();
            JLabel lab = new JLabel("no editable input found");
            java.awt.Font curfont = lab.getFont();
            lab.setFont(new Font(curfont.getName(), curfont.getStyle(), 18));
            noeditableInput.add(lab);
            panel.add(noeditableInput);
            if (hasDescription)
                panel.add(descriptionPanel);
            return panel;
        }

        JComponent comp = createPane();
        panel.add(comp);

        if (hasDescription)
            panel.add(descriptionPanel);
        return panel;
    }

    private JComponent createPane() {
        JTabbedPane pane = new JTabbedPane();
        pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
        InputInterfaceData[] data = inputInterface.getAllEntries();
        JPanel bpanel = new JPanel();
        String blockName = inputInterface.getCurrentBlock().getName();
        if (!blockName.equals("root block")
                && inputInterface.getPrimitiveEntryCount() != 0) {
            pane.addTab(blockName, bpanel);
            pane.revalidate();
        }
        for (int i = 0; i < data.length; i++) {
            String name = data[i].getName();
            if (data[i].isBlock()) {
                logger.debug("current block: " + blockName);
                logger.debug("name: " + name);
                inputInterface.selectBlock(name);
                JComponent comp = createPane();
                if (comp != null) {
                    pane.addTab(name, comp);
                    pane.revalidate();
                }
            } else if (data[i] instanceof InputInterfacePrimitiveEntry) {
                bpanel.add(createPrimitiveGUI((InputInterfacePrimitiveEntry) data[i]));
            }
        }
        JComponent component = null;
        if (inputInterface.getBlockCount() != 0) {
            component = (JComponent) pane;
        } else {
            component = (JComponent) bpanel;
        }
        inputInterface.selectParentBlock();

        return component;
    }

    private JPanel createPrimitiveGUI(InputInterfacePrimitiveEntry entry) {
        JPanel ptf = new JPanel();
        if (entry == null) {
            return null;
        }
        ptf.setBorder(new TitledBorder(entry.getName()));

        String[] array = null;
        if (entry.getValue().trim().length() == 0) {
            array = new String[1];
            array[0] = "";
        } else {
            array = entry.getValue().trim().split(":");
        }

        if (array.length <= 1 || array[1].trim().equalsIgnoreCase("textfield")) {
            JTextField tf = new JTextField(8);
            tf.setText(array[0]);
            tf.addCaretListener(new CaretListenerScriptEditor(entry
                    .getFullName()));
            ptf.add(tf);
            return ptf;
        }

        if (array.length >= 3 && array[1].trim().equalsIgnoreCase("combo")) {
            String[] choices = array[2].toLowerCase().split("_or_");
            JComboBox combo = new JComboBox(choices);
            combo.setSelectedItem(array[0]);
            combo.addActionListener(new ActionListenerScriptEditor(entry
                    .getFullName()));
            ptf.add(combo);
            return ptf;
        }

        if (array.length >= 3 && array[1].trim().equalsIgnoreCase("btn")) {
            String label = array[0];
            String evalscript = System.getProperty("user.home")
                    + System.getProperty("file.separator") + ".phase-viewer"
                    + System.getProperty("file.separator") + "scripts"
                    + System.getProperty("file.separator") + array[2];
            JButton btn = new JButton(label);
            btn.addActionListener(new BtnActionListener(evalscript));
            ptf.add(btn);
        }

        return ptf;
    }

    /**
     * Buttonは, comboとtextfieldと比較してかなり異様なことをしている. まず, Buttonを使いたい場合 key =
     * btn_text:btn:scriptpath という感じのエントリーを記述する(この際, btn_textにスペースがあってはならない)
     * すると, btn_textを表示し, 枠にkeyみたいな文字列の付加されたJButtonが作成される.
     * さらにそのJButtonに登録されたActionListenerは,
     * scriptpathで指定されるスクリプトパスをInterpreterで実行する. この際,
     * scriptpathは$HOME/.phase-viewer/scriptsからの相対パスで指定されていることを想定している.
     */
    class BtnActionListener implements ActionListener {
        private String evalscript;

        BtnActionListener(String evalscript) {
            this.evalscript = evalscript;
        }

        public void actionPerformed(ActionEvent ae) {
            ScriptUtils.runScript(evalscript, parentFrame.getObjects());
        }
    }

    /**
     * ScriptEditor用のテキストフィールド. CaretListenerによって勝手にInputInterfaceオブジェクトが
     * 更新されたりする.
     */
    class CaretListenerScriptEditor implements CaretListener {
        private String tag;

        private String blockName;

        private String entryName;

        CaretListenerScriptEditor(String tag) {
            this.tag = tag;
            this.blockName = getBlockName(tag);
            this.entryName = getEntryName(tag);
            logger.debug("textfield_scripteditor, tag: " + tag);
        }

        public void caretUpdate(CaretEvent e) {
            doUpdate(e);
        }

        private void doUpdate(CaretEvent e) {
            inputInterface.selectBlock(blockName);
            InputInterfacePrimitiveEntry entry = inputInterface
                    .getPrimitiveEntry(entryName);
            JTextField textfield = (JTextField) e.getSource();
            String val = textfield.getText();
            logger.debug("blockName: " + blockName + " entryName: " + entryName
                    + " val: " + val);
            entry.setValue(val);
        }
    }

    /**
     * ScriptEditor用のAction listener. 勝手にInputInterfaceオブジェクトが 更新されたりする.
     */
    class ActionListenerScriptEditor implements ActionListener {
        private String tag;

        private String blockName;

        private String entryName;

        ActionListenerScriptEditor(String tag) {
            this.tag = tag;
            this.blockName = getBlockName(tag);
            this.entryName = getEntryName(tag);
            logger.debug("combobox_scripteditor, tag: " + tag);
        }

        public void actionPerformed(ActionEvent e) {
            doUpdate(e);
        }

        private void doUpdate(ActionEvent e) {
            inputInterface.selectBlock(blockName);
            InputInterfacePrimitiveEntry entry = inputInterface
                    .getPrimitiveEntry(entryName);
            JComboBox box = (JComboBox) e.getSource();
            String val = box.getSelectedItem().toString();
            String append = box.getItemAt(0).toString();
            for (int i = 1; i < box.getItemCount(); i++) {
                append += "_or_" + box.getItemAt(i);
            }
            val += ":combo:" + append;
            entry.setValue(val);
        }
    }

    private String getBlockName(String tag) {
        String[] foo = tag.split("\\.");
        if (foo == null || foo.length <= 1) {
            return "";
        }

        String ret = foo[0];
        for (int i = 1; i < foo.length - 1; i++) {
            ret += "." + foo[i];
        }
        return ret;
    }

    private String getEntryName(String tag) {
        String[] foo = tag.split("\\.");
        if (foo == null || foo.length == 0) {
            return "";
        }
        return foo[foo.length - 1];
    }

}
