/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 CTTC
 *
 * 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: Nicola Baldo <nbaldo@cttc.es>
 */

#include <ns3/object.h>
#include <ns3/spectrum-interference.h>
#include <ns3/spectrum-error-model.h>
#include <ns3/log.h>
#include <ns3/test.h>
#include <ns3/simulator.h>
#include <ns3/packet.h>
#include <ns3/ptr.h>
#include <iostream>

NS_LOG_COMPONENT_DEFINE ("SpectrumInterferenceTest");

namespace ns3 {




class SpectrumInterferenceTestCase : public TestCase
{
public:
  SpectrumInterferenceTestCase (Ptr<SpectrumValue> s, uint32_t txBytes, bool rxCorrect, std::string name);
  virtual ~SpectrumInterferenceTestCase ();
  virtual void DoRun (void);
  void RetrieveTestResult (SpectrumInterference* si);

private:
  Ptr<SpectrumValue> m_s;
  uint32_t m_txBytes;
  uint32_t m_rxCorrectKnownOutcome;
  bool m_error;
  Ptr<const SpectrumModel> m_mySpectrumModel;
};



SpectrumInterferenceTestCase::SpectrumInterferenceTestCase (Ptr<SpectrumValue> s, uint32_t txBytes, bool rxCorrect, std::string name)
  : TestCase (name),
    m_s (s),
    m_txBytes (txBytes),
    m_rxCorrectKnownOutcome (rxCorrect),
    m_mySpectrumModel (s->GetSpectrumModel ())
{
}

SpectrumInterferenceTestCase::~SpectrumInterferenceTestCase ()
{
}



void
SpectrumInterferenceTestCase::DoRun (void)
{
  Ptr<SpectrumValue> n = Create<SpectrumValue> (m_mySpectrumModel);
  Ptr<SpectrumValue> i1 = Create<SpectrumValue> (m_mySpectrumModel);
  Ptr<SpectrumValue> i2 = Create<SpectrumValue> (m_mySpectrumModel);
  Ptr<SpectrumValue> i3 = Create<SpectrumValue> (m_mySpectrumModel);
  Ptr<SpectrumValue> i4 = Create<SpectrumValue> (m_mySpectrumModel);


  (*n)[0] = 5.000000000000e-19;
  (*n)[1] = 4.545454545455e-19;

  (*i1)[0] = 5.000000000000e-18;
  (*i2)[0] = 5.000000000000e-16;
  (*i3)[0] = 1.581138830084e-16;
  (*i4)[0] = 7.924465962306e-17;
  (*i1)[1] = 1.437398936440e-18;
  (*i2)[1] = 5.722388235428e-16;
  (*i3)[1] = 7.204059965732e-17;
  (*i4)[1] = 5.722388235428e-17;


  SpectrumInterference si;
  si.SetErrorModel (CreateObject<ShannonSpectrumErrorModel> ());
  si.SetNoisePowerSpectralDensity (n);

  Time ts  = Seconds (1);
  Time ds  = Seconds (1);
  Time ti1 = Seconds (0);
  Time di1 = Seconds (3);
  Time ti2 = Seconds (0.7);
  Time di2 = Seconds (1);
  Time ti3 = Seconds (1.2);
  Time di3 = Seconds (1);
  Time ti4 = Seconds (1.5);
  Time di4 = Seconds (0.1);

  Simulator::Schedule (ts,  &SpectrumInterference::AddSignal, &si, m_s,  ds);
  Simulator::Schedule (ti1, &SpectrumInterference::AddSignal, &si, i1, di1);
  Simulator::Schedule (ti2, &SpectrumInterference::AddSignal, &si, i2, di2);
  Simulator::Schedule (ti3, &SpectrumInterference::AddSignal, &si, i3, di3);
  Simulator::Schedule (ti4, &SpectrumInterference::AddSignal, &si, i4, di4);

  Ptr<Packet> p = Create<Packet> (m_txBytes);
  Simulator::Schedule (ts,  &SpectrumInterference::StartRx, &si, p, m_s);
  Simulator::Schedule (ts + ds,  &SpectrumInterferenceTestCase::RetrieveTestResult, this, &si);

  Simulator::Run ();
  // the above will return and after RetrieveTestResults have
  // been called and after all signals have expired
  Simulator::Destroy ();
}



void
SpectrumInterferenceTestCase::RetrieveTestResult (SpectrumInterference* si)
{

  bool rxOk = si->EndRx ();
  m_error =  (m_rxCorrectKnownOutcome != rxOk);

  std::ostringstream actualStream;
  actualStream << rxOk;
  std::ostringstream limitStream;
  limitStream << m_rxCorrectKnownOutcome;

  NS_LOG_LOGIC ("actual=" << actualStream.str () << " limit=" << limitStream.str ());

  if (m_error)
    {
      UpdateErrorStatus (m_error);
      ReportTestFailure (std::string ("wrong result"),
                         actualStream.str (),
                         limitStream.str (),
                         std::string ("no message"),
                         __FILE__, __LINE__);
    }

}



class SpectrumInterferenceTestSuite : public TestSuite
{
public:
  SpectrumInterferenceTestSuite ();
};

SpectrumInterferenceTestSuite::SpectrumInterferenceTestSuite ()
  : TestSuite ("spectrum-interference", UNIT)
{

  NS_LOG_INFO ("creating SpectrumInterferenceTestSuite");

  Ptr<const SpectrumModel> m;

  Bands bands;
  struct BandInfo bi;

  bi.fl = 2.400e9;
  bi.fc = 2.410e9;
  bi.fh = 2.420e9;
  bands.push_back (bi);

  bi.fl = 2.420e9;
  bi.fc = 2.431e9;
  bi.fh = 2.442e9;
  bands.push_back (bi);

  m = Create<SpectrumModel> (bands);


  double b; // max deliverable bytes

  const double e = 1e-5; // max tolerated relative error for
                         // deliverable bytes

  // Power Spectral Density of the signal of interest  = [-46 -48] dBm;
  Ptr<SpectrumValue> s1  = Create<SpectrumValue> (m);
  (*s1)[0] = 1.255943215755e-15;
  (*s1)[1] = 7.204059965732e-16;
  b = 10067205.5632012;
  AddTestCase (new SpectrumInterferenceTestCase (s1,   1, 1,            "sdBm  = [-46 -48]  tx bytes: 1"));
  AddTestCase (new SpectrumInterferenceTestCase (s1, static_cast<uint32_t> (b * 0.5 + 0.5), 1,   "sdBm  = [-46 -48]  tx bytes: b*0.5"));
  AddTestCase (new SpectrumInterferenceTestCase (s1, static_cast<uint32_t> (b * (1 - e) + 0.5), 1, "sdBm  = [-46 -48]  tx bytes: b*(1-e)"));
  AddTestCase (new SpectrumInterferenceTestCase (s1, static_cast<uint32_t> (b * (1 + e) + 0.5), 0, "sdBm  = [-46 -48]  tx bytes: b*(1+e)"));
  AddTestCase (new SpectrumInterferenceTestCase (s1, static_cast<uint32_t>(b * 1.5 + 0.5), 0,   "sdBm  = [-46 -48]  tx bytes: b*1.5"));
  AddTestCase (new SpectrumInterferenceTestCase (s1, 0xffffffff, 0,     "sdBm  = [-46 -48]  tx bytes: 2^32-1"));

  // Power Spectral Density of the signal of interest  =  [-63 -61] dBm;
  Ptr<SpectrumValue> s2  = Create<SpectrumValue> (m);
  (*s2)[0] = 2.505936168136e-17;
  (*s2)[1] = 3.610582885110e-17;
  b = 882401.591840728;
  AddTestCase (new SpectrumInterferenceTestCase (s2,   1, 1,            "sdBm  = [-63 -61]  tx bytes: 1"));
  AddTestCase (new SpectrumInterferenceTestCase (s2, static_cast<uint32_t> (b * 0.5 + 0.5), 1,   "sdBm  = [-63 -61]  tx bytes: b*0.5"));
  AddTestCase (new SpectrumInterferenceTestCase (s2, static_cast<uint32_t> (b * (1 - e) + 0.5), 1, "sdBm  = [-63 -61]  tx bytes: b*(1-e)"));
  AddTestCase (new SpectrumInterferenceTestCase (s2, static_cast<uint32_t> (b * (1 + e) + 0.5), 0, "sdBm  = [-63 -61]  tx bytes: b*(1+e)"));
  AddTestCase (new SpectrumInterferenceTestCase (s2, static_cast<uint32_t> (b * 1.5 + 0.5), 0,   "sdBm  = [-63 -61]  tx bytes: b*1.5"));
  AddTestCase (new SpectrumInterferenceTestCase (s2, 0xffffffff, 0,     "sdBm  = [-63 -61]  tx bytes: 2^32-1"));

}

static SpectrumInterferenceTestSuite spectrumInterferenceTestSuite;

} // namespace ns3
