/*
 *  music_event_handler.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 "config.h"

#ifdef HAVE_MUSIC

#include "music_event_handler.h"
#include "nest.h"
#include "event.h"
#include "communicator.h"
#include "network.h" // needed for event_impl.h to be included

namespace nest
{
  MusicEventHandler::MusicEventHandler()
          : music_port_(0),
            music_perm_ind_(0),
            published_(false),
            portname_(""),
            acceptable_latency_(0.0)
  {}

  MusicEventHandler::MusicEventHandler(std::string portname, double acceptable_latency, Network* net)
          : music_port_(0),
            music_perm_ind_(0),
            published_(false),
            portname_(portname),
            acceptable_latency_(acceptable_latency),
            net_(net)
  {}

  MusicEventHandler::~MusicEventHandler()
  {
    if (published_)
    {
      if (music_perm_ind_ != 0)
        delete music_perm_ind_;
      
      if (music_port_ != 0)
        delete music_port_;
    }
  }

  void MusicEventHandler::register_channel(int channel, nest::Node *mp)
  {
    if (channel >= channelmap_.size())
    {
      channelmap_.resize(channel + 1, 0); // all entries not explicitly set will be 0
      eventqueue_.resize(channel + 1);
    }
    
    if (channelmap_[channel] != 0)
      throw MUSICChannelAlreadyMapped("MusicEventHandler", portname_, channel);

    channelmap_[channel] = mp;
    indexmap_.push_back(channel);
  }

  void MusicEventHandler::publish_port()
  {
    if (!published_)
    {
      music_port_ = Communicator::get_music_setup()->publishEventInput(portname_);

      // MUSIC wants seconds, NEST has miliseconds
      double_t acceptable_latency = acceptable_latency_/1000.0;

      if (!music_port_->isConnected())
        throw MUSICPortUnconnected("MusicEventHandler", portname_);

      if (!music_port_->hasWidth())
	throw MUSICPortHasNoWidth("MusicEventHandler", portname_);

      unsigned int music_port_width = music_port_->width();

      // check, if all mappings are within the valid range of port width
      // the maximum channel mapped - 1 == size of channelmap
      if (channelmap_.size() > music_port_width)
        throw MUSICChannelUnknown("MusicEventHandler", portname_, channelmap_.size()-1);
    
      // create the permutation index mapping
      music_perm_ind_ = new MUSIC::PermutationIndex(&indexmap_.front(), indexmap_.size());
      // map the port
      music_port_->map(music_perm_ind_, this, acceptable_latency);

      std::string msg = String::compose("Mapping MUSIC input port '%1' with width=%2 and acceptable latency=%3 ms.",
                                        portname_, music_port_width, acceptable_latency_);
      net_->message(SLIInterpreter::M_INFO, "MusicEventHandler::publish_port()", msg.c_str());
    }
  }

  void MusicEventHandler::operator()(double t, MUSIC::GlobalIndex channel)
  {
    assert(channelmap_[channel] != 0);
    eventqueue_[channel].push(t*1e3); // MUSIC uses seconds as time unit
  }

  void MusicEventHandler::update(Time const & origin, const long_t from, const long_t to)
  {
    for (size_t channel = 0; channel < channelmap_.size(); ++channel)
      if (channelmap_[channel] != 0)
        while(! eventqueue_[channel].empty())
        {
          Time T = Time::ms(eventqueue_[channel].top());

          if (T > origin + Time::step(from) - Time::ms(acceptable_latency_) && T <= origin + Time::step(from + to))
          {
            nest::SpikeEvent se;
            se.set_offset(Time(Time::step(T.get_steps())).get_ms() - T.get_ms());
            se.set_stamp(T);
        
            channelmap_[channel]->handle(se); // deliver to the proxy for this channel
            eventqueue_[channel].pop();       // remove the sent event from the queue
          }
          else
            break;          
        }
  }

} // namespace nest

#endif // #ifdef HAVE_MUSIC