package org.spview.filehandler;
/*
 * Class for experiment file.
 */

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JOptionPane;

import org.spview.point.ExpJsynPoint;
import org.spview.point.ExpXPoint;

////////////////////////////////////////////////////////////////////

/**
 * This class defines experimental data.
 */
public class ExpFile {

    private final String name; // file name
    private final String lnsep; // line separator
    private double[] x; // X data
    private double[] y; // Y data
    private String[] sdobs; // frequency and intensity standard deviations string
    private ArrayList<ExpJsynPoint>[] alejsynpt; // frequency mark, intensity mark, assignment, associated pred, comment
    private String[] exasg; // EXASG
    private int nbxy; // nb of points
    private String str; // to read file
    private BufferedReader br;

////////////////////////////////////////////////////////////////////

    /**
     * Construct a new ExpFile.
     *
     * @param cname name of the file
     */
    public ExpFile(String cname) {

        name = cname; // file name

        nbxy = 0; // nb of points
        lnsep = System.getProperty("line.separator");
    }

////////////////////////////////////////////////////////////////////

    /**
     * Read file.
     * <br> validity is tested following FORMAT 3000 of eq_tds.f and eq_int.f (assignments.t)
     */
    @SuppressWarnings("unchecked")
    public boolean read() {

        double cx;                                                   // to keep X
        double cy;                                                   // to keep Y
        // to keep points, unknown nb of points
        ArrayList<String> alnuo = new ArrayList<>();                         // line number
        ArrayList<Double> alx = new ArrayList<>();                         // frequency
        ArrayList<String> alfaso = new ArrayList<>();                        // frequency mark
        ArrayList<Double> aly = new ArrayList<>();                         // intensity
        ArrayList<String> alsaso = new ArrayList<>();                         // intensity mark
        ArrayList<String> alsdobs = new ArrayList<>();                         // frequency and intensity standard deviations string
        ArrayList<String> aljsyn = new ArrayList<>();                         // assignment
        ArrayList<String> alexasg = new ArrayList<>();                         // EXASG
        ArrayList<String> alcomm = new ArrayList<>();                         // comment
        String cfaso;                                                // current frequency mark
        String csaso;                                                // current intensity mark
        String cjsyn;                                                // current assignment
        int strl;                                                 // line length
        try {
            br = new BufferedReader(new FileReader(name));  // open
            while ((str = br.readLine()) != null) {                 // line read
                strl = str.length();                                 // line length
                if (strl < 31) {                                    // line too short
                    continue;                                        // skip it
                }
                try {
                    cx = Double.parseDouble(str.substring(6, 18));        // Double for X
                    cy = Double.parseDouble(str.substring(20, 31));       // Double for Y
                } catch (NumberFormatException e) {                    // format error
                    continue;                                        // skip the line
                }
                if (str.substring(1, 6).trim().length() == 0) {      // line number has to be defined
                    invalid("empty line number");
                    return false;
                }
                try {
                    Integer.parseInt(str.substring(1, 6).trim());  // validity test
                } catch (NumberFormatException e) {                    // format error
                    invalid("not an integer in >>>" + str.substring(1, 6) + "<<<");
                    return false;                                    // invalid read
                }
                // keep it
                alnuo.add(str.substring(1, 6));                       // line number
                alx.add(cx);                                         // frequency
                cfaso = str.substring(19, 20);                        // frequency mark
                aly.add(cy);                                         // intensity
                if (strl < 103) {
                    // complete with spaces up to 103 characters
                    str = (str +
                            "          " +
                            "          " +
                            "          " +
                            "          " +
                            "          " +
                            "          " +
                            "          " +
                            "  ").substring(0, 103);
                }
                csaso = str.substring(32, 33);                        // intensity mark
                // standard deviations
                if (str.substring(33, 43).trim().length() != 0) {
                    // frequency standard deviation
                    try {
                        Double.valueOf(str.substring(33, 43));  // validity test
                    } catch (NumberFormatException e) {                // format error
                        invalid("not a double in >>>" + str.substring(33, 43) + "<<<");
                        return false;                                // invalid read
                    }
                }
                if (str.substring(43, 49).trim().length() != 0) {
                    // intensity standard deviation
                    try {
                        Double.valueOf(str.substring(43, 49));  // validity test
                    } catch (NumberFormatException e) {                // format error
                        invalid("not a double in >>>" + str.substring(43, 49) + "<<<");
                        return false;                                // invalid read
                    }
                }
                String cstr = str.substring(33, 49);
                if (cstr.trim().length() == 0) {
                    // shrink if empty
                    cstr = "";
                }
                alsdobs.add(cstr);
                // assignment
                cjsyn = str.substring(51, 72);
                if (cjsyn.trim().length() == 0) {
                    // shrink if empty
                    cjsyn = "";
                }
                //  assignment has to be single
                if (cjsyn.trim().length() != 0 &&                    // not empty and
                        aljsyn.contains(cjsyn)) {                  // found
                    JOptionPane.showMessageDialog(null, cjsyn + lnsep +
                            "Assignment duplicated in file" + lnsep +
                            name);
                    return false;                                    // invalid read
                }
                // frequency mark, intensity mark, assignment test
                if (cjsyn.trim().length() == 0 && !(cfaso.equals(" ") && csaso.equals(" "))) {  // no assignment => no freq or int mark
                    invalid("no assignment but a frequency and/or an intensity mark");
                    return false;                                    // invalid read
                }
                if (cjsyn.trim().length() != 0 && (cfaso.equals(" ") && csaso.equals(" "))) {  // assignment => freq mark and/or int mark
                    invalid("an assignment but no frequency or intensity mark");
                    return false;                                    // invalid read
                }
                if (!cfaso.equals(" ") && !cfaso.equals("+") && !cfaso.equals("-")) {  // freq mark = space, + or -
                    invalid("invalid frequency mark (must be space, + or -)");
                    return false;                                    // invalid read
                }
                if (!csaso.equals(" ") && !csaso.equals("+") && !csaso.equals("-")) {  // int mark = space, + or -
                    invalid("invalid intensity mark (must be space, + or -)");
                    return false;                                    // invalid read
                }
                alfaso.add(cfaso);
                alsaso.add(csaso);
                aljsyn.add(cjsyn);
                // str.substring(72,73) is blank separator
                // EXASG string
                cstr = str.substring(73, 103);
                if (cstr.trim().length() == 0) {
                    // shrink if empty
                    cstr = "";
                }
                // assignment, EXASG test
                if (cjsyn.trim().length() == 0 && cstr.trim().length() != 0) {  // EXASG without assignment
                    invalid("no assignment but an EXASG");
                    return false;                                    // invalid read
                }
                if (cjsyn.trim().length() != 0 && cstr.trim().length() == 0) {  // assignment without EXASG
                    invalid("an assignment but no EXASG");
                    return false;                                    // invalid read
                }
                alexasg.add(cstr);
                // str.substring(103,104) is blank separator
                // comment
                cstr = "";
                if (strl > 104) {
                    cstr = str.substring(104, strl);
                }
                cstr = cstr.trim();                                  // suppress leading and extra spaces (!!!)
                alcomm.add(cstr);
            }
        } catch (IOException ioe) {                                     // IO error
            JOptionPane.showMessageDialog(null, "IO error while reading file" + lnsep +
                    name + lnsep +
                    ioe);
            return false;
        } finally {
            // close
            if (br != null) {
                try {
                    br.close();
                } catch (IOException ignored) {
                }
            }
        }
        // save data
        // nb of read lines
        int nbll = alx.size();                                           // nb of points
        if (nbll == 0) {
            // no point
            JOptionPane.showMessageDialog(null, "No valid data found in file" + lnsep +
                    name);
            return false;                                            // invalid read
        }
        // sort according to x, nuo, y, sdfreq then sdint
        ExpXPoint[] expt = new ExpXPoint[nbll];
        for (int i = 0; i < nbll; i++) {
            expt[i] = new ExpXPoint(alnuo.get(i),     // line number
                    alx.get(i),     // frequency
                    alfaso.get(i),     // frequency mark
                    aly.get(i),     // intensity
                    alsaso.get(i),     // intensity mark
                    alsdobs.get(i),     // freq and int standard deviations
                    aljsyn.get(i),     // assignment
                    alexasg.get(i),     // EXASG
                    alcomm.get(i));  // comment
        }
        Arrays.sort(expt);                                           // sort
        // previous, current
        double prevx = -1.;
        String prevnuo = "";
        double prevy = -1.;
        String prevsdobs;
        double prevsdfreq = -1.;
        double prevsdint = -1.;
        String prevjsyn = "";
        double curx;
        String curnuo;
        double cury;
        String cursdobs;
        double cursdfreq;
        double cursdint;
        String curjsyn;

        // find the number of single lines
        // same x, nuo, y , sdfreq and sdint
        nbxy = 0;                                                    // nb of single lines
        for (int i = 0; i < nbll; i++) {
            curx = expt[i].getX();
            curnuo = expt[i].getNuo();
            cury = expt[i].getY();
            cursdfreq = expt[i].getSdfreq();
            cursdint = expt[i].getSdint();
            curjsyn = expt[i].getJsyn();
            if (curx == prevx &&
                    curnuo.equals(prevnuo) &&
                    cury == prevy &&
                    cursdfreq == prevsdfreq &&
                    cursdint == prevsdint) {
                // same line implies assigned
                if (prevjsyn.trim().length() * curjsyn.trim().length() == 0) {  // multi-assignment => no empty assignment
                    JOptionPane.showMessageDialog(null, "Invalid data found:" + lnsep +
                            "multi-assignment => no empty assignment" + lnsep +
                            prevnuo + " " + prevx + " " + prevy + " " + prevjsyn + lnsep +
                            curnuo + " " + curx + " " + cury + " " + curjsyn + lnsep +
                            "in file" + lnsep +
                            name);
                    return false;                                    // invalid read
                }
            } else {
                nbxy++;                                             // new single line
            }
            // to compare with the next one
            prevx = curx;
            prevnuo = curnuo;
            prevy = cury;
            prevsdfreq = cursdfreq;
            prevsdint = cursdint;
            prevjsyn = curjsyn;
        }
        // create arrays
        x = new double[nbxy];
        y = new double[nbxy];
        sdobs = new String[nbxy];
        alejsynpt = new ArrayList[nbxy];
        exasg = new String[nbxy];

        prevx = -1.;
        prevnuo = "";
        prevy = -1.;
        prevsdobs = "";
        prevsdfreq = -1.;
        prevsdint = -1.;
        int j = -1;
        for (int i = 0; i < nbll; i++) {
            // for all lines
            curx = expt[i].getX();
            curnuo = expt[i].getNuo();
            cury = expt[i].getY();
            cursdobs = expt[i].getSdobs();
            cursdfreq = expt[i].getSdfreq();
            cursdint = expt[i].getSdint();
            // nuo has to be unique
            // same nuo => same x, y and sdobs
            if (curnuo.equals(prevnuo) &&
                    (curx != prevx ||
                            cury != prevy ||
                            cursdfreq != prevsdfreq ||
                            cursdint != prevsdint)) {
                JOptionPane.showMessageDialog(null, "Invalid data found:" + lnsep +
                        "same line number => same x, y and sdobs" + lnsep +
                        prevnuo + " " + prevx + " " + prevy + " " + prevsdobs + lnsep +
                        curnuo + " " + curx + " " + cury + " " + cursdobs + lnsep +
                        "in file" + lnsep +
                        name);
                return false;                                        // invalid read
            }
            if (curx != prevx ||
                    !curnuo.equals(prevnuo) ||
                    cury != prevy ||
                    cursdfreq != prevsdfreq ||
                    cursdint != prevsdint) {
                // first occurence
                j++;                                                // single line pointer
                x[j] = curx;
                y[j] = cury;
                sdobs[j] = expt[i].getSdobs();
                alejsynpt[j] = new ArrayList<>();
                exasg[j] = expt[i].getExasg();
            }
            // add a ExpJsynPoint to the alejsynpt of the current single line ([j])
            // a line contains in its alejsynpt all its related ExpJsynPointS.
            alejsynpt[j].add(new ExpJsynPoint(expt[i].getFaso(), expt[i].getSaso(), expt[i].getJsyn(), -1, expt[i].getComm()));
            prevx = curx;
            prevnuo = curnuo;
            prevy = cury;
            prevsdobs = cursdobs;
            prevsdfreq = cursdfreq;
            prevsdint = cursdint;
        }
        // sort each alejsynpt (increasing order)
        // the sort methode needs an array, not an ArrayList
        ExpJsynPoint[] aejpt;
        for (int i = 0; i < nbxy; i++) {
            // for each single line
            int nbass = alejsynpt[i].size();                         // nb of assignments
            if (nbass > 1) {                                        // has to be sorted
                // copy alejsynpt in an array
                aejpt = new ExpJsynPoint[nbass];                     // new array
                for (int k = 0; k < nbass; k++) {                       // fill it
                    aejpt[k] = alejsynpt[i].get(k);
                }
                Arrays.sort(aejpt);                                  // sort it
                // copy the array in alejsynpt
                alejsynpt[i] = new ArrayList<>();
                for (int k = 0; k < nbass; k++) {
                    alejsynpt[i].add(aejpt[k]);
                }
            }
        }
        // happy end
        return true;
    }

    /*
     * Invalid line, show why
     */
    private void invalid(String cmess) {
        JOptionPane.showMessageDialog(null, str + lnsep + cmess + lnsep + "invalid in file" + lnsep + name);
    }

    /**
     * Get number of data points.
     */
    public int getNbxy() {

        return nbxy;
    }

    /**
     * Get X array.
     */
    public double[] getX() {

        return x;
    }

    /**
     * Get Y array.
     */
    public double[] getY() {

        return y;
    }

    /**
     * Get X value.
     */
    public double getX(int id) {

        return x[id];
    }

    /**
     * Get Y value.
     */
    public double getY(int id) {

        return y[id];
    }

    /**
     * Get Sdobs array.
     */
    public String[] getSdobs() {

        return sdobs;
    }

    /**
     * Get Ejsynpt array.
     */
    public ArrayList<ExpJsynPoint>[] getEjsynpt() {

        return alejsynpt;
    }

    /**
     * Get Exasg array.
     */
    public String[] getExasg() {

        return exasg;
    }

}
