/*
Copyright (C) 2010, 2014 Srivats P.

This file is part of "Ostinato"

This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

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, see <http://www.gnu.org/licenses/>
*/

#include "protocolmanager.h"
#include "abstractprotocol.h"

#include "protocol.pb.h"

#include "mac.h"
#include "vlan.h"
#include "svlan.h"
#include "vlanstack.h"

// L2 Protos
#include "dot3.h"    
#include "llc.h"    
#include "dot2llc.h"
#include "snap.h"    
#include "dot2snap.h"
#include "eth2.h"    
#include "stp.h"

// L3 Protos
#include "arp.h"    
#include "ip4.h"    
#include "ip6.h"
#include "ip4over4.h"    
#include "ip4over6.h"    
#include "ip6over4.h"    
#include "ip6over6.h"    

// L4 Protos
#include "gre.h"
#include "icmp.h"    
#include "igmp.h"    
#include "mld.h"    
#include "tcp.h"    
#include "udp.h"    

// L5 Protos
#include "textproto.h"

// Special Protos
#include "hexdump.h"
#include "payload.h"
#include "sample.h"
#include "sign.h"
#include "userscript.h"

ProtocolManager *OstProtocolManager;

ProtocolManager::ProtocolManager()
{
    /*! \todo (LOW) calls to registerProtocol() should be done by the protocols
     themselves (once this is done remove the #includes for all the protocols)
     */
    registerProtocol(OstProto::Protocol::kMacFieldNumber,
            (void*) MacProtocol::createInstance);

    registerProtocol(OstProto::Protocol::kVlanFieldNumber,
            (void*) VlanProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kSvlanFieldNumber,
            (void*) SVlanProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kVlanStackFieldNumber,
            (void*) VlanStackProtocol::createInstance);

    registerProtocol(OstProto::Protocol::kEth2FieldNumber,
            (void*) Eth2Protocol::createInstance);
    registerProtocol(OstProto::Protocol::kDot3FieldNumber,
            (void*) Dot3Protocol::createInstance);
    registerProtocol(OstProto::Protocol::kLlcFieldNumber,
            (void*) LlcProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber,
            (void*) Dot2LlcProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kSnapFieldNumber,
            (void*) SnapProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber,
            (void*) Dot2SnapProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kStpFieldNumber,
            (void*) StpProtocol::createInstance);

    // Layer 3 Protocols
    registerProtocol(OstProto::Protocol::kArpFieldNumber,
            (void*) ArpProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kIp4FieldNumber,
            (void*) Ip4Protocol::createInstance);
    registerProtocol(OstProto::Protocol::kIp6FieldNumber,
            (void*) Ip6Protocol::createInstance);

    registerProtocol(OstProto::Protocol::kIp4over4FieldNumber,
            (void*) Ip4over4Protocol::createInstance);
    registerProtocol(OstProto::Protocol::kIp4over6FieldNumber,
            (void*) Ip4over6Protocol::createInstance);
    registerProtocol(OstProto::Protocol::kIp6over4FieldNumber,
            (void*) Ip6over4Protocol::createInstance);
    registerProtocol(OstProto::Protocol::kIp6over6FieldNumber,
            (void*) Ip6over6Protocol::createInstance);

    // Layer 4 Protocols
    registerProtocol(OstProto::Protocol::kGreFieldNumber,
            (void*) GreProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kIcmpFieldNumber,
            (void*) IcmpProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kIgmpFieldNumber,
            (void*) IgmpProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kMldFieldNumber,
            (void*) MldProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kTcpFieldNumber,
            (void*) TcpProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kUdpFieldNumber,
            (void*) UdpProtocol::createInstance);

    // Layer 5 Protocols
    registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber,
            (void*) TextProtocol::createInstance);

    // Special Protocols
    registerProtocol(OstProto::Protocol::kHexDumpFieldNumber,
            (void*) HexDumpProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kPayloadFieldNumber,
            (void*) PayloadProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kSampleFieldNumber,
            (void*) SampleProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kSignFieldNumber,
            (void*) SignProtocol::createInstance);
    registerProtocol(OstProto::Protocol::kUserScriptFieldNumber,
            (void*) UserScriptProtocol::createInstance);

    populateNeighbourProtocols();
}

ProtocolManager::~ProtocolManager()
{
    numberToNameMap.clear();
    nameToNumberMap.clear();
    neighbourProtocols.clear();
    factory.clear();
    QList<AbstractProtocol*> pl = protocolList.values();
    while (!pl.isEmpty())
        delete pl.takeFirst();
}

void ProtocolManager::registerProtocol(int protoNumber,
    void *protoInstanceCreator)
{
    AbstractProtocol *p;

    Q_ASSERT(!factory.contains(protoNumber));

    factory.insert(protoNumber, protoInstanceCreator);

    p = createProtocol(protoNumber, NULL);
    protocolList.insert(protoNumber, p);

    numberToNameMap.insert(protoNumber, p->shortName());
    nameToNumberMap.insert(p->shortName(), protoNumber);
}

void ProtocolManager::populateNeighbourProtocols()
{
    neighbourProtocols.clear();

    foreach(AbstractProtocol *p, protocolList)
    {
        if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone)
        {
            foreach(AbstractProtocol *q, protocolList)
            {
                if (q->protocolId(p->protocolIdType()))
                    neighbourProtocols.insert(
                        p->protocolNumber(), q->protocolNumber());
            }
        }
    }
}

bool ProtocolManager::isRegisteredProtocol(int protoNumber)
{
    return factory.contains(protoNumber);
}

AbstractProtocol* ProtocolManager::createProtocol(int protoNumber,
    StreamBase *stream, AbstractProtocol *parent)
{
    AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*);
    AbstractProtocol* p;

    pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*))
        factory.value(protoNumber);
    
    Q_ASSERT_X(pc != NULL, 
               __FUNCTION__, 
               qPrintable(QString("No Protocol Creator registered for protocol %1")
                    .arg(protoNumber)));

    p = (*pc)(stream, parent);

    return p;
}

AbstractProtocol* ProtocolManager::createProtocol(QString protoName,
    StreamBase *stream, AbstractProtocol *parent)
{
    return createProtocol(nameToNumberMap.value(protoName), stream, parent);
}

bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix)
{
    if (neighbourProtocols.contains(protoPrefix, protoSuffix))
        return true;
    else
        return false;
}

bool ProtocolManager::protocolHasPayload(int protoNumber)
{
    Q_ASSERT(protocolList.contains(protoNumber));

    return protocolList.value(protoNumber)->protocolHasPayload();
}

QStringList ProtocolManager::protocolDatabase()
{
    return numberToNameMap.values();
}

#if 0
void ProtocolManager::showFieldAttribs()
{
    QStringList protocolList = protocolDatabase();
    Stream stream;
    foreach(QString name, protocolList) {
        if (name.contains("/")) // assume combo
            continue;
        AbstractProtocol *protocol = OstProtocolManager->createProtocol(name, &stream);
        int count = protocol->fieldCount();
        for (int i = 0; i < count; i++) {
            if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField))
                continue;

            uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
                                            .toInt();
            qDebug("$$$$, %s, %d, %s, %u, %x, %llu",
                    qPrintable(name),
                    i,
                    qPrintable(protocol->fieldData(i, AbstractProtocol::FieldName).toString()),
                    bitSize,
                    0, // min
                    (1 << bitSize) - 1);
        }

        delete protocol;
    }
}
#endif
