/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/08/12, 18:44
!  AUTHOR(S): KOGA, Junichiro
!  File : ProjectBrowserUtils.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.projectbrowser;

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.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;

import javax.swing.JEditorPane;
import javax.swing.tree.TreePath;

import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import ciss.phase_viewer.common.ConstParameters;
import ciss.phase_viewer.common.ExternalProgramExecuter;
import ciss.phase_viewer.common.MyThread;
import ciss.phase_viewer.common.Utils;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.projectbrowser.projectdirbrowser.ProjectDirBrowser;
import ciss.phase_viewer.projectbrowser.projectdirbrowser.ProjectDirBrowserNode;
import ciss.phase_viewer.projectbrowser.projectdirbrowser.ProjectDirBrowserObject;
import ciss.phase_viewer.projectbrowser.projectstatemanipulator.ProjectState;
import ciss.phase_viewer.projectbrowser.projectstatemanipulator.SubProjectCreatorPanel;
import ciss.phase_viewer.settings.PropertiesManager;
import ciss.phase_viewer.textviewer.HyperLink;

/**
 * a set of utility static methods for this package.
 * 
 * @author KOGA, Junichiro
 */
public class ProjectBrowserUtils {

    private static final Logger logger = Logger
            .getLogger(ProjectBrowserUtils.class.getName());

    /**
     * configures a jeditorpane object and displays an html file.
     * 
     * @param the
     *            jeditorpane
     * @param the
     *            URL where which the html file exists.
     */
    public static void setHtmlToEditorPane(JEditorPane pane, URL url) {
        pane.setContentType("text/html; charset=Shift_JIS");
        pane.setEditable(false);
        pane.addHyperlinkListener(new HyperLink(pane, url));
        try {
            pane.setPage(url);
        } catch (IOException ioe) {
        }
    }

    /**
     * boots a text editor file specified in
     * $HOME/.phase-viewer/phase-viewer.ini.
     * 
     * @param textfile
     *            the input file for the text editor.
     */
    public static void bootTextViewer(String textfile) {
        File file = new File(textfile);
        if (file.exists()) {
            String editor = PropertiesManager.getGlobalProperties(
                    PropertiesManager.PROPERTIES_PVIEWER).getProperty("editor");
            if (editor == null) {
                logger.error("specify editor");
                return;
            }
            MyThread th = new MyThread(Utils.getPathDQ(editor) + " "
                    + Utils.getPathDQ(textfile));
        } else {
            logger.error("file non-existent");
        }
    }

    /**
     * boots a text editor file specified in
     * $HOME/.phase-viewer/phase-viewer.ini, filtered by the filter specified in
     * args.
     * 
     * @param textfile
     *            the input file for the text editor.
     * @param filter
     * @param ignoreCase
     */
    public static void bootTextViewer(String textfile, String filter,
            boolean ignoreCase) {
        if (ignoreCase) {
            filter = filter.toLowerCase();
        }

        String compString = "";
        String readString = "";
        String writeString = "";
        try {
            BufferedReader br = new BufferedReader(new FileReader(textfile));
            while ((readString = br.readLine()) != null) {
                if (ignoreCase) {
                    compString = readString.toLowerCase();
                } else {
                    compString = readString;
                }
                if (!(compString.indexOf(filter) == -1)) {
                    writeString += readString
                            + System.getProperty("line.separator");
                }
            }
            br.close();
        } catch (Exception exc) {
            exc.printStackTrace();
            System.out.println("failed to read " + textfile);
        }

        File file = new File(textfile + "_" + filter);
        file.deleteOnExit();

        try {
            FileWriter fwriter = new FileWriter(file);
            BufferedWriter bwriter = new BufferedWriter(fwriter);
            bwriter.write(writeString, 0, writeString.length());
            bwriter.flush();
            fwriter.close();
        } catch (Exception exc) {
            exc.printStackTrace();
            System.out.println("failed to write " + file);
        }

        bootTextViewer(file.getAbsolutePath());
    }

    /**
     * boots the program 'BioStationViewer' specified in
     * $HOME/.phase-viewer/phase-viewer.ini
     * 
     * @param input
     *            input file for BioStationViewer(may be null or
     *            input.trim().length() == 0)
     */
    public static void bootBioStationViewer(String input) {
        String bsviewer = System.getProperty("pviewer.home")
                + System.getProperty("file.separator")
                + PropertiesManager.getGlobalProperties(
                        PropertiesManager.PROPERTIES_PVIEWER).getProperty(
                        "BioStationViewer");

        if (bsviewer == null) {
            logger.error("could not find BioStationViewer");
            return;
        }
        if (!((new File(bsviewer)).exists())) {
            logger.error("could not find BioStationViewer");
            return;
        }

        String fileOption = "";
        if (input != null && input.length() != 0) {
            fileOption = " -f " + Utils.getPathDQ(input);
        }

        String bsvexe = Utils.getPathDQ(ConstParameters.getJavaPath())
                + " -jar " + Utils.getPathDQ(bsviewer) + fileOption;

        MyThread mt = new MyThread(bsvexe);
    }

    public static boolean changeProjectState(ProjectDirBrowser dirBrowser,
            ProjectState state) {
        int projState = state.getProjectState();
        ProjectDirBrowserNode origNode = state.getNode();
        ProjectDirBrowserObject origObj = (ProjectDirBrowserObject) origNode
                .getUserObject();
        ProjectDirBrowserNode origParentNode = state.getParentNode();

        ProjectDirBrowserNode nd = dirBrowser.getSelectedNode();
        if (nd == null) {
            logger.error("node not selected.");
            return false;
        }
        ProjectDirBrowserObject obj = (ProjectDirBrowserObject) nd
                .getUserObject();
        /*
         * rename の場合, ProjectInfoのnameとprojectdirectoryのみが変化していると仮定.
         * まあいまいちだな...
         */
        if (projState == ProjectState.PROJECT_RENAME) {
            Element element = origNode.getProjectElement();
            ProjectInfo info = origNode.getProjectInfo();
            String projDir = info.getProjectDirectory();
            String origName = obj.getText();
            File origFile = new File(new File(projDir).getParent()
                    + System.getProperty("file.separator") + origName);
            File newFile = new File(projDir);
            logger.debug("renaming " + origFile.getAbsolutePath() + " to "
                    + newFile.getAbsolutePath());
            ProjectBrowser brow = null;
            if (!origFile.equals(newFile) && origFile.renameTo(newFile)) {
                // updateElement(element,info.getProjectName(),info.getProjectDirectory());
                ProjectDirBrowser.saveMyProjectsDocument();
                origObj.recreate();
                dirBrowser.updateTree(nd);
                brow = origNode.getProjectBrowser();
            } else {
                info.setProjectName(origName);
                info.setProjectDirectory(new File(info.getProjectDirectory())
                        .getParent()
                        + System.getProperty("file.separator")
                        + origName);
                logger.error("failed to rename " + origFile.getName() + " to "
                        + newFile.getName());
                return false;
            }
            if (brow != null) {
                brow.recreate();
            }
        } else if (projState == ProjectState.PROJECT_CUT) {
            String origDir = origNode.getProjectInfo().getProjectDirectory();
            Element ele = origNode.getProjectElement();
            String newDir = nd.getProjectInfo().getProjectDirectory()
                    + System.getProperty("file.separator")
                    + new File(origDir).getName();
            logger.debug("pasting to node: " + nd);
            String newname = new File(newDir).getName();
            if (!new File(origDir).equals(new File(newDir))) {
                newname = getUniqueName(state, new File(newDir).getParentFile());
            }
            newDir = new File(newDir).getParent()
                    + System.getProperty("file.separator") + newname;
            logger.debug("newDir: " + newDir);
            if (ExternalProgramExecuter.move(origDir, newDir)) {
                origNode.getProjectInfo().setProjectDirectory(newDir);
                origObj.recreate();
                dirBrowser.updateTree(origNode);
                dirBrowser.addObject(nd, origNode, true);
                // updateElement(ele,new File(origDir).getName(),newDir);
                ele.detach();
                nd.getProjectElement().addContent(ele);
                ProjectDirBrowser.saveMyProjectsDocument();
                ProjectBrowser brow = origNode.getProjectBrowser();
                if (brow != null) {
                    brow.recreate();
                }
            } else {
                logger.error("couldn't move file ... restoring original project configuration");
                dirBrowser.addObject(origParentNode, origNode, true);
                dirBrowser.updateTree(origNode);
                return false;
            }
            dirBrowser.updateTree(origNode);
        } else if (projState == ProjectState.PROJECT_COPY) {
            String origDir = origNode.getProjectInfo().getProjectDirectory();
            Element ele = origNode.getProjectElement();
            // if ( origNode == nd ) {
            // logger.error("can't copy to same project.");
            // return false;
            // }

            String newDir = nd.getProjectInfo().getProjectDirectory();
            String newname = getUniqueName(state, new File(newDir));
            logger.debug("origDir: " + origDir + ", newDir: " + newDir);
            if (Utils.copyDir(new File(origDir), new File(newDir), newname)) {
                ProjectDirBrowserNode newNode = (ProjectDirBrowserNode) origNode
                        .clone();
                newNode.getProjectInfo().setProjectDirectory(
                        new File(newDir).getParent()
                                + System.getProperty("file.separator")
                                + newname);
                ((ProjectDirBrowserObject) newNode.getUserObject()).recreate();
                dirBrowser.updateTree(newNode);
                dirBrowser.addObject(nd, newNode, true);

                Element newElem = newNode.getProjectElement();
                Element date = newElem.getChild("date");
                if (date == null) {
                    date = new Element("date")
                            .setText(MyElement.encode(Calendar.getInstance()
                                    .getTime().toString()));
                    newElem.addContent(date);
                } else {
                    date.setText(MyElement.encode(Calendar.getInstance()
                            .getTime().toString()));
                }
                ProjectDirBrowser.saveMyProjectsDocument();
                dirBrowser.recreate();
            } else {
                return false;
            }
        }

        return true;
    }

    public static Vector<String> getSupportedSubProjectTypes() {
        Element elem = getProjSpecElement();
        java.util.List list = elem.getChildren("project");
        java.util.Vector retvec = new java.util.Vector();
        for (int i = 0; i < list.size(); i++) {
            Element pro = (Element) list.get(i);
            String type = MyElement.decode(pro.getChildTextTrim("type"));
            if (type.equals("sub")) {
                retvec.addElement(MyElement.decode(pro.getChildTextTrim("name")));
            }
        }
        return retvec;
    }

    private static Element getProjSpecElement() {
        Document doc = null;
        SAXBuilder builder = new SAXBuilder();
        URL projurl = null;
        projurl = SubProjectCreatorPanel.class
                .getResource(ProjectManipulator.PROJECT_MANIPULATOR_XML_PATH);
        try {
            doc = builder.build(projurl);
        } catch (JDOMException jde) {
            logger.error("failed to parse " + projurl);
            logger.error("unrecoverable error.");
        } catch (java.io.IOException ioe) {
            logger.error("failed to parse " + projurl);
            logger.error("unrecoverable error.");
        }

        Element elem = doc.getRootElement();
        if (!elem.getName().equals("root")) {
            logger.error("invalid ProjectSpecification file.");
        }
        return elem;
    }

    private static String getUniqueName(ProjectState state, File toDir) {
        String dir = state.getNode().getProjectInfo().getProjectDirectory();
        File[] files = toDir.listFiles();
        if (files == null || files.length == 0) {
            return new File(dir).getName();
        }
        String[] names = new String[files.length];
        for (int i = 0; i < names.length; i++) {
            names[i] = files[i].getName();
        }
        String key = new File(dir).getName();
        while (true) {
            boolean equ = false;
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(key)) {
                    equ = true;
                }
            }
            if (!equ) {
                break;
            }
            key += "_";
        }
        return key;
    }

    /**
     * creates an appropriate XML element from the specified directory
     * 
     * @param dir
     *            the directory from which an XML element is to be created
     */
    public static java.util.List getElementFromDir(File dir) {
        logger.debug("at getElementFromDir");
        Vector ret = new Vector();
        File[] files = dir.listFiles();
        if (files == null || files.length == 0)
            return ret;

        for (int i = 0; i < files.length; i++) {
            if (files[i].getName().equals("data")) { // とりあえず, 特別扱い
                continue;
            }
            if (files[i].isDirectory())
                ret.addElement(doDir(files[i]));
        }
        return ret;
    }

    private static Element doDir(File dir) {
        boolean chaseXMLexists = false;
        Element element = null;
        File[] files = dir.listFiles();
        String chaseXML = "phase-viewer.xml";
        for (int i = 0; i < files.length; i++) {
            if (files[i].getName().equals(chaseXML))
                chaseXMLexists = true;
        }
        if (chaseXMLexists) {
            Document document = ciss.phase_viewer.jdom.XMLUtils
                    .getDocumentFromFile(new File(dir.getAbsolutePath()
                            + System.getProperty("file.separator") + chaseXML));
            Element rootElement = document.getRootElement();
            Element typeElement = rootElement.getChild("type");
            if (typeElement != null) {
                element = getSubProjectElement(typeElement.getTextTrim(), dir);
            }
        } else {
            element = getProjectElement(dir);
        }

        if (element == null)
            return null;

        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                Element eledir = doDir(files[i]);
                if (eledir != null)
                    element.addContent(eledir);
            }
        }

        return element;
    }

    private static Element getProjectElement(File currDir) {
        Element elem = new Element("project");
        elem.setAttribute("display", "true");
        boolean hasSub = (currDir.listFiles() != null && currDir.listFiles().length != 0);
        elem.setAttribute("hassub", String.valueOf(hasSub));
        elem.setAttribute("subident", "subproject");
        elem.setAttribute("shouldbevisible", "false");
        elem.setAttribute("selected", "false");

        String name = MyElement.encode(currDir.getName());
        Element nameElement = new Element("name").setText(name);
        elem.addContent(nameElement);

        Date date = new Date(currDir.lastModified());
        String dateString = MyElement.encode(date.toString());
        Element dateElement = new Element("date").setText(dateString);
        elem.addContent(dateElement);

        Element typeElement = new Element("type").setText("chase project");
        elem.addContent(typeElement);

        String dirString = MyElement.encode(currDir.getAbsolutePath());
        Element dirElement = new Element("directory").setText(dirString);
        elem.addContent(dirElement);

        return elem;
    }

    private static Element getSubProjectElement(String type, File currDir) {
        Element elem = new Element("subproject");
        elem.setAttribute("display", "true");
        elem.setAttribute("shouldbevisible", "false");
        elem.setAttribute("selected", "false");

        String name = MyElement.encode(currDir.getName());
        Element nameElement = new Element("name").setText(name);
        elem.addContent(nameElement);

        Date date = new Date(currDir.lastModified());
        String dateString = MyElement.encode(date.toString());
        Element dateElement = new Element("date").setText(dateString);
        elem.addContent(dateElement);

        Element typeElement = new Element("type").setText(type);
        elem.addContent(typeElement);

        if (type.equals("ekcal") || type.equals("uvsor-epsilon")) {
            String reldir = "";
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new FileReader(
                        currDir.getAbsolutePath()
                                + System.getProperty("file.separator")
                                + "file_names.data"));
                String line = "";
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    if (line.startsWith("F_CHGT")) {
                        String[] foo = line.split("=");
                        reldir = foo[1].replaceAll("'", "");
                        reldir = new File(reldir).getParent();
                        break;
                    }
                }
            } catch (Exception exc) {
            } finally {
                try {
                    reader.close();
                } catch (Exception exc) {
                }
            }
            Element assdir = new Element("associated_directory")
                    .setText(MyElement.encode(reldir));
            assdir.setAttribute("type", "parent");
            elem.addContent(assdir);
        }

        String dirString = MyElement.encode(currDir.getAbsolutePath());
        Element dirElement = new Element("directory").setText(dirString);
        elem.addContent(dirElement);

        Element commentElement = new Element("comment");
        elem.addContent(commentElement);

        return elem;
    }

    public static boolean remove(ProjectDirBrowserNode node) {
        ProjectInfo info = node.getProjectInfo();
        String dir = info.getProjectDirectory();
        boolean ret = true;
        if (new File(dir).exists())
            ret = ciss.phase_viewer.common.Utils
                    .deleteDir(new java.io.File(dir));
        if (ret) {
            node.getDirBrowser().getTree()
                    .scrollPathToVisible(new TreePath(node.getPath()));
            node.getDirBrowser().getTree()
                    .setSelectionPath(new TreePath(node.getPath()));
            node.getDirBrowser().removeCurrentNode();
            if (node.getProjectBrowser() != null
                    && node.getProjectBrowser().isVisible()) {
                node.getProjectBrowser().dispose();
            }
        }
        return ret;
    }

    /*
     * private static void updateElement(Element element, String name, String
     * dir) { Element elemName = element.getChild("name"); Element eleDir =
     * element.getChild("directory"); if ( elemName == null ) { elemName = new
     * Element("name"); element.addContent(elemName); } if ( eleDir == null ) {
     * eleDir = new Element("directory"); element.addContent(eleDir); }
     * elemName.setText(MyElement.encode(name));
     * eleDir.setText(MyElement.encode(dir)); }
     */
}
