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

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;

import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.ListCellRenderer;
import javax.swing.border.TitledBorder;

import org.apache.log4j.Logger;

import Jama.Matrix;
import ciss.phase_viewer.acviewer.fbz.FBZ;
import ciss.phase_viewer.acviewer.fbz.FBZData;
import ciss.phase_viewer.acviewer.fbz.WignerSeitzAttributes;
import ciss.phase_viewer.atomcoord.VolumetricData;
import ciss.phase_viewer.common.InputPanel;
import ciss.phase_viewer.outputinterface.OutputData;
import ciss.phase_viewer.outputinterface.OutputParser;
import ciss.phase_viewer.outputinterface.OutputParserListener;
import ciss.phase_viewer.plugins.projectmanipulator.phase.resultsviewerpanel.tools.BandPlPanel;
import ciss.phase_viewer.primitiveguis.ComboButton;
import ciss.phase_viewer.projectbrowser.ProjectInfo;
import ciss.phase_viewer.projectbrowser.ProjectManipulator;
import ciss.phase_viewer.projectbrowser.tools.LoadingFilePanel;
import ciss.phase_viewer.projectbrowser.tools.NoFilePanel;

/**
 * バンド計算の結果を見たりするパネル.
 * 
 * @author
 */
public class BandPanel extends ProjectManipulator implements
        OutputParserListener, MouseListener, KeyListener {
    private Logger logger = Logger.getLogger(BandPanel.class.getName());

    private OutputParser parser;

    private boolean spin = false;

    /** Creates a new instance of BanedPanel */
    public BandPanel(ProjectInfo projectInfo) {
        super(projectInfo);
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    }

    public void recreate() {
        projectInfo.getOutputInterface().initCache();
        init();
        revalidate();
    }

    private JList bandList;

    private DefaultListModel bandListModel;

    private JList bandListDown;

    private DefaultListModel bandListModelDown;

    private JButton viewbtn;

    public void parseFinished() {
        removeAll();
        JPanel pbtn = new JPanel();
        pbtn.setLayout(new BoxLayout(pbtn, BoxLayout.X_AXIS));

        JPanel button = null;
        if (new Boolean(parser.getGenericData("banddata")[0]).booleanValue()) {
            button = new JPanel();
            button.setBorder(new TitledBorder("band.pl"));
            button.setLayout(new FlowLayout());
            JButton btndospl = new JButton("run band.pl");
            button.add(btndospl);
            ComboButton cbband = new ComboButton(
                    projectInfo.getProjectDirectory(), "band_structure.eps");
            button.add(cbband);
            btndospl.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    bootBandPlPanel();
                }
            });
        }

        File fsfile = new File(projectInfo.getProjectDirectory()
                + System.getProperty("file.separator") + "fs.data");
        JPanel pbi = null;
        if (fsfile.exists()) {
            viewbtn = new JButton("view");
            viewbtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    bootViewer();
                }
            });

            eigenvalues = parser.getOutputData();
            if (parser.getOutputData().length > 1) {
                spin = true;
            }

            fermi = Float.parseFloat(parser.getGenericData("fermi")[0]);
            bandListModel = new DefaultListModel();
            bandList = createBandList(0, bandListModel);

            if (spin) {
                bandListModelDown = new DefaultListModel();
                bandListDown = createBandList(1, bandListModelDown);
            }

            pbi = new JPanel();
            pbi.setBorder(new TitledBorder("reciprocal space"));
            pbi.setLayout(new BoxLayout(pbi, BoxLayout.X_AXIS));
            // pbi.setLayout(new BoxLayout(pbi,BoxLayout.X_AXIS));
            if (!spin) {
                JScrollPane sp = new JScrollPane(bandList);
                sp.setPreferredSize(new Dimension(200, 150));
                // bandList.setPreferredSize(new Dimension(150,50));
                pbi.add(sp);
            } else {
                JScrollPane sp = new JScrollPane(bandList);
                JScrollPane spd = new JScrollPane(bandListDown);
                sp.setPreferredSize(new Dimension(200, 150));
                spd.setPreferredSize(new Dimension(200, 150));
                JTabbedPane tp = new JTabbedPane();
                tp.add("up", sp);
                tp.add("down", spd);
                pbi.add(tp);
            }

            pbi.add(viewbtn);
        }

        if (button != null)
            pbtn.add(button);
        if (pbi != null)
            pbtn.add(pbi);
        add(pbtn);

        JPanel dataPanel = projectInfo.getOutputInterface().getDataPanel(
                "F_ENERG");
        add(dataPanel);

        revalidate();
    }

    private JList createBandList(int ind, DefaultListModel bandListModel) {
        JList bandList = new JList(bandListModel);
        bandList.addKeyListener(this);
        bandList.addMouseListener(this);
        String[][] d2d = eigenvalues[ind].getData2D();
        int numbands = d2d[0].length - 5;
        boolean[] crossesFermi = new boolean[numbands];
        float[] min = new float[numbands];
        float[] max = new float[numbands];
        logger.debug("fermi energy: " + fermi);
        for (int bandindex = 0; bandindex < numbands; bandindex++) {
            float minval = 1000000f;
            float maxval = -1000000f;
            boolean hasValSmallerThanFermi = false;
            boolean hasValLargerThanFermi = false;
            boolean cf = false;
            for (int i = 0; i < d2d.length; i++) {
                float dat = Float.parseFloat(d2d[i][5 + bandindex]);
                if (dat > fermi) {
                    hasValLargerThanFermi = true;
                }
                if (dat < fermi) {
                    hasValSmallerThanFermi = true;
                }
                if (hasValSmallerThanFermi && hasValLargerThanFermi) {
                    cf = true;
                }
                if (dat < minval) {
                    minval = dat;
                }
                if (dat > maxval) {
                    maxval = dat;
                }
            }
            crossesFermi[bandindex] = cf;
            min[bandindex] = minval;
            max[bandindex] = maxval;
            logger.debug("band no. " + bandindex + " crosses fermi: "
                    + crossesFermi[bandindex]);
        }

        bandList.setCellRenderer(new BandCellRenderer(crossesFermi));

        DecimalFormat form = new DecimalFormat("0.000");
        for (int i = 5; i < d2d[0].length; i++) {
            bandListModel.addElement("    " + String.valueOf(i - 4)
                    + "    (min: " + form.format(min[i - 5]) + "  max: "
                    + form.format(max[i - 5]) + ")");
        }

        return bandList;
    }

    private OutputData[] eigenvalues;

    private float fermi;

    private void bootViewer() {
        String fsfile = projectInfo.getProjectDirectory()
                + System.getProperty("file.separator") + "fs.data";
        BufferedReader reader = null;
        double[][] reclat = null;
        VolumetricData[] vdata = null;
        try {
            reader = new BufferedReader(new FileReader(fsfile));
            String line = "";
            String[] b1 = reader.readLine().trim().split("\\s+");
            String[] b2 = reader.readLine().trim().split("\\s+");
            String[] b3 = reader.readLine().trim().split("\\s+");
            int[] ndiv = new int[3];
            ndiv[0] = Integer.parseInt(b1[0]);
            ndiv[1] = Integer.parseInt(b2[0]);
            ndiv[2] = Integer.parseInt(b3[0]);

            float[][] delta = new float[3][3];
            for (int i = 0; i < 3; i++) {
                delta[0][i] = Float.parseFloat(b1[i + 1]);
                delta[1][i] = Float.parseFloat(b2[i + 1]);
                delta[2][i] = Float.parseFloat(b3[i + 1]);
            }

            reclat = new double[3][3];
            for (int i = 0; i < 3; i++) {
                reclat[i][0] = (double) delta[i][0] * (ndiv[i] - 1);
                reclat[i][1] = (double) delta[i][1] * (ndiv[i] - 1);
                reclat[i][2] = (double) delta[i][2] * (ndiv[i] - 1);
                logger.debug("rec. lat: " + reclat[i][0] + ", " + reclat[i][1]
                        + ", " + reclat[i][2]);
            }

            int ntot = ndiv[0] * ndiv[1] * ndiv[2];
            boolean[] foo = new boolean[ntot];
            int ii = 0;
            while ((line = reader.readLine()) != null) {
                String[] ar = line.trim().split("\\s+");
                foo[ii] = new Boolean(ar[3]).booleanValue();
                ii++;
            }
            ii = 0;

            int[][] bindeces = null;
            if (spin) {
                bindeces = new int[2][];
                bindeces[0] = bandList.getSelectedIndices();
                bindeces[1] = bandListDown.getSelectedIndices();
                vdata = new VolumetricData[bindeces[0].length
                        + bindeces[1].length];
            } else {
                bindeces = new int[1][];
                bindeces[0] = bandList.getSelectedIndices();
                vdata = new VolumetricData[bindeces[0].length];
            }

            if (vdata.length == 0) {
                logger.info("select band indices.");
                return;
            }

            int iband = 0;
            for (int iind = 0; iind < bindeces.length; iind++) {
                int[] bandIndeces = bindeces[iind];
                logger.debug("iind: " + iind);
                for (int ba = 0; ba < bandIndeces.length; ba++) {
                    int bandIndex = bandIndeces[ba];
                    String[][] data = parser.getOutputData()[iind].getData2D();
                    float[] eigenValues = new float[data.length];
                    for (int i = 0; i < data.length; i++) {
                        String[] datum = data[i];
                        eigenValues[i] = Float.parseFloat(datum[5 + bandIndex]);
                    }

                    float[] eigen = new float[ntot];
                    int count = 0;
                    int eigCount = 0;
                    // float [] eig1d = new float [lndiv[0]*lndiv[1]*lndiv[2]];
                    // for ( int i=0 ; i<eig1d.length ; i++ ) {
                    // eig1d[i] = meaninglessValue;
                    // }

                    for (int i = 0; i < ntot; i++) {
                        if (foo[i]) {
                            eigen[count] = eigenValues[eigCount];
                            count++;
                            eigCount++;
                        } else {
                            eigen[count] = meaninglessValue;
                            count++;
                        }
                    }

                    // count=0;
                    // for ( int i=0; i<ndiv[0] ; i++ ) {
                    // for ( int j=0 ; j<ndiv[1] ; j++ ) {
                    // for ( int k=0 ; k<ndiv[2] ; k++ ) {
                    // // int index = ndiv[1]*ndiv[2]*i+ndiv[2]*j+k;
                    // int x=i+ndiv[0]-1;
                    // int y=j+ndiv[1]-1;
                    // int z=k+ndiv[2]-1;
                    // int ind =
                    // Math.abs(i)*ndiv[1]*ndiv[2]+Math.abs(j)*ndiv[2]+Math.abs(k);
                    // int newind = x*lndiv[1]*lndiv[2]+y*lndiv[2]+z;
                    // eig1d[newind] = eigen[ind];
                    // if ( eigen[ind] != meaninglessValue ) {
                    // // System.out.println("coor 1st q: "+x+", "+y+", "+z);
                    // }
                    // count++;
                    // }
                    // }
                    // }
                    //
                    // count=0;
                    // Point3f[] kpts = new Point3f[ntot];
                    // Vector kpoints = new Vector();
                    // Vector eigens = new Vector();
                    // Point3f meaninglessKpoint = new
                    // Point3f(meaninglessValue,meaninglessValue,meaninglessValue);
                    // for ( int i=0 ; i<ndiv[0] ; i++ ) {
                    // for ( int j=0 ; j<ndiv[1] ; j++ ) {
                    // for ( int k=0 ; k<ndiv[2] ; k++ ) {
                    // if ( eigen[count] != meaninglessValue ) {
                    // float [] kp = new float [3];
                    // for ( int l=0 ; l<3 ; l++ ) {
                    // kp[l] = delta[0][l]*i+delta[1][l]*j+delta[2][l]*k;
                    // }
                    // Point3f kps = new Point3f(kp);
                    // kpts[count] = kps;
                    // kpoints.addElement(kps);
                    // eigens.addElement(new Float(eigen[count]));
                    // } else {
                    // kpts[count] = meaninglessKpoint;
                    // kpoints.addElement(meaninglessKpoint);
                    // eigens.addElement(new Float(meaninglessValue));
                    // }
                    // count++;
                    // }
                    // }
                    // }

                    float[] ori = new float[3];
                    for (int i = 0; i < 3; i++) {
                        ori[i] = (float) (reclat[0][i] * 0.5)
                                + (float) (reclat[1][i] * 0.5)
                                + (float) (reclat[2][i] * 0.5);
                        // ori[i] = (float)
                        // (reclat[0][i])+(float)(reclat[1][i])+(float)(reclat[2][i]);
                        // ori[i] = 0f;
                    }
                    //
                    // Plane [] pl = new Plane [3];
                    // Plane plane01 = new Plane();
                    // Plane plane02 = new Plane();
                    // Plane plane12 = new Plane();
                    // pl[0] = plane01;
                    // pl[1] = plane02;
                    // pl[2] = plane12;
                    // float [] norm01 =
                    // VectorOperations.getNormalVector(freclat[0],freclat[1]);
                    // float [] norm02 =
                    // VectorOperations.getNormalVector(freclat[0],freclat[2]);
                    // float [] norm12 =
                    // VectorOperations.getNormalVector(freclat[1],freclat[2]);
                    // plane01.setNormalVector(new Point3f(norm01));
                    // plane02.setNormalVector(new Point3f(norm02));
                    // plane12.setNormalVector(new Point3f(norm12));
                    //
                    // float [][] nms = new float [3][];
                    // nms[0] = norm01;
                    // nms[1] = norm02;
                    // nms[2] = norm12;
                    // float small = 0.00001f;
                    // for ( int i=0 ; i<3 ; i++ ) {
                    // Point3f [] kpos = new Point3f[kpoints.size()];
                    // kpoints.copyInto(kpos);
                    // Float [] eis = new Float[eigens.size()];
                    // eigens.copyInto(eis);
                    // for ( int j=0 ; j<kpos.length ; j++ ) {
                    // Point3f p3 = kpos[j];
                    // if ( p3.epsilonEquals(meaninglessKpoint,0f) ) {
                    // continue;
                    // }
                    // float d1 = -pl[i].getDistanceFrom(p3);
                    // if ( Math.abs(d1) < small ) {
                    // continue;
                    // }
                    // Point3f newkpoint = new Point3f(p3);
                    // // addedPoints.addElement(newkpoint);
                    // newkpoint.x += 2*nms[i][0]*d1;
                    // newkpoint.y += 2*nms[i][1]*d1;
                    // newkpoint.z += 2*nms[i][2]*d1;
                    // int [] coor = getKpointCoefficient(delta,new float
                    // []{newkpoint.x,newkpoint.y,newkpoint.z});
                    //
                    // float [] kp = new float [3];
                    // for ( int l=0 ; l<3 ; l++ ) {
                    // kp[l] =
                    // delta[0][l]*coor[0]+delta[1][l]*coor[1]+delta[2][l]*coor[2];
                    // }
                    // Point3f kps = new Point3f(kp);
                    // // System.out.println("origk: "+newkpoint);
                    // // System.out.println("newk: "+kps);
                    // // addedPoints.addElement(kps);
                    //
                    // int xx = coor[0]+ndiv[0];
                    // int yy = coor[1]+ndiv[1];
                    // int zz = coor[2]+ndiv[2];
                    // int newind =
                    // (xx-1)*lndiv[2]*lndiv[1]+(yy-1)*lndiv[2]+(zz-1);
                    // // System.out.println("coor: "+x+", "+y+", "+z);
                    // eig1d[newind] = eis[j].floatValue();
                    // // eigens.addElement(eis[j]);
                    // // kpoints.addElement(newkpoint);
                    // }
                    // }
                    // count = 0;
                    // vdata = new VolumetricData(eig1d,lndiv,delta,ori);
                    vdata[iband] = new VolumetricData(eigen, ndiv, delta, ori);
                    vdata[iband].setInterpolationScheme(VolumetricData.LINEAR);
                    iband++;
                }
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException ioe) {
            }
        }

        FBZData dat = new FBZData();
        dat.reciprocalLattice = reclat;
        dat.baseDir = projectInfo.getProjectDirectory();
        dat.fermiEnergy = fermi;
        dat.eigenValues = vdata;
        FBZ fbz = new FBZ(dat);

        WignerSeitzAttributes wsattrs = fbz.getFBZ().getAttributes();
    }

    private float meaninglessValue = VolumetricData.meaninglessValue;

    private int[] getKpointCoefficient(float[][] reclat, float[] kpts) {
        double[][] tmprec = new double[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                tmprec[i][j] = (double) reclat[j][i];
            }
        }
        Matrix B = new Matrix(tmprec);
        double[][] tmpmat = new double[3][1];
        tmpmat[0][0] = (double) kpts[0];
        tmpmat[1][0] = (double) kpts[1];
        tmpmat[2][0] = (double) kpts[2];
        Matrix K = new Matrix(tmpmat);
        Matrix S = B.solve(K);
        int x = (Math.round((float) S.get(0, 0)));
        int y = (Math.round((float) S.get(1, 0)));
        int z = (Math.round((float) S.get(2, 0)));
        return new int[] { x, y, z };
    }

    public void initializeProject() {
    }

    public void init() {
        removeAll();
        String fname = projectInfo.getProjectDirectory()
                + System.getProperty("file.separator")
                + projectInfo.getChaseFileManager().getFile("F_ENERG")
                        .getFileName();
        if (!new File(fname).exists()) {
            add(new NoFilePanel("F_ENERG"));
            return;
        }
        add(new LoadingFilePanel("F_ENERG"));
        if (parser == null) {
            parser = projectInfo.getOutputInterface()
                    .getParser("F_ENERG", this);
        }
        parser.setParsed(false);
        parser.doParse();
    }

    private JPanel createBandPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        JPanel panelmode = new JPanel();
        panelmode.setLayout(new BoxLayout(panelmode, BoxLayout.X_AXIS));
        String[] modes = { "total", "atom", "layer" };
        JComboBox checkMode = new JComboBox(modes);
        InputPanel width = new InputPanel("width", 0, 5);
        JCheckBox checkFermi = new JCheckBox("with_fermi");
        JCheckBox checkColor = new JCheckBox("");
        return panel;
    }

    private void bootBandPlPanel() {
        String dirString = projectInfo.getProjectDirectory();
        String banddata = projectInfo.getChaseFileManager().getFile("F_ENERG")
                .getFileName();
        BandPlPanel bpp = new BandPlPanel("run band.pl", dirString, banddata);
        bpp.setVisible(true);
    }

    public void keyTyped(KeyEvent e) {
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            bootViewer();
        }
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
            bootViewer();
        }
    }

    class BandCellRenderer extends JLabel implements ListCellRenderer {
        private int dataIndex = 0;

        private boolean[] crossesFermi;

        BandCellRenderer() {
            setOpaque(true);
        }

        BandCellRenderer(boolean[] crossesFermi) {
            this.crossesFermi = crossesFermi;
            setOpaque(true);
        }

        public Component getListCellRendererComponent(JList list, Object value,
                int index, boolean isSelected, boolean cellHasFocus) {
            if (crossesFermi[index]) {
                setForeground(Color.red);
            } else {
                setForeground(Color.black);
            }
            setText(value.toString());
            setBackground(isSelected ? Color.blue : Color.white);
            return this;
        }
    }

}
