//**************************************************************************//
//* FRIDA: fast reliable interactive data analysis                         *//
//* rssm.cpp: read SPHERES raw data                                        *//
//* (C) Joachim Wuttke 1990-, v2(C++) 2001-                                *//
//* http://frida.sourceforge.net                                           *//
//**************************************************************************//

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <iostream>

#include "mystd.h"
#include "olm.h"
#include <ask_simple_value.h>
#include "rssm.h"
#include "yamc.h"

using namespace std;

class RssmRawFile {
 public:
    void RdRawYam( FILE *F_in );
    double daq_time_step;
    time_t measured_from;
    time_t measured_until;
    int    measured_at;
    double measured_during;
    bool   incremental;
    vector<double> rawdata[6];
    vector<double> angles;
    int    maj_syntax, min_syntax, maj_outform, min_outform;
};

//! Read raw data file, store contents as class variables

void RssmRawFile::RdRawYam( FILE *F_in )
{
    string lin, key, val, bla, blub;
    CCoord co;
    CScan sout;
    int ndet;
    double num;
    vector<double> numvec;

    maj_outform = 5;
    min_outform = 1;

    daq_time_step=0;
    angles.clear();

    CYamlRead y( F_in );

    y.checktype( YAML_STREAM_START_EVENT, "no stream\n" );
    y.checktype( YAML_DOCUMENT_START_EVENT, "no document\n" );
    y.checktype( YAML_MAPPING_START_EVENT, "main: no map\n" );

    y.checkvalue( "Meta" );
    y.checktype( YAML_MAPPING_START_EVENT, "Meta: no map\n" );
    y.checkvalue( "format" );
    val = y.getscalar( "format" );
    if( strncmp( val.c_str(), "acq5.1 for yaml1", 16 ) )
        throw( string("Unexpected format: ") + val + " instead of acq5.1" );
    y.skipvalues( 2 );
    y.checktype( YAML_MAPPING_END_EVENT, "Meta: no end of map\n" );

    y.checkvalue( "History" );
    y.checktype( YAML_SEQUENCE_START_EVENT, "History: no seq\n" );
    while( y.scalar_from_seq( "History", &val ) )
        ; // do nothing

    y.checkvalue( "Shortpar" );
    y.checktype( YAML_MAPPING_START_EVENT, "Shortpar: no map\n" );
    while( y.pair_from_map( "Shortpar", &key, &val ) ){
        if        ( key=="incremental" ){
            if      ( val=="true" )
                incremental = true;
            else if ( val=="false" )
                incremental = false;
            else
                throw string( "Invalid value [" ) + val +
                    "] of 'incremental'";
        } else if ( key=="measured_until" ){
            char date_string[24];
            sscanf( val.c_str(), "%24c", date_string );
            struct tm date_broken;
            strptime( date_string, "%F %A %H:%M:%S", &date_broken );
            measured_until = mktime( &date_broken );
        } else if ( key=="ndet" ){
            if( !mystd::any2int( val, &ndet ) )
                throw string( "invalid ndet" );
        } else if ( key=="daq_time_step" ){
            mystd::string_extract_word( val, &bla, &blub );
            mystd::any2dbl( bla, &daq_time_step );
        }
    }
    if( !daq_time_step )
        throw string( "daq time step not found" );

    y.checkvalue( "Longpar" );
    y.getscalar( "Longpar emptyness" );

    y.checkvalue( "Detectors" );
    y.checktype( YAML_SEQUENCE_START_EVENT, "Detectors: no seq\n" );
    while( true ){
        y.next();
        if( y.event.type == YAML_SEQUENCE_END_EVENT )
            break;
        if( y.event.type != YAML_MAPPING_START_EVENT )
            throw string ( "Dets: no map" );
        y.done();
        if( !y.pair_from_map( "Dets", &key, &val ) )
            throw string( "invalid Det angle entry" );
        if( key != "angle" )
            throw string ( "missing angle" );
        if( !mystd::any2dbl( val, &num ) ){
            num = 0;
            printf( "warning: invalid angle %s\n", val.c_str() );
        }
        y.next();
        if( y.event.type != YAML_MAPPING_END_EVENT ){
            y.done(); y.next(); y.done(); y.next(); // path_offset ?
            if( y.event.type != YAML_MAPPING_END_EVENT ){
                throw string( "Det map does not end" );
            }
        }
        y.done();
        angles.push_back( num );
    }
    y.done();
    if( angles.size()!=ndet )
        throw string( "ndet inconsistent" );

    y.checkvalue( "EnergyHistograms" );
    y.checktype( YAML_SEQUENCE_START_EVENT, "E-Hist: no seq\n" );
    while( true ){
        y.next();
        if( y.event.type == YAML_SEQUENCE_END_EVENT )
            break;
        if( y.event.type != YAML_SEQUENCE_START_EVENT )
            throw string ( "E-Hist: seq: no seq" );
        y.done();
        val = y.getscalar( "x-value" );
        if( !mystd::any2dbl( val, &num ) )
            throw string( "E-Hist: invalid x-value " ) + val;
        for( int i=0; i<4; ++i )
            rawdata[i].push_back( num );
        for( int i=0; i<4; ++i ){
            val = y.getscalar( "cnts line" );
            if( !mystd::str2vec( val, &numvec ) )
                throw string( "cnts line extraction failed" );
            if( numvec.size()!=ndet )
                throw string( "hist: ndet incompatibility" );
            for( int ii=0; ii<numvec.size(); ++ii )
                rawdata[i].push_back( numvec[ii] );
        }
        for( int i=0; i<4; ++i ){
            val = y.getscalar( "time line" );
            if( !mystd::str2vec( val, &numvec ) )
                throw string( "time line extraction failed" );
            for( int ii=0; ii<numvec.size(); ++ii )
                rawdata[i].push_back( numvec[ii] );
        }
        y.checktype( YAML_SEQUENCE_END_EVENT, "E-Hist entry" );
    }
    y.done();

    y.checkvalue( "ChopperHistograms" );
    y.checktype( YAML_SEQUENCE_START_EVENT, "C-Hist: no seq\n" );
    while( true ){
        y.next();
        if( y.event.type == YAML_SEQUENCE_END_EVENT )
            break;
        if( y.event.type != YAML_SEQUENCE_START_EVENT )
            throw string ( "C-Hist: seq: no seq" );
        y.done();
        val = y.getscalar( "x-value" );
        if( !mystd::any2dbl( val, &num ) )
            throw string( "C-Hist: invalid x-value " ) + val;
        for( int i=4; i<6; ++i )
            rawdata[i].push_back( num );
        for( int i=4; i<6; ++i ){
            val = y.getscalar( "cnts line" );
            if( !mystd::str2vec( val, &numvec ) )
                throw string( "cnts line extraction failed" );
            if( numvec.size()!=ndet )
                throw string( "hist: ndet incompatibility" );
            for( int ii=0; ii<numvec.size(); ++ii )
                rawdata[i].push_back( numvec[ii] );
        }
        for( int i=4; i<6; ++i ){
            val = y.getscalar( "time line" );
            if( !mystd::str2vec( val, &numvec ) )
                throw string( "time line extraction failed" );
            for( int ii=0; ii<numvec.size(); ++ii )
                rawdata[i].push_back( numvec[ii] );
        }
        y.checktype( YAML_SEQUENCE_END_EVENT, "C-Hist entry" );
    }
    y.done();

}    

void NRSSM::ReadScan( int format, int flag )

// format values:
//    2 acq2-4 xml
//    5 acq5   yaml
// flag values (to be OR'ed):
//    1 save also open
//    2 save also chop histo
//    4 save spectra also separately for both directions
//    8 do not save regular spectrum

{
    string file_f, name;
    file_f = wask("Read SPHERES data from file");
    RssmRawFile R;
    int ret;
    FILE *F_in;

    if        ( format==2 ){
        throw string( "XML support has been withdrawn from frida2 on 19may09" );
    } else if ( format==5 ){
        if( !(F_in = fopen(file_f.c_str(), "r")) ) {
            cerr << "Cannot read " << file_f << "\n";
            return;
        }
        try{
            R.RdRawYam( F_in );
        }
        catch( string &e) {
            cerr << "Cannot read " << file_f << ":\n";
            cerr << e << endl;
            if (F_in) fclose(F_in);
            return;
        }
        ret = 0;
    } else {
        throw string( "invalid format" );
    }
    if( ret )
        return;

    double tstep = R.daq_time_step;
    vector<double> *raw = R.rawdata;
    int ndet = R.angles.size();

    // *** set file headers ***

    COld olf[8];
    int iolf;
    
    for( iolf=0; iolf<8; ++iolf ){
        olf[iolf].xco = iolf<4 ||iolf>5 ?
            CCoord( "E", "ueV" ) : CCoord( "phase", "deg" );
        olf[iolf].yco = CCoord( "cts", "sec-1" );
        olf[iolf].ZCo.push_back( CCoord( "det", "") );
    }
    mystd::fname_divide( file_f, 0, &name, 0 );
    olf[0].name = name+"left";
    olf[1].name = name+"right";
    olf[2].name = name+"open_left";
    olf[3].name = name+"open_right";
    olf[4].name = name+"chop_elast";
    olf[5].name = name+"chop_inela";
    olf[6].name = name;
    olf[7].name = name+"open";
    string doc = "ry" + (flag==0 ? "" : strg(flag+1)) + " " + file_f;
    olf[0].lDoc.push_back( doc + " # refl, left halfspace" );
    olf[1].lDoc.push_back( doc + " # refl, right halfspace" );
    olf[2].lDoc.push_back( doc + " # open, left halfspace" );
    olf[3].lDoc.push_back( doc + " # open, right halfspace" );
    olf[4].lDoc.push_back( doc + " # elastic" );
    olf[5].lDoc.push_back( doc + " # inelastic" );
    olf[6].lDoc.push_back( doc + " # refl" );
    olf[7].lDoc.push_back( doc + " # open" );

    CScan S;
    for( int j=0; j<ndet; ++j ){
        S.Clear();
        S.z.resize( 1 );
        S.z[0] = R.angles[j];
        for( iolf=0; iolf<8; ++iolf )
            olf[iolf].VS.push_back(S);
    }

    if( R.maj_outform<=2 ){
        double x, tmeas, count;
        // transfer data from raw arrays:
        for( iolf=0; iolf<6; ++iolf ){
            for( int iraw=0; iraw<(raw+iolf)->size(); iraw+=(ndet+2) ){
                x = raw[iolf][iraw];
                tmeas = raw[iolf][iraw+ndet+1];
                if( tmeas<1 ) continue;
                for ( int j=0; j<ndet; ++j ){
                    count = raw[iolf][iraw+1+j];
                    olf[iolf].VS[j].push_xy( x, count/tmeas/tstep );
                }
            }
        }
        // sum over halfspaces:
        for( int ichop=0; ichop<2; ++ichop ){
            int iolf0 = ichop*2;
            int iolf1 = ichop*2+1;
            int nline = (raw+iolf0)->size();
            if( (raw+iolf1)->size()!=nline ){
                printf( "inconsistent raw array sizes\n" );
                return;
            }
            for( int iraw=0; iraw<nline; iraw+=(ndet+2) ){
                x = raw[iolf0][iraw];
                tmeas = raw[iolf0][iraw+ndet+1]+raw[iolf1][iraw+ndet+1];
                if( tmeas<1 ) continue;
                for ( int j=0; j<ndet; ++j ){
                    count = raw[iolf0][iraw+1+j] + raw[iolf1][iraw+1+j];
                    olf[6+ichop].VS[j].push_xy( x, count/tmeas/tstep );
                }
            }
        }

    } else { // as of 2008-03-03
        double x, tmeas, count;
        // transfer data from raw arrays:
        for( iolf=0; iolf<6; ++iolf ){
            for( int iraw=0; iraw<(raw+iolf)->size(); iraw+=(1+ndet*2) ){
                x = raw[iolf][iraw];
                for ( int j=0; j<ndet; ++j ){
                    tmeas = raw[iolf][iraw+1+ndet+j];
                    if( tmeas<1 ) continue;
                    count = raw[iolf][iraw+1+j];
                    olf[iolf].VS[j].push_xy( x, count/tmeas/tstep );
                }
            }
        }
        // sum over halfspaces:
        for( int ichop=0; ichop<2; ++ichop ){
            int iolf0 = ichop*2;
            int iolf1 = ichop*2+1;
            int nline = (raw+iolf0)->size();
            if( (raw+iolf1)->size()!=nline ){
                printf( "inconsistent raw array sizes: "
                        "left %zu right %zu lines\n",
                        (raw+iolf0)->size(), (raw+iolf1)->size() );
                return;
            }
            for( int iraw=0; iraw<nline; iraw+=(1+ndet*2) ){
                x = raw[iolf0][iraw];
                for ( int j=0; j<ndet; ++j ){
                    tmeas = raw[iolf0][iraw+1+ndet+j] +
                        raw[iolf1][iraw+1+ndet+j];
                    if( tmeas<1 ) continue;
                    count = raw[iolf0][iraw+1+j] + raw[iolf1][iraw+1+j];
                    olf[6+ichop].VS[j].push_xy( x, count/tmeas/tstep );
                }
            }
        }
    }
    
    NOlm::SelNew();
    if( flag & 2 ){
        NOlm::OloAdd(&olf[4]);
        NOlm::OloAdd(&olf[5]);
    }
    if( flag & 2 && flag & 4 ){
        NOlm::OloAdd(&olf[2]);
        NOlm::OloAdd(&olf[3]);
    }
    if( flag & 4 ){
        NOlm::OloAdd(&olf[0]);
        NOlm::OloAdd(&olf[1]);
    }
    if( flag & 1 ){
        NOlm::OloAdd(&olf[7]);
    }
    if( ! (flag & 8 ) ){
        NOlm::OloAdd(&olf[6]);
    }

}


void NRSSM::ReadSeries( int format, int flag )

// format values:
//    as above
// flag values (to be OR'ed):
//    1 save also open

{
    char tstrg[30];
    string fser, fnam, name;
    time_t t;
    int ret, isub;
    vector<RssmRawFile> RR;

    // Read consolidated files:
    fser = wask("Read SPHERES data from series");
    for( isub=0; ; ++isub ){
        if       ( format==2 ) {
            throw string(
                "XML support has been withdrawn from frida2 on 19may09" );
        } else if( format==5 ) {
            fnam = fser+"a"+strg(isub);
            FILE *F_in;
            if( !(F_in = fopen( fnam.c_str(), "r" ) ) )
                break;
            // cout << "successfully opened "<<fnam<<"\n";
            try{ 
                RssmRawFile rf;
                rf.RdRawYam( F_in );
                RR.push_back( rf );
            }
            catch(exception& e) {
                cerr << e.what() << endl;
                if (F_in) fclose(F_in);
                return;
            }
            ret = 0;
        } else {
            throw string( "invalid format parameter" );
        } 
        if ( ret )
            return;
    }
    int nsub = RR.size();
    if( nsub==0 )
        throw string( "could not open file " ) + fnam;
    else if( nsub==1 )
        throw string( "series contains just one scan -> use a simpler method");
    printf( "successfully read %d files\n", nsub );

    // Correct time:
    double mean_time =
        ( RR[nsub-1].measured_until - RR[0].measured_until ) / nsub;
    printf( "mean measuring time %g seconds per file\n", mean_time );
    t = (time_t) (RR[0].measured_until-mean_time);
    strftime( tstrg, 30, "%F %b %H:%M:%S", localtime( &t ) );
    printf( "measurement times relative to start time %s\n", tstrg );
    for( isub=0; isub<nsub; ++isub )
        RR[isub].measured_at = RR[isub].measured_until - 
            RR[0].measured_until + (int) (mean_time/2);

    // Get parameters, check consistency:
    int ndet = RR[0].angles.size();
    printf( "used %i detectors\n", ndet );
    double tstep = RR[0].daq_time_step;
    bool incremental_in = RR[0].incremental;

    for( isub=1; isub<nsub; ++isub ){
        if( RR[isub].angles.size()!=ndet )
            throw string( "inconsistent no of det: file " ) + strg(isub)
                + " has " + strg(RR[isub].angles.size()) + " instead of "
                + strg(ndet);
        if( RR[isub].daq_time_step!=tstep )
            throw string( "inconsistent time step" );
        if( RR[isub].incremental!=incremental_in )
            throw string( "inconsistent increment status" );
    }

    // Set file headers:
    COld olf[2];
    int iolf, j, nE;

    for( iolf=0; iolf<2; ++iolf ){
        olf[iolf].lDoc.push_back( "acquire data from series"+fser );
        olf[iolf].xco = CCoord( "E", "ueV" );
        olf[iolf].yco = CCoord( "cts", "sec-1" );
        olf[iolf].ZCo.push_back( CCoord( "#sub", "") );
        olf[iolf].ZCo.push_back( CCoord( "det", "") );
    }
    mystd::fname_divide( fser, 0, &name, 0 );
    olf[0].name = name;
    olf[1].name = name+"open";
    olf[0].lDoc.push_back( "refl, average over both halfspaces" );
    olf[1].lDoc.push_back( "open, average over both halfspaces" );


    for( isub=0; isub<nsub; ++isub ){
        // ? = (double)RR[isub].measured_at/3600;
        for( j=0; j<ndet; ++j ){
            CScan S;
            S.z.resize( 2 );
            S.z[0] = isub;
            S.z[1] = RR[isub].angles[j];
            for( iolf=0; iolf<2; ++iolf )
                olf[iolf].VS.push_back( S );
        }
    }

    if( RR[0].maj_outform>=3 ){
        printf( "new format\n" );
        nE = RR[0].rawdata[0].size()/(1+ndet*2);
        // Reverse incrementation?
        if( incremental_in ){
            printf( "reversing incrementation\n" );
            for( isub=nsub-1; isub>=1; --isub ){
                for( int iarr=0; iarr<4; ++iarr ){
                    for( int iE=0; iE<nE; ++iE ){
                        for( j=1; j<1+ndet*2; ++j ){
                            RR[isub].rawdata[iarr][iE*(1+ndet*2)+j] -= 
                                RR[isub-1].rawdata[iarr][iE*(1+ndet*2)+j];
                        }
                    }
                }
            }
        }

        // Transfer data from raw arrays, sum over halfspaces:
        vector<double> *raw;
        double x, tmeas, count;
        int iraw;
        for( isub=0; isub<nsub; ++isub ){
            raw = RR[isub].rawdata;
            for( int ichop=0; ichop<2; ++ichop ){
                int iolf0 = ichop*2;
                int iolf1 = ichop*2+1;
                if( (raw+iolf1)->size()/(1+ndet*2)!=nE ){
                    printf( "inconsistent raw array sizes:\n"
                        "  chop %i raw1 %zu nE %i\n",
                            ichop, (raw+iolf1)->size(), nE );
                    return;
                }
                for( int iE=0; iE<nE; ++iE ){
                    iraw = iE*(1+ndet*2);
                    x = raw[iolf0][iraw];
                    for( j=0; j<ndet; ++j ){
                        tmeas = raw[iolf0][iraw+ndet+1+j] +
                            raw[iolf1][iraw+ndet+1+j];
                        if( tmeas<1 ) continue;
                        count = raw[iolf0][iraw+1+j] + raw[iolf1][iraw+1+j];
                        olf[ichop].VS[isub*ndet+j].push_xy( 
                            x, count/tmeas/tstep );
                    }
                }
            }
        }

    } else {
        printf( "old format\n" );
        nE = RR[0].rawdata[0].size()/(ndet+2);
        // Reverse incrementation?
        if( incremental_in ){
            printf( "reversing incrementation\n" );
            for( isub=nsub-1; isub>=1; --isub ){
                for( int iarr=0; iarr<4; ++iarr ){
                    for( int iE=0; iE<nE; ++iE ){
                        for( j=1; j<ndet+2; ++j ){
                            RR[isub].rawdata[iarr][iE*(ndet+2)+j] -= 
                                RR[isub-1].rawdata[iarr][iE*(ndet+2)+j];
                        }
                    }
                }
            }
        }

        // Transfer data from raw arrays, sum over halfspaces:
        vector<double> *raw;
        double x, tmeas, count;
        int iraw;
        for( isub=0; isub<nsub; ++isub ){
            raw = RR[isub].rawdata;
            for( int ichop=0; ichop<2; ++ichop ){
                int iolf0 = ichop*2;
                int iolf1 = ichop*2+1;
                if( (raw+iolf1)->size()/(ndet+2)!=nE ){
                    printf( "inconsistent raw array sizes\n" );
                    return;
                }
                for( int iE=0; iE<nE; ++iE ){
                    iraw = iE*(ndet+2);
                    x = raw[iolf0][iraw];
                    tmeas = raw[iolf0][iraw+ndet+1]+raw[iolf1][iraw+ndet+1];
                    if( tmeas<1 ) continue;
                    for( j=0; j<ndet; ++j ){
                        count = raw[iolf0][iraw+1+j] + raw[iolf1][iraw+1+j];
                        olf[ichop].VS[isub*ndet+j].push_xy( 
                            x, count/tmeas/tstep );
                    }
                }
            }
        }
    }

    NOlm::SelNew();
    if( flag & 1 ){
        NOlm::OloAdd(&olf[1]);
    }
    if( 1 ){
        NOlm::OloAdd(&olf[0]);
    }

}