#include <time.h>
#include <chrono>
#include "ns3/internet-module.h"
#include "ns3/olsr-module.h"

#include "ns3/plc-module.h"
#include "ns3/ghn-plc-module.h"

using namespace ns3;
using namespace ghn;
using namespace std;

std::string
ConstructResFoldName (int argc, char *argv[]); //funkce pro vytvoření složky results na základě vstupních parametrů, využitá

int
main (int argc, char *argv[]) //vstup parametrů
{
  std::string resDir = ConstructResFoldName (argc, argv); //volání funkce, vytvoří složky pro výsledky na zálkadě vstupních argumentů
  RemoveDirectory (resDir); //
  CreateDirectory (resDir); //vymazání původních výsledků+vytvoření výsledků nových
  //
  // Set random seed value
  //
  typedef std::chrono::high_resolution_clock myclock; //vytvoření vlastního datového typu pro získání přesné synchronizace hodin
  myclock::time_point beginning = myclock::now (); //spuštění hodin
  myclock::duration d = myclock::now () - beginning;
  RngSeedManager::SetSeed (d.count ()); //nastaví počáteční seed pro případné generování pseudonáhodných hodnot

  uint16_t maxCwSize = 50; //maximální velikost okna
  BandPlanType bandplan = GDOTHN_BANDPLAN_25MHZ; //nastavení šířky pásma
  ModulationType modulace = QAM256; //typ modulace
  GhnPlcDllMacProtocol macMode = CSMA_CA; //přístupová metoda

  std::vector<double> vals; //uložení hodnot do vektoru pro jejich logování
  vals.push_back (macMode); // přístupová metoda
  std::string logID = GetLogIdent (vals); //řetězec sloužící pro popis
  //souboru app_data_0_modem_distance_topology_macMode
  cout << "Initialized the constants.." << endl;

  GhnPlcHelper devHelper (bandplan, modulace); //vložení šířky pásma a modulace do devHelperu
  Ptr<const SpectrumModel> sm = devHelper.GetSpectrumModel (); //definice spektrálního modelu,

  Ptr<PLC_Cable> cablePLC_AXEKCY240 = CreateObject<PLC_AXEKCY240>(sm);
  Ptr<PLC_Cable> cablePLC_AXEKVCEY240 = CreateObject<PLC_AXEKVCEY240>(sm);

  Ptr<PLC_ConstImpedance> vysilac = Create<PLC_ConstImpedance> (sm,PLC_Value(40,0)); //dle zdroje
  Ptr<PLC_ConstImpedance> prijimac = Create<PLC_ConstImpedance> (sm,PLC_Value(100,0)); //dle zdroje
  
  Ptr<PLC_Node> n1 = CreateObject<PLC_Node> ();
  n1->SetPosition(0,0,0);
  n1->SetImpedance(vysilac);
  n1->SetName("DTS7");

  Ptr<PLC_Node> n2 = CreateObject<PLC_Node> ();
  n2->SetPosition(51.1,0,0);
  n2->SetName("Sp1");

  Ptr<PLC_Node> n3 = CreateObject<PLC_Node> ();
  n3->SetPosition(117.1,0,0);
  n3->SetName("Sp2");

  Ptr<PLC_Node> n4 = CreateObject<PLC_Node> ();
  n4->SetPosition(146.4,0,0);
  n4->SetName("Sp3");

  Ptr<PLC_Node> n5 = CreateObject<PLC_Node> ();
  n5->SetPosition(148.9,0,0);
  n5->SetName("Sp4");

  Ptr<PLC_Node> n6 = CreateObject<PLC_Node> ();
  n6->SetPosition(859.3,0,0);
  n6->SetName("Sp5");

  Ptr<PLC_Node> n7 = CreateObject<PLC_Node> ();
  n7->SetPosition(876.8,0,0);
  n7->SetImpedance(prijimac);
  n7->SetName("DTS8");


  PLC_NodeList node_list;
  node_list.push_back(n1);
  node_list.push_back(n2);
  node_list.push_back(n3);
  node_list.push_back(n4);
  node_list.push_back(n5);
  node_list.push_back(n6);
  node_list.push_back(n7);


  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n1, n2);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n2, n3);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n3, n4);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n4, n5);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n5, n6);
  CreateObject<PLC_Line> (cablePLC_AXEKCY240, n6, n7);


  Ptr<SpectrumValue> noiseFloor= Create<SpectrumValue> (sm);

  //*(noiseFloor) = 1e-16; //-130 dBm/Hz hodnota šumu pro VN
  *(noiseFloor) = 3.16e-14; //-105 dBm/Hz hodnota šumu pro VN, normální podmínky
  //*(noiseFloor) = 1e-12; //-90 dBm/Hz
  //*(noiseFloor) = 1e-10; //-70 dBm/Hz

  PLC_ChannelHelper channelHelper (sm);
  channelHelper.Install (node_list); //Urychlení nastavení pomocí instalace channelHelperu
  cout << "Created channel.." << endl;

  Ptr<GhnPlcStats> ncStats = CreateObject<GhnPlcStats> (); //objekt pro statistiky

  Ptr<PLC_Channel> channel;
  channel = channelHelper.GetChannel();

  //
  // Získání CTF z uzlu x do y, pro získání třeba odkomentoval. Pro funkčnost simulátoru musí zůstat zakomentovaná
  //
/*
  Ptr<PLC_Node> vysilaci = node_list.at(0);
  Ptr<PLC_Node> prijimaci = node_list.at(12);

  Ptr<PLC_TxInterface> txIfn1 = CreateObject<PLC_TxInterface> (vysilaci, sm);
  Ptr<PLC_RxInterface> rxIfn2 = CreateObject<PLC_RxInterface> (prijimaci, sm);

  txIfn1->SetTxIfIdx(1);
  rxIfn2->SetRxIfIdx(1);

  //přidání rozhraní do kanálu pro výpočet CTF
  channel->AddTxInterface(txIfn1);
  channel->AddRxInterface(rxIfn2);

  //inicializace a výpočet CTF
  channel->InitTransmissionChannels();
  channel->CalcTransmissionChannels();

  string nazev = vysilaci->GetName() +"-do-"+ prijimaci->GetName();
  PLC_OutputHelper outputHelper;

  Ptr<PLC_TransferBase>ctv=channel->GetChannelTransferData(1,1);
  outputHelper.PrintBode(nazev, ctv, sm);

*/


  //
  // Create communication devices
  //
  devHelper.SetNodeList (node_list); //Vytvoření seznamu do devhelperu
  if (macMode == CSMA_CD)
    {
      devHelper.DefinePhyType (GhnPlcPhyPmdFullD::GetTypeId ());
      devHelper.DefineMacType (GhnPlcDllMacCsmaCd::GetTypeId ());
    }
  devHelper.SetNoiseFloor (noiseFloor); //volba šumu
  devHelper.SetChannel (channelHelper.GetChannel ()); //volba kanálu
  devHelper.SetGhnPlcStats (ncStats); //statistiky pro GhnPLC
  devHelper.DefineLlcFlowType (GhnPlcLlcCodedFlow::GetTypeId ()); //LLC model
  devHelper.DefineBitLoadingType (NcBlVarBatMap::GetTypeId ()); //NcBlVarBatMap pro alokaci s BER, NcBlVarTxPsd pro Water Filling
  devHelper.SetResDirectory (resDir); //nastavení složky pro výsledky
  devHelper.SetMaxCwSize (maxCwSize); //nastavení max velikosti CW

  devHelper.Setup (); //instalace devhelperu na základě zadaných hodnot, nastavuje ostatní parametry fyzické a logické vrstvy!!!!!!!!!!!!!!!!!!
  cout << "Konec" << endl;
  

  return EXIT_SUCCESS;
}

std::string
ConstructResFoldName (int argc, char *argv[])
{
  std::stringstream ss;
  std::string path = argv[0]; // get path from argument 0
  path = path.substr (0, path.rfind ("/") + 1);
  ss << path << "Results";

    for (uint16_t i = 1; i < argc; i++)
      {
        ss << "_" << argv[i];
      }
  ss << "/";
  return ss.str ();
}


