/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 The Boeing Company
 *               2014 Universita' degli Studi di Napoli "Federico II"
 *
 * 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
 *
 */

// 
// This script configures two nodes on an 802.11b physical layer, with
// 802.11b NICs in adhoc mode. One of the nodes generates on-off traffic
// destined to the other node.
//
// The purpose is to test the energy depletion on the nodes and the 
// activation of the callback that puts a node in the sleep state when
// its energy is depleted. Furthermore, this script can be used to test
// the available policies for updating the transmit current based on
// the nominal tx power used to transmit each frame.
//
// There are a number of command-line options available to control
// the default behavior.  The list of available command-line options
// can be listed with the following command:
// ./waf --run "wifi-sleep --help"
//
// Note that all ns-3 attributes (not just the ones exposed in the below
// script) can be changed at command line; see the documentation.
//
// This script can also be helpful to put the Wifi layer into verbose
// logging mode; this command will turn on all wifi logging:
// 
// ./waf --run "wifi-sleep --verbose=1"
//
// When you are done, you will notice four trace files in your directory:
// two for the remaining energy on each node and two for the state transitions
// of each node.
//

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/mobility-module.h"
#include "ns3/config-store-module.h"
#include "ns3/wifi-module.h"
#include "ns3/internet-module.h"
#include "ns3/applications-module.h"
#include "ns3/energy-module.h"

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("WifiSleep");

template <int node>
void RemainingEnergyTrace (double oldValue, double newValue)
{
  std::stringstream ss;
  ss << "energy_" << node << ".log";

  static std::fstream f (ss.str().c_str(), std::ios::out);

  f << Simulator::Now().GetSeconds() << "    remaining energy=" << newValue << std::endl;
}

template <int node>
void PhyStateTrace (std::string context, Time start, Time duration, enum WifiPhy::State state)
{
  std::stringstream ss;
  ss << "state_" << node << ".log";

  static std::fstream f (ss.str().c_str(), std::ios::out);

  f << Simulator::Now().GetSeconds() << "    state=" << state << " start=" << start << " duration=" << duration << std::endl;
}



int main (int argc, char *argv[])
{
  std::string dataRate = "1Mbps";
  uint32_t packetSize = 1000; // bytes
  double duration = 10.0; // seconds
  double initialEnergy = 7.5; // joule
  double voltage = 3.0; // volts
  double txPowerStart = 0.0; // dbm
  double txPowerEnd = 15.0; // dbm
  uint32_t nTxPowerLevels = 16;
  uint32_t txPowerLevel = 0;
  double idleCurrent = 0.273; // Ampere
  double txCurrent = 0.380; // Ampere
  bool verbose = false;


  CommandLine cmd;

  cmd.AddValue ("dataRate", "Data rate", dataRate);
  cmd.AddValue ("packetSize", "size of application packet sent", packetSize);
  cmd.AddValue ("duration", "duration (seconds) of the experiment", duration);
  cmd.AddValue ("initialEnergy", "Initial Energy (Joule) of each node", initialEnergy);
  cmd.AddValue ("voltage", "Supply voltage (Joule)", voltage);
  cmd.AddValue ("txPowerStart", "Minimum available transmission level (dbm)", txPowerStart);
  cmd.AddValue ("txPowerEnd", "Maximum available transmission level (dbm)", txPowerEnd);
  cmd.AddValue ("nTxPowerLevels", "Number of transmission power levels available between txPowerStart and txPowerEnd included", nTxPowerLevels);
  cmd.AddValue ("txPowerLevel", "Transmission power level", txPowerLevel);
  cmd.AddValue ("idleCurrent", "The radio Idle current in Ampere", idleCurrent);
  cmd.AddValue ("txCurrent", "The radio Tx current in Ampere", txCurrent);
  cmd.AddValue ("verbose", "turn on all WifiNetDevice log components", verbose);

  cmd.Parse (argc, argv);


  NodeContainer c;
  c.Create (2);

  // The below set of helpers will help us to put together the wifi NICs we want
  WifiHelper wifi;
  if (verbose)
    {
      wifi.EnableLogComponents ();  // Turn on all Wifi logging
    }
  wifi.SetStandard (WIFI_PHY_STANDARD_80211b);

  YansWifiPhyHelper wifiPhy =  YansWifiPhyHelper::Default ();
  // ns-3 supports RadioTap and Prism tracing extensions for 802.11b
  wifiPhy.SetPcapDataLinkType (YansWifiPhyHelper::DLT_IEEE802_11_RADIO); 

  wifiPhy.Set ("TxPowerStart", DoubleValue (txPowerStart));
  wifiPhy.Set ("TxPowerEnd", DoubleValue (txPowerEnd));
  wifiPhy.Set ("TxPowerLevels", UintegerValue (nTxPowerLevels));

  YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
  wifiPhy.SetChannel (wifiChannel.Create ());

  // Add a non-QoS upper mac, and set the selected tx power level
  NqosWifiMacHelper wifiMac = NqosWifiMacHelper::Default ();
  wifi.SetRemoteStationManager ("ns3::ArfWifiManager", "DefaultTxPowerLevel", UintegerValue (txPowerLevel));
  // Set it to adhoc mode
  wifiMac.SetType ("ns3::AdhocWifiMac");
  NetDeviceContainer devices = wifi.Install (wifiPhy, wifiMac, c);

  MobilityHelper mobility;
  Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator> ();
  positionAlloc->Add (Vector (0.0, 0.0, 0.0));
  positionAlloc->Add (Vector (10.0, 0.0, 0.0));
  mobility.SetPositionAllocator (positionAlloc);
  mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
  mobility.Install (c);

  InternetStackHelper internet;
  internet.Install (c);

  Ipv4AddressHelper ipv4;
  NS_LOG_INFO ("Assign IP Addresses.");
  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
  Ipv4InterfaceContainer i = ipv4.Assign (devices);

  ApplicationContainer apps;

  std::string transportProto = std::string("ns3::UdpSocketFactory");
  OnOffHelper onOff(transportProto, InetSocketAddress (Ipv4Address ("10.1.1.2"), 9000));

  onOff.SetAttribute ("DataRate", DataRateValue (DataRate (dataRate)));
  onOff.SetAttribute ("PacketSize", UintegerValue (packetSize));
  onOff.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.001]"));

  apps = onOff.Install (c.Get (0));

  apps.Start (Seconds (0.01));
  apps.Stop (Seconds (duration));

  // Create a packet sink to receive these packets
  PacketSinkHelper sink (transportProto, InetSocketAddress (Ipv4Address::GetAny (), 9001));
  apps = sink.Install (c.Get (1));
  apps.Start (Seconds (0.01));
  apps.Stop (Seconds (duration));

  // Energy sources
  EnergySourceContainer eSources;
  BasicEnergySourceHelper basicSourceHelper;
  WifiRadioEnergyModelHelper radioEnergyHelper;

  basicSourceHelper.Set ("BasicEnergySourceInitialEnergyJ", DoubleValue (initialEnergy));
  basicSourceHelper.Set ("BasicEnergySupplyVoltageV", DoubleValue (voltage));

  radioEnergyHelper.Set ("IdleCurrentA", DoubleValue (idleCurrent));
  radioEnergyHelper.Set ("TxCurrentA", DoubleValue (txCurrent));

  // compute the efficiency of the power amplifier (eta) assuming that the provided value for tx current
  // corresponds to the minimum tx power level
  double eta = WifiTxCurrentModel::DbmToW (txPowerStart) / ((txCurrent - idleCurrent) * voltage);

  radioEnergyHelper.SetTxCurrentModel ("ns3::LinearWifiTxCurrentModel",
                                       "Voltage", DoubleValue (voltage),
                                       "IdleCurrent", DoubleValue (idleCurrent),
                                       "Eta", DoubleValue (eta));

  // install an energy source on each node
  for (NodeContainer::Iterator n = c.Begin(); n != c.End(); n++)
  {
    eSources.Add (basicSourceHelper.Install (*n));

    Ptr<WifiNetDevice> wnd;

    for (uint32_t i = 0; i < (*n)->GetNDevices (); ++i)
    {
      wnd = (*n)->GetDevice (i)->GetObject<WifiNetDevice> ();
      // if it is a WifiNetDevice
      if (wnd != 0)
      {
        // this device draws power from the last created energy source
        radioEnergyHelper.Install (wnd, eSources.Get (eSources.GetN()-1));
      }
    }
  }

  // Tracing
  eSources.Get (0)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<0>));
  eSources.Get (1)->TraceConnectWithoutContext ("RemainingEnergy", MakeCallback(&RemainingEnergyTrace<1>));

  Config::Connect ("/NodeList/0/DeviceList/*/Phy/State/State", MakeCallback (&PhyStateTrace<0>));
  Config::Connect ("/NodeList/1/DeviceList/*/Phy/State/State", MakeCallback (&PhyStateTrace<1>));

// wifiPhy.EnablePcap ("wifi-sleep", devices);

  Simulator::Stop (Seconds(duration+1));

  Simulator::Run ();
  Simulator::Destroy ();

  return 0;
}

