/*
 *       File name:  aghermann/patterns/patterns.hh
 *         Project:  Aghermann
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 * Initial version:  2013-01-09
 *
 *         Purpose:  class CPattern
 *
 *         License:  GPL
 */

#ifndef AGH_AGHERMANN_PATTERNS_H_
#define AGH_AGHERMANN_PATTERNS_H_

#include <stdexcept>
#include <tuple>
#include <vector>

#include <gsl/gsl_math.h>

#include "libsigproc/sigproc.hh"
#include "aghermann/expdesign/dirlevel.hh"

#if HAVE_CONFIG_H && !defined(VERSION)
#  include "config.h"
#endif

using namespace std;

namespace agh {
namespace pattern {

class CMatch
  : public tuple<double, double, double, double> {
    public:
        CMatch ()
              : tuple<double, double, double, double> (.1, .1, .1, .1) // empirically ok default
                {}
        CMatch (double t1, double t2, double t3, double t4)
              : tuple<double, double, double, double> (t1, t2, t3, t4)
                {}

        bool good_enough( const CMatch& rv) const
                {
                        return get<0>(*this) < get<0>(rv) &&
                               get<1>(*this) < get<1>(rv) &&
                               get<2>(*this) < get<2>(rv) &&
                               get<3>(*this) < get<3>(rv);
                }
};

template <typename T>
struct SPatternPPack {
        SPatternPPack ()
                {
                        make_sane();
                }

        double  env_scope;
        double  bwf_ffrom,
                bwf_fupto;
        int     bwf_order;
        double  dzcdf_step,
                dzcdf_sigma;
        int     dzcdf_smooth;

        bool
        operator==( const SPatternPPack<T>& rv) const
                {
                        return  env_scope == rv.env_scope &&
                                bwf_ffrom == rv.bwf_ffrom &&
                                bwf_fupto == rv.bwf_fupto &&
                                bwf_order == rv.bwf_order &&
                                dzcdf_step == rv.dzcdf_step &&
                                dzcdf_sigma == rv.dzcdf_sigma &&
                                dzcdf_smooth == rv.dzcdf_smooth;
                }
        bool
        is_sane() const
                {
                        return  env_scope > 0. && env_scope <= 1. &&
                                bwf_ffrom < bwf_fupto &&
                                bwf_ffrom >= 0. && bwf_ffrom <= 50. &&
                                bwf_fupto >= 0. && bwf_fupto <= 50. &&
                                bwf_order > 0 && bwf_order <= 5 &&
                                dzcdf_step > 0. && dzcdf_step <= 1. &&
                                dzcdf_sigma > 0. && dzcdf_sigma <= 1. &&
                                dzcdf_smooth >= 0 && dzcdf_smooth <= 50;
                }

        void
        make_sane()
                {
                        env_scope = .25;
                        bwf_ffrom = 0.;
                        bwf_fupto = 1.5;
                        bwf_order = 1;
                        dzcdf_step = .1;
                        dzcdf_sigma = .5;
                        dzcdf_smooth = 3;
                }
};



template <typename T>
struct SPattern
  : public CStorablePPack {

        static constexpr const char* common_subdir = ".patterns/";

        SPattern<T> (const string& name_, TExpDirLevel level_, CExpDesign& ED_, const SExpDirLevelId& level_id_)
              : CStorablePPack (common_subdir, name_, level_, ED_, level_id_),
                samplerate (0),
                context ({0, 0})
                {
                        load();
                }
        SPattern<T> (CExpDesign& ED_, const SExpDirLevelId& level_id_)
              : CStorablePPack (common_subdir, "(unnamed)", TExpDirLevel::transient, ED_, level_id_),
                samplerate (0),
                context ({0, 0})
                {}

        explicit SPattern<T> (const SPattern<T>& rv)
              : CStorablePPack (rv),
                thing      (rv.thing),
                samplerate (rv.samplerate),
                context    (rv.context),
                Pp         (rv.Pp),
                criteria   (rv.criteria)
                {
                        // assign_keys();
                }
        explicit SPattern<T> (SPattern<T>&& rv)
              : CStorablePPack (common_subdir, move(rv.name), rv.level, rv.ED, move(rv.level_id)),
                thing      (move(rv.thing)),
                samplerate (rv.samplerate),
                context    (rv.context),
                Pp         (move(rv.Pp)),
                criteria   (move(rv.criteria))
                {
                        rv.level = TExpDirLevel::transient;  // we do the saving, prevent rv from doing so
                        // assign_keys();
                }

       ~SPattern<T> ()
                {
                        save();
                }

        SPattern&
        operator=( const SPattern<T>& rv)
                {
                        CStorablePPack::operator=(*this);
                        thing.resize(rv.thing.size());
                        thing      = rv.thing;
                        samplerate = rv.samplerate;
                        context    = rv.context;
                        Pp       = rv.Pp;
                        criteria = rv.criteria;

                        return *this;
                }
        SPattern&
        operator=( SPattern<T>&& rv)
                {
                        CStorablePPack::operator=(move(*this));
                        swap       (thing, rv.thing);
                        samplerate  = rv.samplerate;
                        context     = rv.context;
                        swap       (Pp, rv.Pp);
                        swap       (criteria, rv.criteria);

                        return *this;
                }

        bool
        operator==( const SPattern<T>& rv) const
                { return Pp == rv.Pp; }  // don't bother about CStorable nor criteria

        size_t
        pattern_size_essential() const
                { return thing.size() - context.first - context.second; }

        double
        pattern_length() const // in seconds
                { return (double)thing.size() / samplerate; }

        double
        pattern_length_essential() const
                { return (double)pattern_size_essential() / samplerate; }


        int load();
        int save();
        string serialize() const;

        valarray<T>
                thing;
        size_t  samplerate;
        pair<size_t,size_t>
                context;
        static const size_t
                context_pad = 100;

        SPatternPPack<TFloat>
                Pp;
        CMatch  criteria;
};



using TContext = pair<size_t, size_t>;

template <typename T>
class CPatternTool
  : public SPatternPPack<T> {
        DELETE_DEFAULT_METHODS (CPatternTool);

    public:
      // the complete pattern signature is made of:
      // (a) signal breadth at given tightness;
      // (b) its course;
      // (c) target frequency (band-passed);
      // (d) instantaneous frequency at fine intervals;

        CPatternTool (const sigproc::SSignalRef<T>& thing,
                      const TContext& context_,
                      const SPatternPPack<T>& Pp_)
              : SPatternPPack<T> (Pp_),
                penv             (thing),
                ptarget_freq     (thing),
                pdzcdf           (thing),
                samplerate       (thing.samplerate),
                context          (context_)
                {
                        if ( context.first + context.second >= thing.signal.size() )
                                throw invalid_argument ("pattern size too small");
                }

        int
        do_search( const sigproc::SSignalRef<T>& field,
                   size_t inc);
        int
        do_search( const valarray<T>& field,
                   size_t inc);
        int
        do_search( const valarray<T>& env_u,  // field broken down
                   const valarray<T>& env_l,
                   const valarray<T>& target_freq,
                   const valarray<T>& dzcdf,
                   size_t inc);

        size_t
        size_with_context() const
                { return ptarget_freq.signal.size(); }
        size_t
        size_essential() const
                { return size_with_context() - context.first - context.second; }

        vector<CMatch>
                diff;

    private:
        sigproc::SCachedEnvelope<T>
                penv;
        sigproc::SCachedBandPassCourse<T>
                ptarget_freq;
        sigproc::SCachedDzcdf<T>
                pdzcdf;

        size_t  samplerate;
        TContext
                context;

        T       crit_linear_unity;
        double  crit_dzcdf_unity;
};



#include "patterns.ii"

}
} // namespace agh::pattern


#endif

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