/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2005,2006 INRIA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
 */
#include "ns3/core-config.h"
#include "simulator.h"
#include "simulator-impl.h"
#include "scheduler.h"
#include "map-scheduler.h"
#include "event-impl.h"

#include "ptr.h"
#include "string.h"
#include "object-factory.h"
#include "global-value.h"
#include "assert.h"
#include "log.h"

#include <math.h>
#include <fstream>
#include <list>
#include <vector>
#include <iostream>

NS_LOG_COMPONENT_DEFINE ("Simulator");

namespace ns3 {

GlobalValue g_simTypeImpl = GlobalValue ("SimulatorImplementationType", 
                                         "The object class to use as the simulator implementation",
                                         StringValue ("ns3::DefaultSimulatorImpl"),
                                         MakeStringChecker ());

GlobalValue g_schedTypeImpl = GlobalValue ("SchedulerType", 
                                           "The object class to use as the scheduler implementation",
                                           TypeIdValue (MapScheduler::GetTypeId ()),
                                           MakeTypeIdChecker ());

static void
TimePrinter (std::ostream &os)
{
  os << Simulator::Now ().GetSeconds () << "s";
}

static void
NodePrinter (std::ostream &os)
{
  if (Simulator::GetContext () == 0xffffffff)
    {
      os << "-1";
    }
  else
    {
      os << Simulator::GetContext ();
    }
}

static SimulatorImpl **PeekImpl (void)
{
  static SimulatorImpl *impl = 0;
  return &impl;
}

static SimulatorImpl * GetImpl (void)
{
  SimulatorImpl **pimpl = PeekImpl ();
  /* Please, don't include any calls to logging macros in this function
   * or pay the price, that is, stack explosions.
   */
  if (*pimpl == 0)
    {
      {
        ObjectFactory factory;
        StringValue s;

        g_simTypeImpl.GetValue (s);
        factory.SetTypeId (s.Get ());
        *pimpl = GetPointer (factory.Create<SimulatorImpl> ());
      }
      {
        ObjectFactory factory;
        StringValue s;
        g_schedTypeImpl.GetValue (s);
        factory.SetTypeId (s.Get ());
        (*pimpl)->SetScheduler (factory);
      }

//
// Note: we call LogSetTimePrinter _after_ creating the implementation
// object because the act of creation can trigger calls to the logging 
// framework which would call the TimePrinter function which would call 
// Simulator::Now which would call Simulator::GetImpl, and, thus, get us 
// in an infinite recursion until the stack explodes.
//
      LogSetTimePrinter (&TimePrinter);
      LogSetNodePrinter (&NodePrinter);
    }
  return *pimpl;
}

void
Simulator::Destroy (void)
{
  NS_LOG_FUNCTION_NOARGS ();

  SimulatorImpl **pimpl = PeekImpl (); 
  if (*pimpl == 0)
    {
      return;
    }
  /* Note: we have to call LogSetTimePrinter (0) below because if we do not do
   * this, and restart a simulation after this call to Destroy, (which is 
   * legal), Simulator::GetImpl will trigger again an infinite recursion until
   * the stack explodes.
   */
  LogSetTimePrinter (0);
  LogSetNodePrinter (0);
  (*pimpl)->Destroy ();
  (*pimpl)->Unref ();
  *pimpl = 0;
}

void
Simulator::SetScheduler (ObjectFactory schedulerFactory)
{
  NS_LOG_FUNCTION (schedulerFactory);
  GetImpl ()->SetScheduler (schedulerFactory);
}

bool 
Simulator::IsFinished (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  return GetImpl ()->IsFinished ();
}

void 
Simulator::Run (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  GetImpl ()->Run ();
}

void 
Simulator::Stop (void)
{
  NS_LOG_LOGIC ("stop");
  GetImpl ()->Stop ();
}

void 
Simulator::Stop (Time const &time)
{
  NS_LOG_FUNCTION (time);
  GetImpl ()->Stop (time);
}

Time
Simulator::Now (void)
{
  /* Please, don't include any calls to logging macros in this function
   * or pay the price, that is, stack explosions.
   */
  return GetImpl ()->Now ();
}

Time
Simulator::GetDelayLeft (const EventId &id)
{
  NS_LOG_FUNCTION (&id);
  return GetImpl ()->GetDelayLeft (id);
}

EventId
Simulator::Schedule (Time const &time, const Ptr<EventImpl> &ev)
{
  NS_LOG_FUNCTION (time << ev);
  return DoSchedule (time, GetPointer (ev));
}

EventId
Simulator::ScheduleNow (const Ptr<EventImpl> &ev)
{
  NS_LOG_FUNCTION (ev);
  return DoScheduleNow (GetPointer (ev));
}
void
Simulator::ScheduleWithContext (uint32_t context, const Time &time, EventImpl *impl)
{
  return GetImpl ()->ScheduleWithContext (context, time, impl);
}
EventId
Simulator::ScheduleDestroy (const Ptr<EventImpl> &ev)
{
  NS_LOG_FUNCTION (ev);
  return DoScheduleDestroy (GetPointer (ev));
}
EventId 
Simulator::DoSchedule (Time const &time, EventImpl *impl)
{
  return GetImpl ()->Schedule (time, impl);
}
EventId 
Simulator::DoScheduleNow (EventImpl *impl)
{
  return GetImpl ()->ScheduleNow (impl);
}
EventId 
Simulator::DoScheduleDestroy (EventImpl *impl)
{
  return GetImpl ()->ScheduleDestroy (impl);
}


EventId
Simulator::Schedule (Time const &time, void (*f)(void))
{
  NS_LOG_FUNCTION (time << f);
  return DoSchedule (time, MakeEvent (f));
}

void
Simulator::ScheduleWithContext (uint32_t context, Time const &time, void (*f)(void))
{
  NS_LOG_FUNCTION (time << context << f);
  return ScheduleWithContext (context, time, MakeEvent (f));
}

EventId
Simulator::ScheduleNow (void (*f)(void))
{
  NS_LOG_FUNCTION (f);
  return DoScheduleNow (MakeEvent (f));
}

EventId
Simulator::ScheduleDestroy (void (*f)(void))
{
  NS_LOG_FUNCTION (f);
  return DoScheduleDestroy (MakeEvent (f));
}

void
Simulator::Remove (const EventId &ev)
{
  NS_LOG_FUNCTION (&ev);
  if (*PeekImpl () == 0)
    {
      return;
    }
  return GetImpl ()->Remove (ev);
}

void
Simulator::Cancel (const EventId &ev)
{
  NS_LOG_FUNCTION (&ev);
  if (*PeekImpl () == 0)
    {
      return;
    }
  return GetImpl ()->Cancel (ev);
}

bool 
Simulator::IsExpired (const EventId &id)
{
  NS_LOG_FUNCTION (&id);
  if (*PeekImpl () == 0)
    {
      return true;
    }
  return GetImpl ()->IsExpired (id);
}

Time Now (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  return Time (Simulator::Now ());
}

Time 
Simulator::GetMaximumSimulationTime (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  return GetImpl ()->GetMaximumSimulationTime ();
}

uint32_t
Simulator::GetContext (void)
{
  return GetImpl ()->GetContext ();
}

uint32_t
Simulator::GetSystemId (void)
{
  NS_LOG_FUNCTION_NOARGS ();

  if (*PeekImpl () != 0)
    {
      return GetImpl ()->GetSystemId ();
    }
  else
    {
      return 0;
    }
}

void
Simulator::SetImplementation (Ptr<SimulatorImpl> impl)
{
  if (*PeekImpl () != 0)
    {
      NS_FATAL_ERROR ("It is not possible to set the implementation after calling any Simulator:: function. Call Simulator::SetImplementation earlier or after Simulator::Destroy.");
    }
  *PeekImpl () = GetPointer (impl);
  // Set the default scheduler
  ObjectFactory factory;
  StringValue s;
  g_schedTypeImpl.GetValue (s);
  factory.SetTypeId (s.Get ());
  impl->SetScheduler (factory);
//
// Note: we call LogSetTimePrinter _after_ creating the implementation
// object because the act of creation can trigger calls to the logging 
// framework which would call the TimePrinter function which would call 
// Simulator::Now which would call Simulator::GetImpl, and, thus, get us 
// in an infinite recursion until the stack explodes.
//
  LogSetTimePrinter (&TimePrinter);
  LogSetNodePrinter (&NodePrinter);
}
Ptr<SimulatorImpl>
Simulator::GetImplementation (void)
{
  return GetImpl ();
}



} // namespace ns3

