/*
 *       File name:  aghermann/patterns/patterns.cc
 *         Project:  Aghermann
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 * Initial version:  2013-01-09
 *
 *         Purpose:  CPatternTool explicit pattern instantiations be
 *                   here, also loading patterns
 *
 *         License:  GPL
 */

#include <cstring>
#include <dirent.h>
#include <sys/stat.h>

#include "common/fs.hh"
#include "aghermann/globals.hh"
#include "aghermann/expdesign/expdesign.hh"
#include "patterns.hh"

using namespace std;
using namespace agh::pattern;


namespace agh {
namespace pattern {

template CPatternTool<TFloat>::CPatternTool( const sigproc::SSignalRef<TFloat>&, const TContext&, const SPatternPPack<TFloat>&);
template int CPatternTool<TFloat>::do_search( const valarray<TFloat>&, const valarray<TFloat>&, const valarray<TFloat>&, const valarray<TFloat>&, size_t);
template int CPatternTool<TFloat>::do_search( const sigproc::SSignalRef<TFloat>&, size_t);
template int CPatternTool<TFloat>::do_search( const valarray<TFloat>&, size_t);



using confval::SValidator;


template <>
int
SPattern<TFloat>::
save()
{
        if ( saved || level == agh::TExpDirLevel::transient || level == agh::TExpDirLevel::system )
                return 0;

        if ( thing.size() == 0 ) {
                APPLOG_WARN ("save_pattern(\"%s\"): thing is empty", path().c_str());
                return -1;
        }

        if ( agh::fs::mkdir_with_parents( agh::fs::dirname(path())) ) {
                fprintf( stderr, "save_pattern(\"%s\"): mkdir failed\n", path().c_str());
                return -1;
        }

        string fname {path()};
        FILE *fd = fopen( fname.c_str(), "w");
        try {
                if ( !fd )
                        throw -2;

                if ( fprintf( fd,
                              "%g  %u %g %g  %g %g %u  %g %g %g %g\n"
                              "%zu  %zu %zu %zu\n"
                              "--DATA--\n",
                              Pp.env_scope,
                              Pp.bwf_order, Pp.bwf_ffrom, Pp.bwf_fupto,
                              Pp.dzcdf_step, Pp.dzcdf_sigma, Pp.dzcdf_smooth,
                              get<0>(criteria), get<1>(criteria), get<2>(criteria), get<3>(criteria),
                              samplerate, context.first, context.second,
                              thing.size()) < 1 ) {
                        fprintf( stderr, "save_pattern(\"%s\"): write failed\n", fname.c_str());
                        throw -3;
                }

                for ( size_t i = 0; i < thing.size(); ++i )
                        if ( fprintf( fd, "%a\n", (double)thing[i]) < 1 ) {
                                fprintf( stderr, "save_pattern(\"%s\"): write failed\n", fname.c_str());
                                throw -3;
                        }
                fclose( fd);

                saved = true;

                return 0;

        } catch (int ret) {
                if ( fd )
                        fclose( fd);
                return ret;
        }
}



template <>
int
SPattern<TFloat>::
load()
{
        string msg;
        using agh::str::sasprintf;

        auto fname_ = path();
        auto fname = fname_.c_str();
        FILE *fd = NULL;

        try {
                {
                        struct stat attr;
                        if ( 0 != lstat( fname, &attr) || !S_ISREG (attr.st_mode) )
                                return -1;
                }

                if ( !(fd = fopen( fname, "r")) )
                        throw invalid_argument ("Failed to open");

                size_t  full_sample;
                double  t1, t2, t3, t4;
                if ( 15 != fscanf(
                             fd,
                             "%lg  %u %lg %lg  %lg %lg %u"
                             " %lg %lg %lg %lg"
                             " %zu %zu %zu %zu\n"
                             "--DATA--\n",
                             &Pp.env_scope,
                             &Pp.bwf_order, &Pp.bwf_ffrom, &Pp.bwf_fupto,
                             &Pp.dzcdf_step, &Pp.dzcdf_sigma, &Pp.dzcdf_smooth,
                             &t1, &t2, &t3, &t4,
                             &samplerate, &context.first, &context.second,
                             &full_sample) ) {
                        throw invalid_argument ("Bogus data in header");
                }

                criteria = CMatch(t1, t2, t3, t4);

                if ( samplerate == 0 || samplerate > 4096 ||
                     full_sample == 0 || full_sample > samplerate * 10 ||
                     context.first > samplerate * 2 ||
                     context.second > samplerate * 2 ||
                     not Pp.is_sane() )
                        throw invalid_argument ("Inconsistent data in header");

                thing.resize( full_sample);
                for ( size_t i = 0; i < full_sample; ++i ) {
                        double d;
                        if ( fscanf( fd, "%la", &d) != 1 )
                                throw invalid_argument (sasprintf( "short read at sample %zu", i));
                        thing[i] = d;
                }

                fclose( fd);

                printf( "loaded pattern in %s\n", fname);
                saved = true;
                name = agh::str::tokens( fname, "/").back();

                return 0;

        } catch (invalid_argument& msg) {

                fprintf( stderr, "load_pattern(\"%s\"): %s\n", fname, msg.what());
                if ( fd )
                        fclose( fd);

                if (0 == rename( fname, (string(fname) + "~").c_str()))
                        fprintf( stderr, "load_pattern(\"%s\"): file renamed as *~\n", fname);
                else
                        fprintf( stderr, "load_pattern(\"%s\"): tried, but failed, to rename as *~\n", fname);

                return -2;
        }
}


template <>
string
SPattern<TFloat>::
serialize() const
{
        return CStorablePPack::serialize() +
                str::sasprintf(
                        " ::"
                        " env_scope: %g;"
                        " bwf_ffrom: %g;"
                        " bwf_fupto: %g;"
                        " bwf_order: %d;"
                        " dzcdf_step: %g;"
                        " dzcdf_sigma: %g;"
                        " dzcdf_smooth: %d;"
                        " criteria: %g %g %g %g;"
                        " thing: %zu smpl;",
                        Pp.env_scope,
                        Pp.bwf_ffrom,
                        Pp.bwf_fupto,
                        Pp.bwf_order,
                        Pp.dzcdf_step,
                        Pp.dzcdf_sigma,
                        Pp.dzcdf_smooth,
                        get<0>(criteria),
                        get<1>(criteria),
                        get<2>(criteria),
                        get<3>(criteria),
                        thing.size());
}



template <>
SPattern<TFloat>::
SPattern (const string& name_, TExpDirLevel level_, CExpDesign& ED_, const SExpDirLevelId& level_id_)
      : CStorablePPack (".patterns", name_, level_, ED_, level_id_),
        samplerate (0),
        context ({0, 0})
{
        config
                ("env_scope",    &Pp.env_scope)
                ("bwf_ffrom",    &Pp.bwf_ffrom)
                ("bwf_fupto",    &Pp.bwf_fupto)
                ("dzcdf_step",   &Pp.dzcdf_step)
                ("dzcdf_sigma",  &Pp.dzcdf_sigma)
                ("criteria.c0",  &get<0>(criteria))
                ("criteria.c1",  &get<1>(criteria))
                ("criteria.c2",  &get<2>(criteria))
                ("criteria.c3",  &get<3>(criteria))
                ("bwf_order",    &Pp.bwf_order)
                ("dzcdf_smooth", &Pp.dzcdf_smooth);

        load();
}




}
} // namespace agh::pattern

// Local Variables:
// Mode: c++
// indent-tabs-mode: nil
// tab-width: 8
// c-basic-offset: 8
// End:
