/*
 *  stdp_connection_facetshw_hom.cpp
 *
 *  This file is part of NEST
 *
 *  Copyright (C) 2004 by
 *  The NEST Initiative
 *
 *  See the file AUTHORS for details.
 *
 *  Permission is granted to compile and modify
 *  this file for non-commercial use.
 *  See the file LICENSE for details.
 *
 */

#include "network.h"
#include "dictdatum.h"
#include "connector_model.h"
#include "common_synapse_properties.h"
#include "stdp_connection_facetshw_hom.h"
#include "event.h"

namespace nest
{
  //
  // Implementation of class STDPFACETSHWHomCommonProperties.
  //

  STDPFACETSHWHomCommonProperties::STDPFACETSHWHomCommonProperties() :
    CommonSynapseProperties(),

    tau_plus_(20.0),
    tau_minus_(20.0),

    Wmax_(100.0),

    no_synapses_(0),
    synapses_per_driver_(50),  //hardware efficiency of 50/256=20%,
                               //which is comparable to Fieres et al. (2008)
    driver_readout_time_(15.0) //in ms; measured on hardware

  {
    lookuptable_0_.resize(16);
    lookuptable_1_.resize(16);
    lookuptable_2_.resize(16);

    //intermediate Guetig (mu=0.4)
    //with r=4 bits and n=36 SSPs, see [3]
    lookuptable_0_.at(0) = 2;
    lookuptable_0_.at(1) = 3;
    lookuptable_0_.at(2) = 4;
    lookuptable_0_.at(3) = 4;
    lookuptable_0_.at(4) = 5;
    lookuptable_0_.at(5) = 6;
    lookuptable_0_.at(6) = 7;
    lookuptable_0_.at(7) = 8;
    lookuptable_0_.at(8) = 9;
    lookuptable_0_.at(9) = 10;
    lookuptable_0_.at(10) = 11;
    lookuptable_0_.at(11) = 12;
    lookuptable_0_.at(12) = 13;
    lookuptable_0_.at(13) = 14;
    lookuptable_0_.at(14) = 14;
    lookuptable_0_.at(15) = 15;

    lookuptable_1_.at(0) = 0;
    lookuptable_1_.at(1) = 0;
    lookuptable_1_.at(2) = 1;
    lookuptable_1_.at(3) = 2;
    lookuptable_1_.at(4) = 3;
    lookuptable_1_.at(5) = 4;
    lookuptable_1_.at(6) = 5;
    lookuptable_1_.at(7) = 6;
    lookuptable_1_.at(8) = 7;
    lookuptable_1_.at(9) = 8;
    lookuptable_1_.at(10) = 9;
    lookuptable_1_.at(11) = 10;
    lookuptable_1_.at(12) = 10;
    lookuptable_1_.at(13) = 11;
    lookuptable_1_.at(14) = 12;
    lookuptable_1_.at(15) = 13;

    for(size_t i = 0; i < lookuptable_0_.size(); ++i){
      lookuptable_2_.at(i) = i;
    }

    configbit_0_.resize(4);
    configbit_1_.resize(4);
    
    //see [4]
    configbit_0_.at(0) = 0;
    configbit_0_.at(1) = 0;
    configbit_0_.at(2) = 1;
    configbit_0_.at(3) = 0;
    configbit_1_.at(0) = 0;
    configbit_1_.at(1) = 1;
    configbit_1_.at(2) = 0;
    configbit_1_.at(3) = 0;

    reset_pattern_.resize(6);
    for(size_t i = 0; i < reset_pattern_.size(); ++i){
      reset_pattern_.at(i) = true;
    }
    
    weight_per_lut_entry_ = Wmax_ / (lookuptable_0_.size() - 1);
    calc_readout_cycle_duration_();
  }

  void STDPFACETSHWHomCommonProperties::calc_readout_cycle_duration_()
  {
    readout_cycle_duration_ = int((no_synapses_ - 1.0) / synapses_per_driver_ + 1.0) * driver_readout_time_;
    //std::cout << "stdp_connection_facetshw_hom::debug: readout cycle duration changed to " << readout_cycle_duration_ << std::endl;
  }

  void STDPFACETSHWHomCommonProperties::get_status(DictionaryDatum & d) const
  {
    CommonSynapseProperties::get_status(d);

    def<double_t>(d, "tau_plus", tau_plus_);
    def<double_t>(d, "tau_minus_stdp", tau_minus_);
    def<double_t>(d, "Wmax", Wmax_);
    def<double_t>(d, "weight_per_lut_entry", weight_per_lut_entry_);

    def<long_t>(d, "no_synapses", no_synapses_);
    def<long_t>(d, "synapses_per_driver", synapses_per_driver_);
    def<double_t>(d, "driver_readout_time", driver_readout_time_);
    def<double_t>(d, "readout_cycle_duration", readout_cycle_duration_);

    (*d)["lookuptable_0"] = IntVectorDatum(new std::vector<long_t>(lookuptable_0_));
    (*d)["lookuptable_1"] = IntVectorDatum(new std::vector<long_t>(lookuptable_1_));
    (*d)["lookuptable_2"] = IntVectorDatum(new std::vector<long_t>(lookuptable_2_));
    (*d)["configbit_0"] = IntVectorDatum(new std::vector<long_t>(configbit_0_));
    (*d)["configbit_1"] = IntVectorDatum(new std::vector<long_t>(configbit_1_));
    (*d)["reset_pattern"] = IntVectorDatum(new std::vector<long_t>(reset_pattern_));
  }
 
  void STDPFACETSHWHomCommonProperties::set_status(const DictionaryDatum & d, ConnectorModel &cm)
  {
    CommonSynapseProperties::set_status(d, cm);

    updateValue<double_t>(d, "tau_plus", tau_plus_);
    updateValue<double_t>(d, "tau_minus_stdp", tau_minus_);
    if(updateValue<double_t>(d, "Wmax", Wmax_)){
      weight_per_lut_entry_ = Wmax_ / (lookuptable_0_.size() - 1);
    }

    //TP: they should not be allowed to be changed! But needed for CopyModel ...
    updateValue<double_t>(d, "weight_per_lut_entry", weight_per_lut_entry_);
    updateValue<double_t>(d, "readout_cycle_duration", readout_cycle_duration_);
    if(updateValue<long_t>(d, "no_synapses", no_synapses_)){
      calc_readout_cycle_duration_();
    }

    if(updateValue<long_t>(d, "synapses_per_driver", synapses_per_driver_)){
      calc_readout_cycle_duration_();
    }
    if(updateValue<double_t>(d, "driver_readout_time", driver_readout_time_)){
      calc_readout_cycle_duration_();
    }

    if(d->known("lookuptable_0")){
      updateValue<std::vector<long_t> >(d, "lookuptable_0", lookuptable_0_);

      //right size?
      if(lookuptable_0_.size() != lookuptable_1_.size()){
        throw BadProperty("Look-up table has not 2^4 entries!");
      }

      //are look-up table entries out of bounds?
      for(size_t i = 0; i < size_t(lookuptable_0_.size()); ++i){
        if((lookuptable_0_[i] < 0) || (lookuptable_0_[i] > 15)){
          throw BadProperty("Look-up table entries must be integers in [0,15]");
        }
      }
    }
    if(d->known("lookuptable_1")){
      updateValue<std::vector<long_t> >(d, "lookuptable_1", lookuptable_1_);

      //right size?
      if(lookuptable_1_.size() != lookuptable_0_.size()){
        throw BadProperty("Look-up table has not 2^4 entries!");
      }

      //are look-up table entries out of bounds?
      for(size_t i = 0; i < size_t(lookuptable_1_.size()); ++i){
        if((lookuptable_1_[i] < 0) || (lookuptable_1_[i] > 15)){
          throw BadProperty("Look-up table entries must be integers in [0,15]");
        }
      }
    }
    if(d->known("lookuptable_2")){
      updateValue<std::vector<long_t> >(d, "lookuptable_2", lookuptable_2_);

      //right size?
      if(lookuptable_2_.size() != lookuptable_0_.size()){
        throw BadProperty("Look-up table has not 2^4 entries!");
      }

      //are look-up table entries out of bounds?
      for(size_t i = 0; i < size_t(lookuptable_2_.size()); ++i){
        if((lookuptable_2_[i] < 0) || (lookuptable_2_[i] > 15)){
          throw BadProperty("Look-up table entries must be integers in [0,15]");
        }
      }
    }

    if(d->known("configbit_0")){
      updateValue<std::vector<long_t> >(d, "configbit_0", configbit_0_);

      //right size?
      if(configbit_0_.size() != 4){
        throw BadProperty("Wrong number of configuration bits (!=4).");
      }
    }
    if(d->known("configbit_1")){
      updateValue<std::vector<long_t> >(d, "configbit_1", configbit_1_);

      //right size?
      if(configbit_1_.size() != 4){
        throw BadProperty("Wrong number of configuration bits (!=4).");
      }
    }
    if(d->known("reset_pattern")){
      updateValue<std::vector<long_t> >(d, "reset_pattern", reset_pattern_);

      //right size?
      if(reset_pattern_.size() != 6){
        throw BadProperty("Wrong number of reset bits (!=6).");
      }
    }
  }


  //
  // Implementation of class STDPFACETSHWConnectionHom.
  //

  STDPFACETSHWConnectionHom::STDPFACETSHWConnectionHom() :
    a_causal_(0.0),
    a_acausal_(0.0),
    a_thresh_th_(21.835), //exp(-10ms/20ms) * 36SSPs
    a_thresh_tl_(21.835),

    init_flag_(false),
    synapse_id_(0),
    next_readout_time_(0.0),
    discrete_weight_(0)
  { }

  STDPFACETSHWConnectionHom::STDPFACETSHWConnectionHom(const STDPFACETSHWConnectionHom &rhs) :
    ConnectionHetWD(rhs)
  {
    a_causal_ = rhs.a_causal_;
    a_acausal_ = rhs.a_acausal_;
    a_thresh_th_ = rhs.a_thresh_th_;
    a_thresh_tl_ = rhs.a_thresh_tl_;

    init_flag_ = rhs.init_flag_;
    synapse_id_ = rhs.synapse_id_;
    next_readout_time_ = rhs.next_readout_time_;
    discrete_weight_ = rhs.discrete_weight_;
  }

  void STDPFACETSHWConnectionHom::get_status(DictionaryDatum & d) const
  {
    // base class properties, different for individual synapse
    ConnectionHetWD::get_status(d);

    // own properties, different for individual synapse
    def<double_t>(d, "a_causal", a_causal_);
    def<double_t>(d, "a_acausal", a_acausal_);
    def<double_t>(d, "a_thresh_th", a_thresh_th_);
    def<double_t>(d, "a_thresh_tl", a_thresh_tl_);

    def<bool>(d, "init_flag", init_flag_);
    def<long_t>(d, "synapse_id", synapse_id_);
    def<double_t>(d, "next_readout_time", next_readout_time_);
    //useful to get conversion before activity, but weight_per_lut_entry_ not known here
    //def<uint_t>(d, "discrete_weight", entry_to_weight_(weight_to_entry_(weight_, weight_per_lut_entry_), weight_per_lut_entry_));
  }

  void STDPFACETSHWConnectionHom::set_status(const DictionaryDatum & d, ConnectorModel &cm)
  {
    // base class properties
    ConnectionHetWD::set_status(d, cm);

    updateValue<double_t>(d, "a_causal", a_causal_);
    updateValue<double_t>(d, "a_acausal", a_acausal_);
    updateValue<double_t>(d, "a_thresh_th", a_thresh_th_);
    updateValue<double_t>(d, "a_thresh_tl", a_thresh_tl_);

    updateValue<long_t>(d, "synapse_id", synapse_id_);

    //TP: they should not be allowed to be changed! But needed for CopyModel ...
    updateValue<bool>(d, "init_flag", init_flag_);
    updateValue<double_t>(d, "next_readout_time", next_readout_time_);

    //setting discrete_weight_ does not make sense, is temporary variable
  }

   /**
   * Set properties of this connection from position p in the properties
   * array given in dictionary.
   */
  void STDPFACETSHWConnectionHom::set_status(const DictionaryDatum & d, index p, ConnectorModel &cm)
  {
    ConnectionHetWD::set_status(d, p, cm);

     if (d->known("tau_pluss") ||
         d->known("tau_minus_stdps") ||
         d->known("Wmaxs") ||
         d->known("weight_per_lut_entrys") ||
         d->known("no_synapsess") ||
         d->known("synapses_per_drivers") ||
         d->known("driver_readout_times") ||
         d->known("readout_cycle_durations") ||
         d->known("lookuptable_0s") ||
         d->known("lookuptable_1s") ||
         d->known("lookuptable_2s") ||
         d->known("configbit_0s") ||
         d->known("configbit_1s") ||
         d->known("reset_patterns") )
     {
       cm.network().message(SLIInterpreter::M_ERROR, "STDPFACETSHWConnectionHom::set_status()", "you are trying to set common properties via an individual synapse.");
     }
  }

  void STDPFACETSHWConnectionHom::initialize_property_arrays(DictionaryDatum & d) const
  {
    ConnectionHetWD::initialize_property_arrays(d);
  }

  /**
   * Append properties of this connection to the given dictionary. If the
   * dictionary is empty, new arrays are created first.
   */
  void STDPFACETSHWConnectionHom::append_properties(DictionaryDatum & d) const
  {
    ConnectionHetWD::append_properties(d);
  }

} // of namespace nest