/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/02/06, 19:26
!  AUTHOR(S): KOGA, Junichiro
!  File : BandParser.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.plugins.projectmanipulator.ekcal.resultsviewerpanel.parsers;

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

import org.apache.log4j.Logger;

import ciss.phase_viewer.mainpanel.ProgressInfo;
import ciss.phase_viewer.outputinterface.OutputData;
import ciss.phase_viewer.outputinterface.OutputParser;

/**
 * PHASE, EKCALの「F_ENERGファイル」のパーサー.
 * 
 * @author
 */
public class BandParser extends OutputParser implements ProgressInfo {
    private Logger logger = Logger.getLogger(BandParser.class.getName());

    private OutputData outDataUP;

    private OutputData outDataDOWN;

    /**
     * @param fileName
     *            F_ENERGファイルへのパス.
     */
    public BandParser(String fileName) {
        super(fileName);
    }

    private final String BANDKPTIN = "bandkpt.in";

    private boolean bbandkptin = false;

    private double[] b1 = new double[3];

    private double[] b2 = new double[3];

    private double[] b3 = new double[3];

    protected boolean parse() {
        init();
        String dir = new File(fileName).getParent();
        String bandinp = dir + System.getProperty("file.separator") + BANDKPTIN;
        if (new File(bandinp).exists()) {
            bbandkptin = true;
        }

        if (bbandkptin) {
            BufferedReader rd = null;
            try {
                rd = new BufferedReader(new FileReader(bandinp));
                rd.readLine();
                String[] sb1 = rd.readLine().trim().split("\\s+");
                String[] sb2 = rd.readLine().trim().split("\\s+");
                String[] sb3 = rd.readLine().trim().split("\\s+");
                for (int i = 0; i < 3; i++) {
                    b1[i] = Double.parseDouble(sb1[i]);
                    b2[i] = Double.parseDouble(sb2[i]);
                    b3[i] = Double.parseDouble(sb3[i]);
                }
            } catch (Exception exc) {
                bbandkptin = false;
                logger.warn("failed to read bandkpt.in");
                exc.printStackTrace();
            } finally {
                try {
                    rd.close();
                } catch (Exception exc) {
                }
            }
        }

        if (reader == null) {
            return false;
        }
        String line = "";
        try {
            while ((line = reader.readLine()) != null) {
                boolean foo = parseLine(line.trim());
                if (!foo) {
                    logger.error("failed parse of line: " + line.trim());
                    logger.error("line number: " + reader.getLineNumber());
                    return false;
                }
            }

            String[][] up = null;
            if (bandVectorUP.size() != 0) {
                up = new String[bandVectorUP.size()][idents.length];
                for (int i = 0; i < bandVectorUP.size(); i++) {
                    up[i] = (String[]) bandVectorUP.get(i);
                }
                String name = "band";
                if (nspin == 2) {
                    name = "band (UP)";
                }
                outDataUP = new OutputData(name, "energy eigenvalues", idents,
                        up, description);
                outDataUP.setFileName(fileName);
                addOutputData(outDataUP);
            }

            String[][] down = null;
            if (bandVectorDOWN.size() != 0) {
                down = new String[bandVectorDOWN.size()][idents.length];
                for (int i = 0; i < bandVectorDOWN.size(); i++) {
                    down[i] = (String[]) bandVectorDOWN.get(i);
                }
                String name = "band (DOWN)";
                outDataDOWN = new OutputData(name, "energy eigenvalues",
                        idents, down, description);
                outDataDOWN.setFileName(fileName);
                addOutputData(outDataDOWN);
            }
        } catch (IOException ioe) {
            logger.error("failed parse of F_ENERG file");
            ioe.printStackTrace();
            return false;
        } finally {
            isDone = true;
            try {
                reader.close();
            } catch (Exception exc) {
            }
        }

        return true;
    }

    public String getCurrentMessage() {
        return null;
    }

    private void init() {
        bandVectorUP = new Vector();
        bandVectorDOWN = new Vector();
        description = "";
        prev_k = new double[] { 0.0, 0.0, 0.0 };
        korigin = null;
        dkorigin = null;
    }

    private Vector bandVectorUP = new Vector();

    private Vector bandVectorDOWN = new Vector();

    private int num_kpoints = -1;

    private int num_bands = -1;

    private int nspin = -1;

    private int numeig;

    private String fermi = "(not available)";

    private String[] idents;

    private final String K_START = "ik =";

    private final String NUM_KPOINTS = "num_kpoints";

    private final String NUM_BANDS = "num_bands";

    private final String NUM_SPIN = "nspin";

    private final String FERMI = "Fermi";

    private final String VALENCE = "Valence";

    private final String DATA_START_TAG = "=== energy_eigen_values ===";

    private boolean init_done = false;

    private String description = "";

    private double dk = 0.0;

    private double[] prev_k = { 0.0, 0.0, 0.0 };

    private double[] korigin = null;

    private Double dkorigin = null;

    private double dkp = 0.0;
    private boolean inData = false;

    private boolean parseLine(String line) {
        if (num_bands > 0 && nspin > 0 && !init_done) {
            idents = new String[num_bands + 5];
            idents[0] = "ik";
            idents[1] = "kx";
            idents[2] = "ky";
            idents[3] = "kz";
            idents[4] = "|k|";
            for (int i = 0; i < num_bands; i++) {
                idents[5 + i] = "eig" + String.valueOf(i + 1);
            }
            init_done = true;
        }

        if (line.startsWith(FERMI) || line.startsWith(VALENCE)) {
            String[] strfermi = line.split("=");
            if (strfermi != null && strfermi.length >= 2) {
                fermi = strfermi[1].trim();
                description += line + System.getProperty("line.separator");
            }
        }

        if (line.startsWith(NUM_KPOINTS)) {
            String[] kpts = line.split("=");
            if (kpts == null || kpts.length < 2) {
                return false;
            }
            try {
                num_kpoints = Integer.parseInt(kpts[1].trim());
            } catch (Exception exc) {
                return false;
            }
            description += line + System.getProperty("line.separator");
        }
        if (line.startsWith(NUM_BANDS)) {
            String[] nbands = line.split("=");
            if (nbands == null || nbands.length < 2) {
                return false;
            }
            try {
                num_bands = Integer.parseInt(nbands[1].trim());
            } catch (Exception exc) {
                return false;
            }
            description += line + System.getProperty("line.separator");
        }
        if (line.startsWith(NUM_SPIN)) {
            String[] nsp = line.split("=");
            if (nsp == null || nsp.length < 2) {
                return false;
            }
            try {
                nspin = Integer.parseInt(nsp[1].trim());
            } catch (Exception exc) {
                return false;
            }
            description += line + System.getProperty("line.separator");
        }

        if (line.startsWith(DATA_START_TAG)) {
            if (!inData) {
                inData = true;
                return true;
            }
        }
        if (inData) {
            String[] data = new String[num_bands + 5];
            if (!init_done) {
                logger.error("header unparsed... can't proceed");
                return false;
            }
            String[] array = line.split("\\s+");
            if (array == null || array.length < 7) {
                return false;
            }

            data[0] = array[2];
            data[1] = array[4];
            data[2] = array[5];
            data[3] = array[6];
            if (nspin == 2) {
                data[3] = array[6].substring(0, array[6].length() - 2);
            }
            if (bbandkptin) {
                try {
                    double dkx = Double.parseDouble(data[1].trim());
                    double dky = Double.parseDouble(data[2].trim());
                    double dkz = Double.parseDouble(data[3].trim());
                    if (korigin == null) {
                        korigin = new double[3];
                        korigin[0] = dkx;
                        korigin[1] = dky;
                        korigin[2] = dkz;
                    }
                    double[] dkc = new double[3];
                    dkc[0] = b1[0] * (prev_k[0] - dkx) + b1[1]
                            * (prev_k[1] - dky) + b1[2] * (prev_k[2] - dkz);
                    dkc[1] = b2[0] * (prev_k[0] - dkx) + b2[1]
                            * (prev_k[1] - dky) + b2[2] * (prev_k[2] - dkz);
                    dkc[2] = b3[0] * (prev_k[0] - dkx) + b3[1]
                            * (prev_k[1] - dky) + b3[2] * (prev_k[2] - dkz);
                    dk = dkp
                            + Math.sqrt(Math.pow(dkc[0], 2)
                                    + Math.pow(dkc[1], 2) + Math.pow(dkc[2], 2));
                    if (dkorigin == null) {
                        dkorigin = new Double(dk);
                    }
                    logger.debug("dk, dkorigin: " + dk + " "
                            + dkorigin.doubleValue());
                    double dkk = dk - dkorigin.doubleValue();

                    data[4] = String.valueOf(dkk);
                    prev_k[0] = Double.parseDouble(data[1].trim());
                    prev_k[1] = Double.parseDouble(data[2].trim());
                    prev_k[2] = Double.parseDouble(data[3].trim());
                    dkp = dk;
                } catch (Exception exc) {
                    logger.error("one of the k-points was not a number");
                    exc.printStackTrace();
                    // return false;
                }
            } else {
                data[4] = "(bvec not available)";
            }

            int count = 0;
            String eig = "";
            boolean isUp = true;
            if (array.length >= 8 && nspin == 2) {
                isUp = array[7].trim().equals("UP");
            }
            try {
                while ((eig = reader.readLine()) != null) {
                    String[] eigs = eig.trim().split("\\s+");
                    for (int i = 0; i < eigs.length; i++) {
                        data[5 + count] = eigs[i].trim();
                        count++;
                        if (count == num_bands) {
                            if (isUp) {
                                logger.debug("adding 'up'");
                                bandVectorUP.addElement(data);
                            } else {
                                logger.debug("adding 'down'");
                                bandVectorDOWN.addElement(data);
                            }
                            inData = false;
                            return true;
                        }
                    }
                }
            } catch (Exception exc) {
                exc.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * 引数に"fermi"とタイプすればFermiエネルギーをゲットできる. "banddata"とすると, バンド情報を持つ場合"true"
     * 
     * @param key
     *            "fermi"でFermiエネルギー, banddataでバンド情報を持っている場合"true",
     *            そうでない場合"false"を返す,
     * @return 第ゼロ要素がフェルミエネルギー, またはtrue, false
     */
    public String[] getGenericData(String key) {
        if (key.toLowerCase().startsWith("fermi")) {
            return new String[] { fermi };
        }
        if (key.toLowerCase().startsWith("banddata")) {
            return new String[] { String.valueOf(bbandkptin) };
        }
        return null;
    }

}
