#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("T1");

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

  Ptr<PLC_Node> n3 = CreateObject<PLC_Node> ();
  n3->SetPosition(165.1,0,0);
  n3->SetImpedance(prijimac);
  n3->SetName("T2");

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

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

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

  Ptr<PLC_Node> n7 = CreateObject<PLC_Node> ();
  n7->SetPosition(414.3,0,0);
  n7->SetName("T2d");

  Ptr<PLC_Node> n8 = CreateObject<PLC_Node> ();
  n8->SetPosition(684.1,0,0);
  n8->SetImpedance(prijimac);
  n8->SetName("T3");

  Ptr<PLC_Node> n9 = CreateObject<PLC_Node> ();
  n9->SetPosition(687.0,0,0);
  n9->SetName("T3a");

  Ptr<PLC_Node> n10 = CreateObject<PLC_Node> ();
  n10->SetPosition(705.1,0,0);
  n10->SetName("T3b");

  Ptr<PLC_Node> n11 = CreateObject<PLC_Node> ();
  n11->SetPosition(1125.1,0,0);
  n11->SetImpedance(prijimac);
  n11->SetName("T4");

  Ptr<PLC_Node> n12 = CreateObject<PLC_Node> ();
  n12->SetPosition(1209.5,0,0);
  n12->SetName("T4a");

  Ptr<PLC_Node> n13 = CreateObject<PLC_Node> ();
  n13->SetPosition(1264.6,0,0);
  n13->SetImpedance(prijimac);
  n13->SetName("T5");

  Ptr<PLC_Node> n14 = CreateObject<PLC_Node> ();
  n14->SetPosition(1363.8,0,0);
  n14->SetImpedance(prijimac);
  n14->SetName("T6");

  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);
  node_list.push_back(n8);
  node_list.push_back(n9);
  node_list.push_back(n10);
  node_list.push_back(n11);
  node_list.push_back(n12);
  node_list.push_back(n13);
  node_list.push_back(n14);

  CreateObject<PLC_Line> (cablePLC_AXEKCY240, n1, n2);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n2, n3);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n3, n4);
  CreateObject<PLC_Line> (cablePLC_AXEKCY240, n4, n5);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n5, n6);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n6, n7);
  CreateObject<PLC_Line> (cablePLC_AXEKCY240, n7, n8);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n8, n9);
  CreateObject<PLC_Line> (cablePLC_AXEKCY240, n9, n10);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n10, n11);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n11, n12);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n12, n13);
  CreateObject<PLC_Line> (cablePLC_AXEKVCEY240, n13, n14);

  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 ();
}


