/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : Restorer.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.
!
!=======================================================================
 */
/**
 * PHASE-Viewerの「プロジェクト情報」などが記録されたmy_projects.xmlファイルをディレクトリー/ファイル構成から再構築する．
 * （完璧に再現できるとは限らない）
 */
package ciss.phase_viewer.projectbrowser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Calendar;

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

import ciss.phase_viewer.inputinterface.DataManager;
import ciss.phase_viewer.inputinterface.InputInterface;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.jdom.XMLUtils;

public class Restorer {
    private static Logger logger = Logger.getLogger(Restorer.class.getName());
    private static File rootDirectory;

    private static final String PROJECT = "project";
    private static final String SUBPROJECT = "subproject";

    private static final String TRUE = "true";
    private static final String FALSE = "false";
    private static final String DISPLAY = "display";
    private static final String HASSUB = "hassub";
    private static final String SUBIDENT = "subident";
    private static final String SHOULDBEVISIBLE = "shouldbevisible";
    private static final String SELECTED = "selected";
    private static final String FS = System.getProperty("file.separator");

    private static final String PHASE = "phase";
    private static final String EKCAL = "ekcal";
    private static final String UVSOR = "uvsor-epsilon";

    public static boolean restore(File targetDir, File outFile) {
        if (!targetDir.exists() || !targetDir.isDirectory()) {
            logger.error("Specify an existing directory!");
            return false;
        }

        Element rootElement = new Element("projects");
        initRoot(targetDir, rootElement);

        File[] files = targetDir.listFiles();
        for (File file : files) {
            if (!file.isDirectory())
                continue;
            restoreCore(file, rootElement);
        }

        dumpProjectSpecFile(outFile, rootElement);
        return true;
    }

    private static void initRoot(File targetDir, Element rootElement) {
        rootDirectory = targetDir;
        rootElement.setAttribute(new Attribute(DISPLAY, TRUE));
        rootElement.setAttribute(new Attribute(HASSUB, TRUE));
        rootElement.setAttribute(new Attribute(SUBIDENT, PROJECT));
        rootElement.setAttribute(new Attribute(SHOULDBEVISIBLE, TRUE));
        rootElement.setAttribute(new Attribute(SELECTED, FALSE));
        Element name = new Element("name").setText(MyElement.encode(targetDir
                .getName()));
        Element type = new Element("type").setText("root");
        Element dir = new Element("directory").setText(MyElement
                .encode(targetDir.getAbsolutePath()));
        rootElement.addContent(name);
        rootElement.addContent(type);
        rootElement.addContent(dir);
    }

    private static void restoreCore(File targetDir, Element parentElement) {
        File[] files = targetDir.listFiles();
        if (files == null || files.length == 0) {
            return;
        }

        // the new element
        Element element = null;

        if (isProject(targetDir) && !isPPDirectory(targetDir))
            element = getProjectElement(targetDir);
        else
            element = getSubProjectElement(targetDir);

        // add the new element to the parent element.
        parentElement.addContent(element);

        // apply the current method recursively to the sub directories
        for (File file : files)
            if (file.isDirectory())
                restoreCore(file, element);
    }

    private static Element getProjectElement(File targetDir) {
        Element ret = new Element(PROJECT);
        ret.setAttribute(new Attribute(DISPLAY, TRUE));
        ret.setAttribute(new Attribute(HASSUB, TRUE));
        ret.setAttribute(new Attribute(SUBIDENT, SUBPROJECT));
        ret.setAttribute(new Attribute(SHOULDBEVISIBLE, FALSE));
        ret.setAttribute(new Attribute(SELECTED, FALSE));
        Element name = new Element("name").setText(MyElement.encode(targetDir
                .getName()));
        Element typ = new Element("type").setText("chase project");

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(targetDir.lastModified());
        Element time = new Element("date").setText(MyElement.encode(calendar
                .getTime().toString()));
        Element dir = new Element("directory").setText(MyElement
                .encode(targetDir.getAbsolutePath()));
        ret.addContent(name);
        ret.addContent(typ);
        ret.addContent(time);
        ret.addContent(dir);
        // the 'data' project must not be editable
        if (isDataDirectory(targetDir))
            ret.addContent(new Element("editable").setText("false"));
        return ret;
    }

    private static Element getSubProjectElement(File targetDir) {
        // build the element for the 'sub-project' directory.
        // a 'sub-project' directory is a directory under which the
        // calculation files reside.

        Element ret = new Element(SUBPROJECT);
        ret.setAttribute(new Attribute(DISPLAY, TRUE));
        ret.setAttribute(new Attribute(SHOULDBEVISIBLE, FALSE));
        ret.setAttribute(new Attribute(SELECTED, FALSE));

        Element name = new Element("name").setText(MyElement.encode(targetDir
                .getName()));
        String fname = targetDir.getAbsolutePath() + FS + "file_names.data";
        String nfinp = getFilePathFromFileNamesData(fname, "F_INP",
                "nfinp.data");
        InputInterface inpint = getInputInterface(targetDir.getAbsolutePath()
                + FS + nfinp);
        if (inpint == null)
            return ret;

        // resolve the 'type' of the sub-directory
        String cond = inpint
                .getInputInterfacePrimitiveEntry("control.condition")
                .getValue().trim();
        boolean isPhase = true;
        if (cond.equals("2") || cond.equals("3")
                || cond.toLowerCase().startsWith("fixed_charge"))
            isPhase = false;

        String subprojType = PHASE;
        String associatedDirectory = null;

        if (!isPhase) {
            if (inpint.getInputInterfacePrimitiveEntry("epsilon.sw_epsilon")
                    .getValue().length() == 0)
                subprojType = EKCAL;
            else
                subprojType = UVSOR;

            // resolve the associated SCF directory for fixed-charge
            // calculations
            String nfchgt = getFilePathFromFileNamesData(fname, "F_CHGT",
                    "nfchgt.data");
            if (nfchgt != null)
                associatedDirectory = new File(nfchgt).getParent();
        }

        // we are now ready to build the element corresponding to the
        // current sub-project

        // a 'PP' subproject must not be editable
        if (isPPDirectory(targetDir)) {
            subprojType = "pp";
            ret.addContent(new Element("editable").setText("false"));
        }

        Element typ = new Element("type").setText(subprojType);

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(targetDir.lastModified());
        Element dat = new Element("date").setText(MyElement.encode(calendar
                .getTime().toString()));

        ret.addContent(name);
        ret.addContent(typ);
        ret.addContent(dat);
        if (!isPhase && associatedDirectory != null) {
            Element ass = new Element("associated_directory");
            ass.setAttribute(new Attribute("type", "parent"));
            ass.setText(MyElement.encode(associatedDirectory));
            ret.addContent(ass);
        }
        return ret;
    }

    private static InputInterface getInputInterface(String nfinp) {
        DataManager dm = new DataManager(nfinp);
        try {
            dm.parse();
        } catch (Exception exc) {
            return null;
        }

        InputInterface inputInterface = dm.getInputInterface();
        return inputInterface;
    }

    private static String getFilePathFromFileNamesData(String fileNamesData,
            String ident, String defaultName) {
        BufferedReader reader = null;
        String ret = defaultName;
        try {
            reader = new BufferedReader(new FileReader(fileNamesData));
            String line = "";
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("&"))
                    continue;

                String[] lines = line.split("=");
                if (lines.length < 2)
                    continue;

                String fname = lines[1].trim();
                if (fname.length() <= 2)
                    continue;

                if (lines[0].trim().equals(ident)) {
                    ret = fname.substring(1, fname.length() - 1);
                    break;
                }
            }
        } catch (IOException ioe) {
            return ret;
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
            }
        }
        return ret;
    }

    private static boolean isProject(File dir) {
        if (!dir.isDirectory())
            return false;

        File[] files = dir.listFiles();
        boolean filesAreDirectories = true;
        boolean fileNamesDataExists = false;
        for (int i = 0; i < files.length; i++) {
            if (!files[i].isDirectory())
                filesAreDirectories = false;

            if (files[i].getName().equals("file_names.data"))
                fileNamesDataExists = true;
        }

        if (filesAreDirectories || !fileNamesDataExists)
            return true;

        return false;
    }

    private static boolean isDataDirectory(File dir) {
        return dir
                .getAbsolutePath()
                .toLowerCase()
                .equals((rootDirectory.getAbsolutePath() + FS + "data")
                        .toLowerCase());
    }

    private static boolean isPPDirectory(File dir) {
        return dir
                .getAbsolutePath()
                .toLowerCase()
                .equals((rootDirectory.getAbsolutePath() + FS + "data" + FS + "pseudopotential")
                        .toLowerCase());
    }

    private static void dumpProjectSpecFile(File file, Element rootElement) {
        Document doc = new Document();
        doc.setRootElement(rootElement);
        XMLUtils.saveDocumentTo(doc, file);
    }

}
