/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/10/17, 12:11
!  AUTHOR(S): KOGA, Junichiro
!  File : InputWizardController.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.input;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;

import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;

import ciss.phase_viewer.inputinterface.Input;
import ciss.phase_viewer.main.PluginLoader;
import ciss.phase_viewer.mainpanel.InternalFrameChase;
import ciss.phase_viewer.projectbrowser.ProjectInfo;

/**
 * InputWizardを制御するためのクラス, のベースクラス.
 * 
 * @author
 */
abstract public class InputWizardController {
    private Logger logger = Logger.getLogger(InputWizardController.class
            .getName());

    protected Vector inputWizards;

    protected Input input;

    private Document specDocument;

    private Element[] wizardSpecs;

    private InputWizardGUI gui;

    private int wizNow = 0;

    private Vector listeners;

    private HashMap properties = new HashMap();

    private InputWizardRoot root;

    private ProjectInfo projectInfo;

    // /**
    // * 入力オブジェクトと, Wizard Specification用Documentを引数に取る.
    // * @param input 作成したい入力オブジェクト
    // * @param specDocument WizardのSpecificationが記述してあるDocument
    // */
    // public InputWizardController(Input input,Document specDocument) {
    // this.input = input;
    // this.specDocument = specDocument;
    // this.initialize();
    // }

    /**
     * 入力オブジェクトと, Wizard Specification用Documentを引数に取る.
     * 
     * @param input
     *            作成したい入力オブジェクト
     * @param specDocument
     *            WizardのSpecificationが記述してあるDocument
     */
    public InputWizardController(Input input, InputWizardRoot root,
            ProjectInfo projectInfo) {
        this.input = input;
        this.root = root;
        this.projectInfo = projectInfo;
        this.specDocument = root.getSpecDocument();
        this.initialize();
    }

    /**
     * プロジェクトの情報を取得.
     * 
     * @return プロジェクトの情報
     */
    public ProjectInfo getProjectInfo() {
        return this.projectInfo;
    }

    public void addInputWizardListener(InputWizardListener listener) {
        if (listeners == null)
            listeners = new Vector();
        listeners.addElement(listener);
    }

    public void removeInputWizardListener(InputWizardListener listener) {
        if (listeners == null)
            return;
        listeners.remove(listener);
    }

    public void cleanUp() {
        listeners.removeAllElements();
        listeners = null;
    }

    public void resetWizards() {
        specDocument = root.getSpecDocument();
        Element rootElement = specDocument.getRootElement();
        java.util.List list = rootElement.getChildren("inputwizard");
        wizardSpecs = new Element[list.size()];
        for (int i = 0; i < wizardSpecs.length; i++)
            wizardSpecs[i] = (Element) list.get(i);
        String aa = wizardSpecs[wizNow].getTextTrim();
        logger.debug("wiz. class : " + aa);
        InputWizard firstWiz = null;
        if (root == null) {
            firstWiz = (InputWizard) PluginLoader.instantiate(aa, null);
        } else {
            firstWiz = root;
        }
        firstWiz.setInput(input);
        firstWiz.setProperties(properties);
        firstWiz.setParent(this);
        inputWizards = new Vector();
        inputWizards.add(firstWiz);
    }

    private void initialize() {
        gui = new InputWizardGUI();
        resetWizards();
        if (gui != null)
            update(root);
    }

    public void update(InputWizard wiz) {
        gui.setGUI(wiz.getGUI());
        description.setText(wiz.getDescription());
        border.setTitle(wiz.getName());
        next.setEnabled(wiz.hasNext());
        previous.setEnabled(wiz.hasPrevious());
        finished.setEnabled(wiz.hasFinished());
        gui.revalidate();
        gui.repaint();
    }

    public void updateButtons(InputWizard wiz) {
        next.setEnabled(wiz.hasNext());
        previous.setEnabled(wiz.hasPrevious());
        finished.setEnabled(wiz.hasFinished());
    }

    private void next() {
        if (wizNow >= wizardSpecs.length - 1)
            return;

        notifyListeners(InputWizardEvent.TO_NEXT);
        wizNow++;
        if (inputWizards.size() <= wizNow) {
            try {
                InputWizard wiz = (InputWizard) PluginLoader.instantiate(
                        wizardSpecs[wizNow].getTextTrim(), null);
                wiz.setInput(input);
                wiz.setProperties(properties);
                wiz.setParent(this);
                inputWizards.add(wiz);
            } catch (Exception exc) {
                exc.printStackTrace();
            }
        }
        InputWizard wi = (InputWizard) inputWizards.get(wizNow);
        wi.setProperties(properties);
        update(wi);
    }

    private void previous() {
        if (wizNow == 0)
            return;
        notifyListeners(InputWizardEvent.TO_PREVIOUS);
        wizNow--;
        InputWizard wi = (InputWizard) inputWizards.get(wizNow);
        wi.setProperties(properties);
        update(wi);
    }

    private boolean can = true;

    private void finished() {
        can = false;
        if (listeners != null) {
            InputWizardEvent ev = new InputWizardEvent(this,
                    InputWizardEvent.FINISHING);
            for (int i = 0; i < listeners.size(); i++)
                ((InputWizardListener) listeners.get(i)).stateChanged(ev);
        }
        logger.info("creating input...");
        for (int i = 0; i < inputWizards.size(); i++) {
            String result = ((InputWizard) inputWizards.get(i)).process();
            if (result != null)
                logger.info(result);
        }

        int numwiz = root.getNumWizards();
        if (numwiz < 0)
            numwiz = wizardSpecs.length;

        // 実際にはインスタンス化していないWizardも.
        for (int i = inputWizards.size(); i < numwiz; i++) {
            InputWizard wiz = (InputWizard) PluginLoader.instantiate(
                    wizardSpecs[i].getTextTrim(), null);
            wiz.setInput(input);
            wiz.setProperties(properties);
            wiz.setParent(this);
            wiz.getGUI();
            wiz.process();
        }

        input.save();
        gui.dispose();

        notifyListeners(InputWizardEvent.FINISHED);
    }

    private void notifyListeners(int mode) {
        if (listeners != null) {
            listeners.trimToSize();
            InputWizardEvent ev = new InputWizardEvent(this, mode);
            int n = listeners.size();
            for (int i = 0; i < n; i++) {
                if (listeners == null)
                    break;
                Object o = listeners.get(i);
                ((InputWizardListener) o).stateChanged(ev);
            }
        }
    }

    private void canceled() {
        can = false;
        logger.info("canceled.");
        gui.dispose();
        notifyListeners(InputWizardEvent.CANCELED);
    }

    public abstract String getWizardType();

    private JButton next;

    private JButton previous;

    private JButton finished;

    private JButton canceled;

    private JTextField description;

    private JPanel panel;

    private TitledBorder border;

    class InputWizardGUI extends InternalFrameChase {
        private static final long serialVersionUID = 1L;

        InputWizardGUI() {
            super("create input for " + getWizardType(), true, true, true,
                    true, new Dimension(550, 400));
            init();
        }

        void setGUI(JComponent comp) {
            panel.removeAll();
            panel.add(comp);
        }

        private void init() {
            JPanel foo = new JPanel();
            foo.setLayout(new BorderLayout());
            border = new TitledBorder("");
            foo.setBorder(border);
            JPanel pd = createDescriptionPanel();
            JPanel pp = createBtnPanel();
            panel = new JPanel();
            foo.add(pd, BorderLayout.NORTH);
            foo.add(panel, BorderLayout.CENTER);
            foo.add(pp, BorderLayout.SOUTH);

            getContentPane().setLayout(new BorderLayout());
            getContentPane().add(foo);
            addInternalFrameListener(new InternalFrameAdapter() {
                public void internalFrameClosed(InternalFrameEvent e) {
                    if (can)
                        canceled();
                }
            });
        }

        private JPanel createDescriptionPanel() {
            JPanel ret = new JPanel();
            ret.setBorder(new TitledBorder("description"));
            ret.setLayout(new BorderLayout());
            description = new JTextField();
            description.setEditable(false);
            ret.add(description);
            return ret;
        }

        private JPanel createBtnPanel() {
            JPanel pbtn = new JPanel();
            pbtn.setLayout(new BoxLayout(pbtn, BoxLayout.X_AXIS));
            next = new JButton("next");
            previous = new JButton("previous");
            finished = new JButton("OK");
            canceled = new JButton("cancel");
            pbtn.add(previous);
            pbtn.add(next);
            pbtn.add(finished);
            pbtn.add(canceled);
            JPanel ret = new JPanel();
            ret.setLayout(new BorderLayout());
            ret.add(pbtn, BorderLayout.EAST);

            next.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    next();
                }
            });

            previous.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    previous();
                }
            });

            finished.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    finished();
                }
            });

            canceled.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    canceled();
                }
            });

            return ret;
        }

    }

}
