/*
 *       File name:  libcn/sources.hh
 *         Project:  cnrun
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 *                   building on original work by Thomas Nowotny <tnowotny@ucsd.edu>
 * Initial version:  2010-02-24
 *
 *         Purpose:  External stimulation sources (periodic, tape, noise).
 *
 *         License:  GPL-2+
 */

#ifndef CNRUN_LIBCN_SOURCES_H_
#define CNRUN_LIBCN_SOURCES_H_

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

#include <cstdio>
#include <string>
#include <vector>
#include <gsl/gsl_rng.h>

#include "libstilton/lang.hh"
#include "forward-decls.hh"


using namespace std;

namespace cnrun {

enum class TSourceType { null, tape, periodic, function, noise };

class C_BaseSource {

        DELETE_DEFAULT_METHODS (C_BaseSource)

    public:
        static const char* const type_s( TSourceType);

        C_BaseSource (const string& name_, TSourceType type_)
              : _name (name_), _type (type_)
                {}
        virtual ~C_BaseSource()
                {}

        const char* name() const
                {  return _name.c_str();  }
        const TSourceType type() const
                {  return _type;  }
        const char* type_s() const
                {  return type_s(_type);  }

        virtual double operator() ( double)
                {  return 0.;  }
        virtual bool is_periodic()
                {  return false;  }

        bool operator== ( const C_BaseSource &rv)
                {  return _name == rv._name; }
        bool operator== ( const string& rv)
                {  return _name == rv; }

        virtual void dump( FILE *strm = stdout) const = 0;

    protected:
        string  _name;
        TSourceType
                _type;
};


enum class TSourceLoopingOption { yes, no };

class CSourceTape : public C_BaseSource {

        DELETE_DEFAULT_METHODS (CSourceTape)

    public:
        CSourceTape (const string& name_, const string& fname_,
                     TSourceLoopingOption = TSourceLoopingOption::no);

        TSourceLoopingOption is_looping;

        double operator() ( double at)
                {
                        while ( next(_I) != _values.end() && next(_I)->first < at )
                                ++_I;

                        if ( next(_I) == _values.end() && is_looping == TSourceLoopingOption::yes )
                                _I = _values.begin();

                        return _I->second;
                }

        void dump( FILE *strm = stdout) const;

    private:
        string _fname;
        vector<pair<double, double>> _values;
        vector<pair<double, double>>::iterator _I;
};



class CSourcePeriodic : public C_BaseSource {

        DELETE_DEFAULT_METHODS (CSourcePeriodic)

    public:
        CSourcePeriodic (const string& name_, const string& fname_,
                         TSourceLoopingOption,
                         double period);

        TSourceLoopingOption is_looping;

        double operator() ( double at)
                {
                        size_t  i_abs = (size_t)(at / _period),
                                i_eff = (is_looping == TSourceLoopingOption::yes)
                                        ? i_abs % _values.size()
                                        : min (i_abs, _values.size() - 1);
                        return _values[i_eff];
                }

        void dump( FILE *strm = stdout) const;

        bool is_periodic()
                {  return true;  }
        double period() const
                {  return _period;  }

    private:
        string _fname;
        vector<double> _values;
        double _period;
};



class CSourceFunction : public C_BaseSource {
// not useful in Lua

        DELETE_DEFAULT_METHODS (CSourceFunction)

    public:
        CSourceFunction (const string& name_, double (*function_)(double))
              : C_BaseSource (name_, TSourceType::function), _function (function_)
                {}

        double operator() ( double at)
                {
                        return _function( at);
                }

        void dump( FILE *strm = stdout) const;

    private:
        double (*_function)( double at);
};



class CSourceNoise : public C_BaseSource {

        DELETE_DEFAULT_METHODS (CSourceNoise)

    public:
        enum class TDistribution { uniform, gaussian, };
        static const char * const distribution_s( TDistribution);
        static TDistribution distribution_by_name( const string&);

        CSourceNoise (const string& name_, double min_ = 0., double max_ = 1.,
                      double sigma_ = 1.,
                      TDistribution = TDistribution::uniform,
                      int seed = 0);
       ~CSourceNoise ();

        double operator() ( double unused);

        void dump( FILE *strm = stdout) const;

    private:
        double _min, _max, _sigma;
        TDistribution _dist_type;
        gsl_rng *_rng;
};

}

#endif

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