/*
 *  mymodule.cpp
 *
 *  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/>.
 *
 */

// include necessary NEST headers
#include "config.h"
#include "network.h"
#include "model.h"
#include "dynamicloader.h"
#include "genericmodel.h"
#include "generic_connector.h"
#include "booldatum.h"
#include "integerdatum.h"
#include "tokenarray.h"
#include "exceptions.h"
#include "sliexceptions.h"
#include "nestmodule.h"

// include headers with your own stuff
#include "mymodule.h"
#include "pif_psc_alpha.h"
#include "drop_odd_spike_connection.h"

// -- Interface to dynamic module loader ---------------------------------------

/*
 * The dynamic module loader must be able to find your module.
 * You make the module known to the loader by defining an instance of your
 * module class in global scope. This instance must have the name
 *
 * <modulename>_LTX_mod
 *
 * The dynamicloader can then load modulename and search for symbol "mod" in it.
 */

mynest::MyModule mymodule_LTX_mod;

// -- DynModule functions ------------------------------------------------------

mynest::MyModule::MyModule()
  {
#ifdef LINKED_MODULE
     // register this module at the dynamic loader
     // this is needed to allow for linking in this module at compile time
     // all registered modules will be initialized by the main app's dynamic loader
     nest::DynamicLoaderModule::registerLinkedModule(this);
#endif
   }

mynest::MyModule::~MyModule()
   {
   }

   const std::string mynest::MyModule::name(void) const
   {
     return std::string("My NEST Module"); // Return name of the module
   }

   const std::string mynest::MyModule::commandstring(void) const
   {
     /* 1. Tell interpreter that we provide the C++ part of MyModule with the
           current revision number.
        2. Instruct the interpreter to check that mymodule-init.sli exists,
           provides at least version 1.0 of the SLI interface to MyModule, and
           to load it.
      */
     return std::string(
       "/mymodule /C++ ($Revision: 10071 $) provide-component "
       "/mymodule /SLI (7165) require-component"
       );
   }

   /* BeginDocumentation
      Name: StepPatternConnect - Connect sources and targets with a stepping pattern

      Synopsis:
      [sources] source_step [targets] target_step synmod StepPatternConnect -> n_connections

      Parameters:
      [sources]     - Array containing GIDs of potential source neurons
      source_step   - Make connection from every source_step'th neuron
      [targets]     - Array containing GIDs of potential target neurons
      target_step   - Make connection to every target_step'th neuron
      synmod        - The synapse model to use (literal, must be key in synapsedict)
      n_connections - Number of connections made

      Description:
      This function subsamples the source and target arrays given with steps
      source_step and target_step, beginning with the first element in each array,
      and connects the selected nodes.

      Example:
      /first_src 0 /network_size get def
      /last_src /iaf_neuron 20 Create def  % nodes  1 .. 20
      /src [first_src last_src] Range def
      /last_tgt /iaf_neuron 10 Create def  % nodes 21 .. 30
      /tgt [last_src 1 add last_tgt] Range def

      src 6 tgt 4 /drop_odd_spike StepPatternConnect

      This connects nodes [1, 7, 13, 19] as sources to nodes [21, 25,
      29] as targets using synapses of type drop_odd_spike, and
      returning 12 as the number of connections.  The following
      command will print the connections (you must paste the SLI
      command as one long line):

      src { /s Set << /source s >> GetConnections { cva 1 get } Map dup length 0 gt { cout s <- ( -> ) <- exch <-- endl } if ; } forall
      1 -> [21 25 29]
      7 -> [21 25 29]
      13 -> [21 25 29]
      19 -> [21 25 29]

      Remark:
      This function is only provided as an example for how to write your own
      interface function.

      Author:
      Hans Ekkehard Plesser

      SeeAlso:
      Connect, ConvergentConnect, DivergentConnect
   */
   void mynest::MyModule::StepPatternConnect_Vi_i_Vi_i_lFunction::execute(SLIInterpreter *i) const
   {
     // Check if we have (at least) five arguments on the stack.
     i->assert_stack_load(5);

     // Retrieve source, source step, target, target step from the stack
     const TokenArray sources = getValue<TokenArray> (i->OStack.pick(4)); // bottom
     const long src_step      = getValue<long>       (i->OStack.pick(3));
     const TokenArray targets = getValue<TokenArray> (i->OStack.pick(2));
     const long tgt_step      = getValue<long>       (i->OStack.pick(1));
     const Name synmodel_name = getValue<std::string>(i->OStack.pick(0)); // top

     // Obtain synapse model index
     const Token synmodel
       = nest::NestModule::get_network().get_synapsedict().lookup(synmodel_name);
     if ( synmodel.empty() )
       throw nest::UnknownSynapseType(synmodel_name.toString());
     const nest::index synmodel_id = static_cast<nest::index>(synmodel);

     // Build a list of targets with the given step
     TokenArray selected_targets;
     for ( size_t t = 0 ; t < targets.size() ; t += tgt_step )
       selected_targets.push_back(targets[t]);

     // Now connect all appropriate sources to this list of targets
     size_t Nconn = 0;  // counts connections
     for ( size_t s = 0 ; s < sources.size() ; s += src_step )
     {
       // We must first obtain the GID of the source as integer
       const nest::long_t sgid = getValue<nest::long_t>(sources[s]);

       // nest::network::divergent_connect() requires weight and delay arrays. We want to use
       // default values from the synapse model, so we pass empty arrays.
       nest::NestModule::get_network().divergent_connect(sgid, selected_targets,
							 TokenArray(), TokenArray(),
							 synmodel_id);
       Nconn += selected_targets.size();
     }

     // We get here only if none of the operations above throws and exception.
     // Now we can safely remove the arguments from the stack and push Nconn
     // as our result.
     i->OStack.pop(5);
     i->OStack.push(Nconn);

     // Finally, we pop the call to this functions from the execution stack.
     i->EStack.pop();
   }

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

  void mynest::MyModule::init(SLIInterpreter *i, nest::Network*)
  {
    /* Register a neuron or device model.
       Give node type as template argument and the name as second argument.
       The first argument is always a reference to the network.
       Return value is a handle for later unregistration.
    */
    nest::register_model<pif_psc_alpha>(nest::NestModule::get_network(),
                                        "pif_psc_alpha");

    /* Register a synapse type.
       Give synapse type as template argument and the name as second argument.
       The first argument is always a reference to the network.
    */
    nest::register_prototype_connection<DropOddSpikeConnection>(nest::NestModule::get_network(),
                                                       "drop_odd_synapse");

    /* Register a SLI function.
       The first argument is the function name for SLI, the second a pointer to
       the function object. If you do not want to overload the function in SLI,
       you do not need to give the mangled name. If you give a mangled name, you
       should define a type trie in the mymodule-init.sli file.
    */
    i->createcommand("StepPatternConnect_Vi_i_Vi_i_l",
                     &stepPatternConnect_Vi_i_Vi_i_lFunction);

  }  // MyModule::init()