/*
 *  sinusoidal_poisson_generator.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef SINUSOIDAL_POISSON_GENERATOR_H
#define SINUSOIDAL_POISSON_GENERATOR_H

#include "nest.h"
#include "event.h"
#include "node.h"
#include "stimulating_device.h"
#include "poisson_randomdev.h"
#include "connection.h"
#include "universal_data_logger.h"

namespace nest {

  class Network;

  /* BeginDocumentation
     Name: sinusoidal_poisson_generator - Generates sinusoidally modulated Poisson spike trains.

     Description:
     sinusoidal_poisson_generator generates sinusoidally modulated Poisson spike trains. By default,
     each target of the generator will receive a different spike train.

     The instantaneous rate of the process is given by

         f(t) = max(0, dc + ac sin ( 2 pi freq t + phi )) >= 0

     Parameters: 
     The following parameters can be set in the status dictionary:

     dc         double - Mean firing rate in spikes/second, default: 0 s^-1
     ac         double - Firing rate modulation amplitude in spikes/second, default: 0 s^-1
     freq       double - Modulation frequency in Hz, default: 0 Hz
     phi        double - Modulation phase in radian, default: 0
  
     individual_spike_trains   bool - See note below, default: true

     Remarks:
     - If ac > dc, firing rate is cut off at zero. In this case, the mean
       firing rate will be less than dc.
     - The state of the generator is reset on calibration.
     - The generator does not support precise spike timing.
     - You can use the multimeter to sample the rate of the generator.
     - The generator will create different trains if run at different
       temporal resolutions.

     - Individual spike trains vs single spike train:
       By default, the generator sends a different spike train to each of its targets.
       If /individual_spike_trains is set to false using either SetDefaults or CopyModel
       before a generator node is created, the generator will send the same spike train
       to all of its targets.

     Receives: DataLoggingRequest

     Sends: SpikeEvent

     FirstVersion: July 2006, Oct 2009, May 2013
     Author: Hans Ekkehard Plesser
     SeeAlso: poisson_generator, sinusoidal_gamma_generator
  */

  class sinusoidal_poisson_generator: public Node
  {
    
  public:        
    
    sinusoidal_poisson_generator();
    sinusoidal_poisson_generator(const sinusoidal_poisson_generator&);

    using Node::handle;
    using Node::connect_sender;

    port check_connection(Connection&, port);

    void handle(DataLoggingRequest &);
    port connect_sender(DataLoggingRequest &, port);

    void get_status(DictionaryDatum &) const;
    void set_status(const DictionaryDatum &) ;

    //! Model can be switched between proxies (single spike train) and not
    bool has_proxies() const { return not P_.individual_spike_trains_; }

    //! Allow multimeter to connect to local instances
    bool local_receiver() const { return true; } 
    
  private:
    void init_state_(const Node&);
    void init_buffers_();
    void calibrate();
    void event_hook(DSSpikeEvent&);

    void update(Time const &, const long_t, const long_t);
    
    struct Parameters_ {
      /** temporal frequency in radian/ms. */
      double_t om_;
            
      /** phase in radian */
      double_t phi_;
      
      /** DC amplitude in spikes/s */
      double_t dc_;
      
      /** AC amplitude in spikes/s */
      double_t ac_;

      /** Emit individual spike trains for each target, or same for all? */
      bool individual_spike_trains_;

      Parameters_();  //!< Sets default parameter values
      Parameters_(const Parameters_& );
      Parameters_& operator=(const Parameters_& p); // Copy constructor EN

      void get(DictionaryDatum&) const;  //!< Store current values in dictionary
      
      /**
       * Set values from dicitonary.
       * @note State is passed so that the position can be reset if the 
       *       spike_times_ vector has been filled with new data.
       */
      void set(const DictionaryDatum&, const sinusoidal_poisson_generator&);  
    };

    // ------------------------------------------------------------
    
    /**
     * State
     */
    struct State_ {      

      double_t y_0_;      //!< Two-component oscillator state vector, see Rotter&Diesmann
      double_t y_1_;

      double_t rate_;    //!< current rate, kept for recording

      State_();  //!< Sets default state value

      void get(DictionaryDatum&) const;  //!< Store current values in dictionary
      void set(const DictionaryDatum&, const Parameters_&);  //!< Set values from dicitonary
    };

    // ------------------------------------------------------------

    // These friend declarations must be precisely here.
    friend class RecordablesMap<sinusoidal_poisson_generator>;
    friend class UniversalDataLogger<sinusoidal_poisson_generator>;

    // ---------------------------------------------------------------- 

    /**
     * Buffers of the model.
     */
    struct Buffers_ {
      Buffers_(sinusoidal_poisson_generator&);
      Buffers_(const Buffers_&, sinusoidal_poisson_generator&);
      UniversalDataLogger<sinusoidal_poisson_generator> logger_;
    };

    // ------------------------------------------------------------

    struct Variables_ {
      librandom::PoissonRandomDev poisson_dev_;  //!< random deviate generator
      
      double_t h_;    //! time resolution (ms)
      double_t sin_;  //!< sin(h om) in propagator
      double_t cos_;  //!< cos(h om) in propagator
    };

    double_t get_rate_() const { return 1000.0 * S_.rate_; }

    // ------------------------------------------------------------

    StimulatingDevice<SpikeEvent> device_;
    static RecordablesMap<sinusoidal_poisson_generator> recordablesMap_;

    Parameters_ P_;
    State_      S_;
    Variables_  V_;
    Buffers_    B_;

  };

  inline
    port sinusoidal_poisson_generator::check_connection(Connection& c, port receptor_type)
    {
      if ( P_.individual_spike_trains_ )
      {
	DSSpikeEvent e;
	e.set_sender(*this);
	c.check_event(e);
	return c.get_target()->connect_sender(e, receptor_type);
      }
      else
      {
	SpikeEvent e;
	e.set_sender(*this);
	c.check_event(e);
	return c.get_target()->connect_sender(e, receptor_type);
      }
    }

  inline
    port sinusoidal_poisson_generator::connect_sender(DataLoggingRequest& dlr, 
				      port receptor_type)
    {
      if (receptor_type != 0)
	throw UnknownReceptorType(receptor_type, get_name());
      return B_.logger_.connect_logging_device(dlr, recordablesMap_);
    }

  inline
    void sinusoidal_poisson_generator::get_status(DictionaryDatum &d) const
  {
    P_.get(d);
    S_.get(d);
    device_.get_status(d);
    (*d)[names::recordables] = recordablesMap_.get_list();
  }

  inline
    void sinusoidal_poisson_generator::set_status(const DictionaryDatum &d)
  {
    Parameters_ ptmp = P_;  // temporary copy in case of errors

    ptmp.set(d, *this);     // throws if BadProperty
    // We now know that ptmp is consistent. We do not write it back
    // to P_ before we are also sure that the properties to be set
    // in the parent class are internally consistent.
    device_.set_status(d);

    // if we get here, temporaries contain consistent set of properties
    P_ = ptmp;

  }

} // namespace

#endif // sinusoidal_poisson_generator_H