/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/03/06, 19:22
!  AUTHOR(S): KOGA, Junichiro
!  File : ChaseGUI.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.mainpanel;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import org.apache.log4j.Logger;

import ciss.phase_viewer.common.ChaseFileChooser;
import ciss.phase_viewer.common.MyMessageBox;
import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.inputinterface.DataManager;
import ciss.phase_viewer.inputinterface.InputInterface;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.logger.JTextAreaAppender;
import ciss.phase_viewer.main.PhaseViewerMain;
import ciss.phase_viewer.projectbrowser.ProjectBrowser;
import ciss.phase_viewer.projectbrowser.projectdirbrowser.ProjectDirBrowser;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;
import ciss.phase_viewer.ssh.hosts.HostInfo;
import ciss.phase_viewer.ssh.hosts.HostList;
import ciss.phase_viewer.textviewer.HelpViewer;

import com.l2fprod.gui.plaf.skin.SkinLookAndFeel;

/**
 * トップレベルのGUIを作成する.
 * 
 * 具体的には, 以下のような操作を行う. -# メニューバーを作成し, そこに色々な項目を配備する. さらに,
 * 各項目を選択するとそれに合わせたGUIが起動するようなアクションリスナーを配備する. -# キーボードショートカットの定義. -#
 * プログラム全体で使用するプロパティーファイルへアクセスするためのクラスの定義のインスタンス化. このようにすることによって,
 * とあるオブジェクトで施したプロパティーファイルへの変更がプログラム実行時に, 動的に反映される. -#
 * $HOME/.phase-viewerの存在を確かめ, ない場合は適切な初期化の処理を行う.
 * 
 * @author K. Mae and J. Koga
 */

public class ChaseGUI extends FrameChase {
    private static Logger logger = Logger.getLogger(ChaseGUI.class.getName());
    private final String FS = System.getProperty("file.separator");

    // private boolean b_ctrl_PRESSED = false;

    private MyMessageBox mbox;
    private Container cont;

    public JDesktopPane desktop; /* !< 本プログラムのGUIはこのオブジェクト上に描画される. */

    private int fontsize = 12;
    private String fonttype = "SansSerif";
    private int fontstyle = Font.PLAIN;
    // private PropertyManager propProj;

    private boolean chaseiniExists = true;
    private ChaseMenu chaseMenu;
    private static ProjectDirBrowser dirBrowser;

    private Vector components = new Vector();
    private ChaseProgressMonitor monitor;

    private final String toggleWindowString = "TOGGLE_WINDOW";
    private final KeyStroke toggleWindowKey = KeyStroke.getKeyStroke(
            KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK, false);
    private final Action toggleWindowAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            toggleWindow(false);
            // toggleInternalFrame(false);
            setEnabled(true);
        }
    };

    private final String toggleWindowRevString = "TOGGLE_WINDOW_REVERSE";
    private final KeyStroke toggleWindowRevKey = KeyStroke.getKeyStroke(
            KeyEvent.VK_TAB,
            KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK, false);
    private final Action toggleWindowRevAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            toggleWindow(true);
            // toggleInternalFrame(true);
            setEnabled(true);
        }
    };

    private final String closeWindowString = "CLOSE_WINDOW";
    private final KeyStroke closeWindowKey = KeyStroke.getKeyStroke(
            KeyEvent.VK_W, KeyEvent.CTRL_MASK, false);
    private final KeyStroke closeWindowKey2 = KeyStroke.getKeyStroke(
            KeyEvent.VK_ESCAPE, 0, false);
    private final Action closeWindowAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            closeActiveWindow();
            setEnabled(true);
        }
    };

    private final String closeAppString = "CLOSE_APPLICATION";
    private final KeyStroke closeAppKey = KeyStroke.getKeyStroke(KeyEvent.VK_Q,
            KeyEvent.CTRL_MASK, false);
    private final Action closeAppAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            endApplication();
            setEnabled(true);
        }
    };

    private final String toggleDirString = "TOGGLE_DIR_BROWSER";
    private final KeyStroke toggleDirKey = KeyStroke.getKeyStroke(
            KeyEvent.VK_T, KeyEvent.CTRL_MASK + KeyEvent.SHIFT_MASK, false);
    private final Action toggleDirAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            dirBrowser.requestFocus();
            setEnabled(true);
        }
    };

    private final String bootHelpString = "BOOT_HELP";
    private final KeyStroke bootHelpKey = KeyStroke.getKeyStroke(
            KeyEvent.VK_F1, 0, false);
    private final Action bootHelpAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            HelpViewer vie = new HelpViewer();
            setEnabled(true);
        }
    };

    private final String selectFromProj = "SELECT_FROM_PROJECTS";
    private final KeyStroke selectFromProjKey = KeyStroke.getKeyStroke(
            KeyEvent.VK_1, KeyEvent.CTRL_MASK + KeyEvent.SHIFT_MASK, false);
    private final Action selectFromProjAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            selectFromProjects();
        }
    };

    private boolean loadSample = false;

    void setLoadSample(boolean loadSample) {
        this.loadSample = loadSample;
    }

    void init() {
        logger.info("Welcome to PHASE-Viewer!");
        String ver = "Copyright (C)  Corporation.";
        try {
            ver = PhaseViewerMain.getDefaultPropertiesDocument().getRootElement()
                    .getChildTextTrim("copyright");
        } catch (Exception exc) {
        }
        logger.info(ver);

        GlobalProperties props = PropertiesManager
                .getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER);

        desktop = Desk.getDesktop();
        JScrollPane scrpanedesk = new JScrollPane(desktop);
        scrpanedesk.setPreferredSize(new Dimension(desktop.getWidth(), desktop
                .getHeight()));
        ((Desk) desktop).setScrollPane(scrpanedesk);
        ((Desk) desktop).setParentFrame(this);
        // dirBrowser = new ProjectDirBrowser("projects",true);
        dirBrowser = new ProjectDirBrowser("projects");
        if (loadSample) {
            dirBrowser.loadSample();
        }
        mbox = new MyMessageBox();
        cont = getContentPane();
        cont.setLayout(new BoxLayout(cont, BoxLayout.Y_AXIS));

        JPanel panel1 = new JPanel();
        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));

        String unit_increment = props
                .getProperty("desktop_scrollbar_increment");
        int unitincre = 30;
        try {
            unitincre = Integer.parseInt(unit_increment);
        } catch (NumberFormatException nfe) {
            unitincre = 30;
        }

        int hunit = unitincre;
        int vunit = unitincre;

        scrpanedesk.getHorizontalScrollBar().setUnitIncrement(hunit);
        scrpanedesk.getVerticalScrollBar().setUnitIncrement(vunit);

        scrpanedesk.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);

        JTextArea textarea = JTextAreaAppender.getTextArea();
        textarea.setLineWrap(true);
        textarea.setWrapStyleWord(true);
        JScrollPane scrpane = new JScrollPane(textarea);
        scrpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scrpane.setPreferredSize(new Dimension(250, 250));

        JPanel logview = new JPanel();
        logview.setLayout(new BoxLayout(logview, BoxLayout.X_AXIS));
        JButton clearLogButton = new JButton("clear log");
        JButton logsaveButton = new JButton("save log");
        logview.add(scrpane);

        JPanel logbuttons = new JPanel();
        logbuttons.setLayout(new BoxLayout(logbuttons, BoxLayout.Y_AXIS));
        logbuttons.add(clearLogButton);
        logbuttons.add(logsaveButton);
        logview.add(logbuttons);

        JScrollPane scrDir = new JScrollPane();
        JPanel panel = new JPanel();
        scrDir.getViewport().setView(dirBrowser);

        ChaseSplitPane splitpaneDir = new ChaseSplitPane("dirselector",
                JSplitPane.HORIZONTAL_SPLIT, this);
        components.addElement(splitpaneDir);

        splitpaneDir.setOneTouchExpandable(true);
        splitpaneDir.setLeftComponent(dirBrowser);
        splitpaneDir.setRightComponent(scrpanedesk);
        splitpaneDir.init();

        ChaseSplitPane splitpane = new ChaseSplitPane("logviewer",
                JSplitPane.VERTICAL_SPLIT, this);
        splitpane.setTopComponent(splitpaneDir);
        splitpane.setBottomComponent(logview);
        splitpane.setContinuousLayout(true);
        splitpane.setOneTouchExpandable(true);
        splitpane.setResizeWeight(1);
        splitpane.init();
        JPanel foo = new JPanel();
        foo.setLayout(new BoxLayout(foo, BoxLayout.X_AXIS));
        foo.add(splitpane);
        panel1.add(foo);

        JPanel foo1 = new JPanel();
        foo1.setLayout(new BorderLayout());
        monitor = ChaseProgressMonitor.getMonitor();
        foo1.add(monitor, BorderLayout.WEST);
        panel1.add(monitor);
        cont.add(panel1);

        components.addElement(splitpane);

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

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

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                endApplication();
            }
        });

        GlobalProperties gp = PropertiesManager
                .getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER);
        String me = gp.getProperty("dont_show_menu");
        if (!new Boolean(me).booleanValue()) {
            chaseMenu = new ChaseMenu(this);
            this.setJMenuBar(chaseMenu);
        }

        GlobalHotkeyManager hkmanager = GlobalHotkeyManager.getInstance();
        hkmanager.getInputMap().put(toggleWindowKey, toggleWindowString);
        hkmanager.getActionMap().put(toggleWindowString, toggleWindowAction);
        hkmanager.getInputMap().put(toggleWindowRevKey, toggleWindowRevString);
        hkmanager.getActionMap().put(toggleWindowRevString,
                toggleWindowRevAction);
        hkmanager.getInputMap().put(closeWindowKey, closeWindowString);
        // hkmanager.getInputMap().put(closeWindowKey2,closeWindowString);
        // //ESCはモーダルなGUIがあがってる時に困る
        hkmanager.getActionMap().put(closeWindowString, closeWindowAction);
        hkmanager.getInputMap().put(closeAppKey, closeAppString);
        hkmanager.getActionMap().put(closeAppString, closeAppAction);
        hkmanager.getInputMap().put(toggleDirKey, toggleDirString);
        hkmanager.getActionMap().put(toggleDirString, toggleDirAction);

        hkmanager.getInputMap().put(bootHelpKey, bootHelpString);
        hkmanager.getActionMap().put(bootHelpString, bootHelpAction);

        hkmanager.getInputMap().put(selectFromProjKey, selectFromProj);
        hkmanager.getActionMap().put(selectFromProj, selectFromProjAction);

        /**
         * 'ProjectDirBrowser'の操作 ...
         * と思ったけどこれらをグローバルにするとたとえばCTRL+CとかCTRL+Nとかで困る; 止めた.
         */
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_X,KeyEvent.CTRL_DOWN_MASK),"CUT");
        // hkmanager.getActionMap().put("CUT",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.doProjectCut(dirBrowser.getSelectedNode());
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C,KeyEvent.CTRL_DOWN_MASK),"COPY");
        // hkmanager.getActionMap().put("COPY",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.doProjectCopy(dirBrowser.getSelectedNode());
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_V,KeyEvent.CTRL_DOWN_MASK),"PASTE");
        // hkmanager.getActionMap().put("PASTE",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.doProjectPaste();
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F2,0),"RENAME");
        // hkmanager.getActionMap().put("RENAME",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.bootRenamer(dirBrowser.getSelectedNode());
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),"ACTIVATE");
        // hkmanager.getActionMap().put("ACTIVATE",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // if ( dirBrowser.hasFocus() ) {
        // ProjectDirBrowserNode node = dirBrowser.getSelectedNode();
        // if ( node != null ) {
        // node.selectMe();
        // dirBrowser.saveMyProjects();
        // }
        // }
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_N,KeyEvent.CTRL_MASK),"NEWPROJ");
        // hkmanager.getActionMap().put("NEWPROJ",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.bootProjectCreator();
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_N,KeyEvent.SHIFT_MASK+KeyEvent.CTRL_MASK),"NEWSUBPROJ");
        // hkmanager.getActionMap().put("NEWSUBPROJ",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.bootSubProjectCreator();
        // }
        // });
        // hkmanager.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0),"REMOVE");
        // hkmanager.getActionMap().put("REMOVE",new AbstractAction() {
        // public void actionPerformed(ActionEvent ae) {
        // dirBrowser.bootProjectRemover();
        // }
        // });

        if (lb != null) {
            lb.stop();
        }
        if (ini != null) {
            ini.setVisible(false);
            ini.dispose();
        }

        /*
         * try{ SwingUtilities.updateComponentTreeUI(this); } catch(Exception
         * exc) { logger.error("failed update of UI"); exc.printStackTrace(); }
         */

        display(true);
    }

    private LogoBooter lb;
    private ChaseInitializer ini;

    private void convertHostsFile() {
        // logger.info("converting old hosts file");
        HostInfo hinfo = HostList.getHostList().getHostInfo("localhost");
        DataManager dm = new DataManager(oldHosts);
        dm.setWSisDelimiter(false);
        dm.parse();
        InputInterface inputInterface = dm.getInputInterface();
        inputInterface.selectRoot();
        String oldbdir = inputInterface.getInputInterfacePrimitiveEntry(
                "localhost.basedir").getValue();
        if (oldbdir != null && oldbdir.trim().length() != 0) {
            hinfo.setProperty("basedir", oldbdir);
        }
        HostList.getHostList().save();
    }

    private String oldHosts = System.getProperty("user.home")
            + System.getProperty("file.separator") + ".phase-viewer"
            + System.getProperty("file.separator") + "hosts";
    private String currHosts = System.getProperty("user.home")
            + System.getProperty("file.separator") + ".phase-viewer"
            + System.getProperty("file.separator") + "hosts.xml";

    /**
     * メニューバーやその項目, 対応するアクションリスナーの配備, プロパティーファイルアクセス用の オブジェクトの作成, などの操作を行う.
     * 
     * @param splash
     *            真のとき起動時にスプラッシュスクリーンを描画する.
     */
    public ChaseGUI(boolean splash) {
        super("PHASE-Viewer:", new Dimension(1024, 768), false);

        String chaseini = System.getProperty("user.home")
                + System.getProperty("file.separator")
                + System.getProperty("file.separator") + ".phase-viewer"
                + System.getProperty("file.separator") + "phase-viewer.ini";
        boolean chaseiniExists = new File(chaseini).exists();

        String version = System.getProperty("user.home")
                + System.getProperty("file.separator") + ".phase-viewer"
                + System.getProperty("file.separator") + "VERSION";
        boolean verfileExists = new File(version).exists();
        double locversion = -1;
        if (verfileExists) {
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new FileReader(version));
                locversion = Double.parseDouble(reader.readLine().split(":")[1]
                        .trim());
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } catch (NumberFormatException nfe) {
            } catch (NullPointerException npe) {
            } finally {
                try {
                    reader.close();
                } catch (Exception io) {
                }
            }
        }
        logger.debug("version file: " + version + " does it exsist? "
                + verfileExists);
        String versionno = PhaseViewerMain.getDefaultPropertiesDocument().getRootElement()
                .getChildTextTrim("version");
        double currversion = Double.parseDouble(versionno);

        logger.debug("version: " + versionno);

        boolean currhostsexi = new File(currHosts).exists();
        boolean oldhostsexi = new File(oldHosts).exists();
        logger.debug("currhost exists? " + currhostsexi);
        logger.debug("oldhost exists? " + oldhostsexi);
        if (new File(oldHosts).exists() && !new File(currHosts).exists()) {
            convertHostsFile();
        }

        if (splash) {
            lb = new LogoBooter(this);
            // new Thread(lb).start();
        }

        setInitialLookandFeel();
        if (chaseiniExists && currversion <= locversion) {
            init();
        } else {
            if (lb != null) {
                lb.stop();
            }
            PrintWriter verwriter = null;
            try {
                verwriter = new PrintWriter(new BufferedWriter(new FileWriter(
                        version)));
                verwriter.println("version: " + versionno);
                verwriter.flush();
            } catch (Exception exc) {
                logger.error("failed write to: " + version);
            } finally {
                try {
                    verwriter.close();
                } catch (Exception exc) {
                }
            }
            ini = new ChaseInitializer(this);
        }
    }

    // END of constructor

    public ChaseMenu getMenuBarChase() {
        return this.chaseMenu;
    }

    private void toggleWindow(boolean reverse) {
        if (isActive()) {
            toggleInternalFrame(reverse);
        } else {
            Desktop4Frame.toggleFrame(reverse);
        }
    }

    private void closeActiveWindow() {
        Frame frameHeavy = Desktop4Frame.getActiveComponent();
        if (frameHeavy != null && frameHeavy != this) {
            frameHeavy.dispose();
            return;
        }
        JInternalFrame frame = desktop.getSelectedFrame();
        if (frame != null) {
            frame.dispose();
        }
    }

    /**
     * このGUIを可視化する. その際, $HOME/.phase-viewerディレクトリーとその下の 設定ファイルの有無も調べる.
     * 存在しない場合作成する.
     * 
     * @param disp
     *            真の時可視にする.
     */
    public void display(boolean disp) {
        if (monitor != null) {
            monitor.init();
        }

        setVisible(disp);
        // if ( !chaseiniExists ) {
        // Object [] selection = {"yes", "no, I'll do it later"};
        // int ret = JOptionPane.showInternalOptionDialog(Desktop.getDesktop(),
        // "this seems to be your first time boot ... set preferences now?",
        // "message",
        // JOptionPane.YES_NO_OPTION,
        // JOptionPane.INFORMATION_MESSAGE,
        // null,
        // selection,
        // selection[0]);
        // if ( ret == JOptionPane.YES_OPTION ) {
        // Utils.bootPropertyPanel();
        // }
        // }
        //
        JInternalFrame[] frames = desktop.getAllFrames();
        if (frames != null && frames.length != 0) {
            try {
                frames[frames.length - 1].setSelected(true);
            } catch (Exception exc) {
            }
        }
    }

    private void endApplication() {
        exiting();
        dispose();
        System.exit(0);
    }

    public void exiting() {
        dirBrowser.saveMyProjects();
        for (int i = 0; i < components.size(); i++) {
            ((ChaseFrame) components.elementAt(i)).saveState();
        }
    }

    public static int getScreenHeight() {
        GraphicsEnvironment ge = GraphicsEnvironment
                .getLocalGraphicsEnvironment();
        GraphicsDevice device = ge.getDefaultScreenDevice();
        DisplayMode mode = device.getDisplayMode();
        if (mode == null)
            return 0;
        return mode.getHeight();
    }

    public static int getScreenWidth() {
        GraphicsEnvironment ge = GraphicsEnvironment
                .getLocalGraphicsEnvironment();
        GraphicsDevice device = ge.getDefaultScreenDevice();
        DisplayMode mode = device.getDisplayMode();
        if (mode == null)
            return 0;
        return mode.getWidth();
    }

    public void setUI(String str) {
        try {
            UIManager.setLookAndFeel(str);
            SwingUtilities.updateComponentTreeUI(this);
        } catch (Exception exc) {
            logger.error("Error loading L&F: " + str);
            exc.printStackTrace();
        } catch (java.lang.Error er) {
            setUI(LookandFeel.landf_metal);
        }
    }

    static ProjectDirBrowser getProjectDirBrowser() {
        return dirBrowser;
    }

    private void updateInternalFrameMap(JInternalFrame[] frames,
            JInternalFrame currentFrame) {
        if (frameMap == null || frameMap.length != frames.length) {
            frameMap = new JInternalFrame[frames.length];
            for (int i = 0; i < frameMap.length; i++) {
                frameMap[i] = frames[i];
            }
        } else {
            boolean found = false;
            for (int i = 0; i < frameMap.length; i++) {
                if (frameMap[i] == currentFrame) {
                    found = true;
                }
            }
            if (!found) {
                frameMap = new JInternalFrame[frames.length];
                for (int i = 0; i < frameMap.length; i++) {
                    frameMap[i] = frames[i];
                }
            }
        }
    }

    static void selectFromProjects() {
        JInternalFrame currFrame = Desk.getDesktop().getSelectedFrame();
        if (currFrame instanceof ProjectBrowser) {
            ((ProjectBrowser) currFrame).selectFromProjects();
        }
    }

    private JInternalFrame[] frameMap;

    private void toggleInternalFrame(boolean reverse) {
        JInternalFrame[] frames = desktop.getAllFrames();
        if (frames == null || frames.length == 0) {
            return;
        }
        JInternalFrame currentFrame = desktop.getSelectedFrame();
        if (currentFrame == null) {
            currentFrame = frames[frames.length - 1];
        }
        JInternalFrame nextFrame = null;
        updateInternalFrameMap(frames, currentFrame);

        if (frameMap.length == 1) {
            nextFrame = currentFrame;
        } else {
            if (!reverse) {
                for (int i = 0; i < frameMap.length; i++) {
                    if (frameMap[i] == currentFrame) {
                        if (i == frameMap.length - 1) {
                            nextFrame = frameMap[0];
                        } else {
                            nextFrame = frameMap[i + 1];
                        }
                    }
                }
            } else {
                for (int i = 0; i < frameMap.length; i++) {
                    if (frameMap[i] == currentFrame) {
                        if (i == 0) {
                            nextFrame = frameMap[frameMap.length - 1];
                        } else {
                            nextFrame = frameMap[i - 1];
                        }
                    }
                }
            }
        }
        try {
            nextFrame.setSelected(true);
        } catch (Exception exc) {
        }
    }

    private void clearLog() {
        JTextArea textArea = JTextAreaAppender.getTextArea();
        textArea.setText("");
    }

    private void exportLog() {
        JTextArea textArea = JTextAreaAppender.getTextArea();
        ChaseFileChooser filechooser = new ChaseFileChooser(
                ChaseFileChooser.log);
        filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (filechooser.showDialog(this, "open") != JFileChooser.APPROVE_OPTION) {
            return;
        }

        String selectedFile = filechooser.getSelectedFile().getAbsolutePath();
        logger.info("exported log file to " + selectedFile);

        try {
            FileWriter writer = new FileWriter(new File(selectedFile));
            writer.write(textArea.getText());
            writer.flush();
            writer.close();
        } catch (IOException ioe) {
            logger.error("failed to export log to file : " + selectedFile);
        }
    }

    protected void setInitialLookandFeel() {
        GlobalProperties props = PropertiesManager
                .getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER);

        try {
            int default_font_style = java.awt.Font.BOLD;
            String default_font_name = "Helvetica";
            int default_font_size = 11;
            default_font_name = props.getProperty("default_font_name");
            default_font_style = Integer.parseInt(props
                    .getProperty("default_font_style"));
            default_font_size = Integer.parseInt(props
                    .getProperty("default_font_size"));
            LookandFeel.setUIFont(default_font_name, default_font_style,
                    default_font_size);
        } catch (Exception exc) {
        }

        String lf = props.getProperty("look_and_feel");
        if (lf != null && lf.length() != 0) {
            boolean failed_skin_load = false;
            if (lf.equals(LookandFeel.landf_skin)) {
                String skin = props.getProperty("look_and_feel_skin");
                String defaultSkin = "defaultthemepack.zip";
                if (skin != null && skin.trim().length() != 0) {
                    try {
                        SkinLookAndFeel.setSkin(SkinLookAndFeel
                                .loadThemePack(System
                                        .getProperty("pviewer.home")
                                        + FS
                                        + "lib" + FS + "skinlf" + FS + skin));
                    } catch (Exception ee) {
                        failed_skin_load = true;
                        logger.error("failed to load: " + skin);
                    }
                } else {
                    try {
                        SkinLookAndFeel.setSkin(SkinLookAndFeel
                                .loadThemePack(System
                                        .getProperty("pviewer.home")
                                        + FS
                                        + "lib"
                                        + FS
                                        + "skinlf"
                                        + FS
                                        + defaultSkin));
                    } catch (Exception ex) {
                        failed_skin_load = true;
                        logger.error("failed to load: " + defaultSkin);
                    }
                }
            }

            int i;
            boolean sup = false;
            for (i = 0; i < LookandFeel.supported_lf.length; i++) {
                if (LookandFeel.supported_lf[i].equals(lf)) {
                    setUI(lf);
                    sup = true;
                    break;
                }
            }

            if (!sup) {
                logger.error("unsupported look and feel: " + lf);
                logger.error("using default: " + LookandFeel.landf_metal);
                setUI(LookandFeel.landf_metal);
            } else if (failed_skin_load) {
                logger.error("unsupported skin.");
                setUI(LookandFeel.landf_metal);
            }

        } else {
            if (Utils.isWindows())
                setUI(LookandFeel.landf_win);
            else
                setUI(LookandFeel.landf_metal);
        }

    }
}

/**
 * 起動時のスプラッシュスクリーンを生成するクラス.
 * 
 * @author K. Mae and J. Koga,
 */
class ChaseLogoWindow extends JFrame {

    private Logger logger = Logger.getLogger(ChaseLogoWindow.class.getName());
    private BufferedImage image;
    private int sleep;

    public ChaseLogoWindow(Frame frame) {
        // super(frame);
        // display position
        // window size
        //super("chase");
        super("PHASE-Viewer");
        setUndecorated(true);
        org.jdom.Document doc = PhaseViewerMain.getDefaultPropertiesDocument();
        org.jdom.Element element = doc.getRootElement().getChild(
                "splash_screen");

        String imgpath = MyElement.decode(element
                .getChildTextTrim("imagefilepath"));
        String sx = MyElement.decode(element.getChildTextTrim("scale_x"));

        String sy = MyElement.decode(element.getChildTextTrim("scale_y"));
        String ssleep = MyElement.decode(element
                .getChildTextTrim("default_sleep"));

        double scalex = 0.7d;
        double scaley = 0.7d;
        sleep = 2000;
        if (sx != null && sy != null && ssleep != null) {
            try {
                scalex = Double.parseDouble(sx);
                scaley = Double.parseDouble(sy);
                sleep = Integer.parseInt(ssleep);
            } catch (NumberFormatException nfe) {
            }
        }
        java.net.URL url = ChaseLogoWindow.class.getClass().getResource(
                imgpath.trim());

        BufferedImage bi = null;
        try {
            bi = ImageIO.read(url);
        } catch (Exception e) {
        }

        if (bi == null) {
            logger.error("unable to load splash screen.");
            return;
        }

        int sizex = (int) (bi.getWidth() * scalex);
        int sizey = (int) (bi.getHeight() * scaley);
        AffineTransform trans = new AffineTransform();
        trans.scale(scalex, scaley);

        AffineTransformOp transform = new AffineTransformOp(trans,
                AffineTransformOp.TYPE_BILINEAR);
        image = transform.filter(bi, null);

        setSize(sizex, sizey);
        int scrx = ChaseGUI.getScreenWidth();
        int scry = ChaseGUI.getScreenHeight();

        int centerx = (int) (scrx / 2 - sizex / 2);
        int centery = (int) (scry / 2 - sizey / 2);

        setLocation(centerx, centery);
    }

    protected int getDefaultSleepTime() {
        return sleep;
    }

    /**
     * スプラッシュスクリーンの内容を描画する.
     * 
     * @param g
     *            このGraphicsオブジェクトにスプラッシュスクリーンを描画する.
     */
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        if (image != null) {
            g2.drawImage(image, null, 0, 0);
        }
    }
}

class LogoBooter implements Runnable {
    private Frame frame;
    private boolean stop = false;
    private ChaseLogoWindow mlw;

    protected LogoBooter(Frame frame) {
        this.frame = frame;
        mlw = new ChaseLogoWindow(frame);
        mlw.setVisible(true);

        int sleeptime = mlw.getDefaultSleepTime();

        String s = PropertiesManager.getGlobalProperties(
                PropertiesManager.PROPERTIES_PVIEWER).getProperty(
                "logo_sleep_time");
        try {
            sleeptime = Integer.parseInt(s);
        } catch (NumberFormatException nfe) {
            sleeptime = 2000;
        } catch (NullPointerException npe) {
            sleeptime = 2000;
        }

        try {
            Thread.sleep(sleeptime); // sleep 2sec
        } catch (InterruptedException exp) {
        }

    }

    public void run() {
        // show chase splash screen if splash=true
        //
        // int sleeptime = 2000;
        // String s =
        // PropertiesManager.getGlobalProperties(PropertiesManager.PROPERTIES_PVIEWER).getProperty("logo_sleep_time");
        // try {
        // sleeptime = Integer.parseInt(s);
        // } catch(NumberFormatException nfe) {
        // sleeptime = 2000;
        // } catch(NullPointerException npe) {
        // sleeptime = 2000;
        // }
        //
        // try {
        // Thread.sleep(sleeptime); //sleep 2sec
        // } catch (InterruptedException exp) {
        // }
        // mlw = new ChaseLogoWindow(frame);
        // mlw.setVisible(true);
        // hide logo window (dispose dekinai?)
    }

    protected void stop() {
        mlw.dispose();
    }

}
