/* -*-    Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2014 University of Campinas (Unicamp)
 *
 * 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: Luciano Chaves <luciano@lrc.ic.unicamp.br>
 *         Vitor M. Eichemberger <vitor.marge@gmail.com>
 *
 * Two hosts connected to a four-switch loop topology (OpenFlow)
 * The switches are managed by the default learning controller application.
 */

#include <ns3/core-module.h>
#include <ns3/network-module.h>
#include <ns3/csma-module.h>
#include <ns3/internet-module.h>
#include <ns3/ofswitch13-module.h>
#include <ns3/internet-apps-module.h>

using namespace ns3;

int
main (int argc, char *argv[])
{
    uint16_t simTime = 10;
    bool verbose = false;
    bool trace = false;

    // Configure command line parameters
    CommandLine cmd;
    cmd.AddValue ("simTime", "Simulation time (seconds)", simTime);
    cmd.AddValue ("verbose", "Enable verbose output", verbose);
    cmd.AddValue ("trace", "Enable datapath stats and pcap traces", trace);
    cmd.Parse (argc, argv);

    if (verbose) {
        OFSwitch13Helper::EnableDatapathLogs ();
        LogComponentEnable ("OFSwitch13Interface", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13Device", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13Port", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13Queue", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13SocketHandler", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13Controller", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13LearningController", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13Helper", LOG_LEVEL_ALL);
        LogComponentEnable ("OFSwitch13InternalHelper", LOG_LEVEL_ALL);
    }

    // Enable checksum computations (required by OFSwitch13 module)
    GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true));

    // Create two host nodes
    NodeContainer hosts;
    hosts.Create (2);
    NetDeviceContainer hostDevices;

    // Create the switch nodes
    NodeContainer switches;
    switches.Create (4);
    NetDeviceContainer switchPorts [4];

    // Use the CsmaHelper to connect host nodes to the switch node
    CsmaHelper csmaHelper;
    csmaHelper.SetChannelAttribute ("DataRate", DataRateValue (DataRate ("100Mbps")));
    csmaHelper.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));

    NodeContainer pair (hosts.Get (0), switches.Get (0));    // h1 <==> s1
    NetDeviceContainer link = csmaHelper.Install (pair);
    hostDevices.Add (link.Get (0));
    switchPorts [0].Add (link.Get (1));
    pair = NodeContainer (hosts.Get (1), switches.Get (2)); // h2 <==> s3
    link = csmaHelper.Install (pair);
    hostDevices.Add (link.Get (0));
    switchPorts [2].Add (link.Get (1));

    // Create switch links
    pair = NodeContainer (switches.Get (0), switches.Get (1));    // s1 <==> s2
    link = csmaHelper.Install (pair);
    switchPorts [0].Add (link.Get (0));
    switchPorts [1].Add (link.Get (1));
    pair = NodeContainer (switches.Get (1), switches.Get (2));    // s2 <==> s3
    link = csmaHelper.Install (pair);
    switchPorts [1].Add (link.Get (0));
    switchPorts [2].Add (link.Get (1));
    pair = NodeContainer (switches.Get (2), switches.Get (3));    // s3 <==> s4
    link = csmaHelper.Install (pair);
    switchPorts [2].Add (link.Get (0));
    switchPorts [3].Add (link.Get (1));
    pair = NodeContainer (switches.Get (3), switches.Get (0));    // s4 <==> s1
    link = csmaHelper.Install (pair);
    switchPorts [3].Add (link.Get (0));
    switchPorts [0].Add (link.Get (1));

    // Create the controller node
    Ptr<Node> controllerNode = CreateObject<Node> ();

    // Configure the OpenFlow network domain
    Ptr<OFSwitch13InternalHelper> of13Helper = CreateObject<OFSwitch13InternalHelper> ();
    of13Helper->InstallController (controllerNode);
    for (size_t i = 0; i < switches.GetN (); ++i)
        of13Helper->InstallSwitch (switches.Get (i), switchPorts [i]);
    of13Helper->CreateOpenFlowChannels ();

    // Install the TCP/IP stack into hosts nodes
    InternetStackHelper internet;
    internet.Install (hosts);

    // Set IPv4 host addresses
    Ipv4AddressHelper ipv4helpr;
    Ipv4InterfaceContainer hostIpIfaces;
    ipv4helpr.SetBase ("10.1.1.0", "255.255.255.0");
    hostIpIfaces = ipv4helpr.Assign (hostDevices);

    // Configure ping application between hosts
    V4PingHelper pingHelper = V4PingHelper (hostIpIfaces.GetAddress (1));
    pingHelper.SetAttribute ("Verbose", BooleanValue (true));
    ApplicationContainer pingApps = pingHelper.Install (hosts.Get (0));
    pingApps.Start (Seconds (1));

    // Enable datapath stats and pcap traces at hosts, switch(es), and controller(s)
    if (trace) {
        of13Helper->EnableOpenFlowPcap ("openflow");
        of13Helper->EnableDatapathStats ("switch-stats");
        for (size_t i = 0; i < switches.GetN (); ++i)
            csmaHelper.EnablePcap ("switch", switchPorts[i], true);
        csmaHelper.EnablePcap ("host", hostDevices);
    }

    // Run the simulation
    Simulator::Stop (Seconds (simTime));
    Simulator::Run ();
    Simulator::Destroy ();
}
