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

import java.io.BufferedReader;
import java.io.File;
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 String name; // file name
	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 nbll; // nb of read lines
	private int nbxy; // nb of points

	private String str; // to read file
	private BufferedReader br;
	private String lnsep; // line separator

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

	/**
	 * 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<String>();                         // line number
        ArrayList<Double> alx     = new ArrayList<Double>();                         // frequency
        ArrayList<String> alfaso  = new ArrayList<String>();                         // frequency mark
        ArrayList<Double> aly     = new ArrayList<Double>();                         // intensity
        ArrayList<String> alsaso  = new ArrayList<String>();                         // intensity mark
        ArrayList<String> alsdobs = new ArrayList<String>();                         // frequency and intensity standard deviations string
        ArrayList<String> aljsyn  = new ArrayList<String>();                         // assignment
        ArrayList<String> alexasg = new ArrayList<String>();                         // EXASG
        ArrayList<String> alcomm  = new ArrayList<String>();                         // 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( new File(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.valueOf(str.substring(6,18));        // Double for X
                    cy = Double.valueOf(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
        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(  (String) alnuo.get(i)             ,     // line number
                                     ((Double) alx.get(i)).doubleValue(),     // frequency
                                      (String) alfaso.get(i)            ,     // frequency mark
                                     ((Double) aly.get(i)).doubleValue(),     // intensity
                                      (String) alsaso.get(i)            ,     // intensity mark
                                      (String) alsdobs.get(i)           ,     // freq and int standard deviations
                                      (String) aljsyn.get(i)            ,     // assignment
                                      (String) alexasg.get(i)           ,     // EXASG
                                      (String) 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<ExpJsynPoint>();
                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] = (ExpJsynPoint)alejsynpt[i].get(k);
                }
                Arrays.sort(aejpt);                                  // sort it
                // copy the array in alejsynpt
                alejsynpt[i] = new ArrayList<ExpJsynPoint>();
                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;
    }

}
