// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/libfeaclient/ifmgr_cmd_queue.cc,v 1.13 2007/02/16 22:45:59 pavlin Exp $"

#include <algorithm>
#include <iterator>

#include "libfeaclient_module.h"
#include "libxorp/xlog.h"

#include "ifmgr_atoms.hh"
#include "ifmgr_cmds.hh"
#include "ifmgr_cmd_queue.hh"

// ----------------------------------------------------------------------------
// IfMgrCommandSinkBase

IfMgrCommandSinkBase::~IfMgrCommandSinkBase()
{
}

// ----------------------------------------------------------------------------
// IfMgrCommandTee

IfMgrCommandTee::IfMgrCommandTee(IfMgrCommandSinkBase& o1,
				 IfMgrCommandSinkBase& o2)
    : _o1(o1), _o2(o2)
{
}

void
IfMgrCommandTee::push(const Cmd& cmd)
{
    _o1.push(cmd);
    _o2.push(cmd);
}

// ----------------------------------------------------------------------------
// IfMgrCommandDispatcher

IfMgrCommandDispatcher::IfMgrCommandDispatcher(IfMgrIfTree& tree)
    : _iftree(tree)
{
}

void
IfMgrCommandDispatcher::push(const Cmd& cmd)
{
    if (_cmd.get() != 0) {
	XLOG_WARNING("Dropping buffered command.");
    }
    _cmd = cmd;
}

bool
IfMgrCommandDispatcher::execute()
{
    bool success = false;
    if (_cmd.get()) {
	success = _cmd->execute(_iftree);
	_cmd = 0;
    }
    return success;
}


// ----------------------------------------------------------------------------
// IfMgrCommandFifoQueue

void
IfMgrCommandFifoQueue::push(const Cmd& c)
{
    _fifo.push_back(c);
}

bool
IfMgrCommandFifoQueue::empty() const
{
    return _fifo.empty();
}

IfMgrCommandFifoQueue::Cmd&
IfMgrCommandFifoQueue::front()
{
    return _fifo.front();
}

const IfMgrCommandFifoQueue::Cmd&
IfMgrCommandFifoQueue::front() const
{
    return _fifo.front();
}

void
IfMgrCommandFifoQueue::pop_front()
{
    _fifo.pop_front();
}


// ----------------------------------------------------------------------------
// IfMgrCommandIfClusteringQueue

IfMgrCommandIfClusteringQueue::IfMgrCommandIfClusteringQueue()
    : _current_ifname("rolf harris")
{}

void
IfMgrCommandIfClusteringQueue::push(const Cmd& cmd)
{
    IfMgrIfCommandBase* ifcmd = dynamic_cast<IfMgrIfCommandBase*>(cmd.get());
    XLOG_ASSERT(ifcmd != 0);
    if (ifcmd->ifname() == _current_ifname) {
	_current_cmds.push_back(cmd);
    } else {
	_future_cmds.push_back(cmd);
	if (_current_cmds.empty()) {
	    change_active_interface();
	}
    }
}

bool
IfMgrCommandIfClusteringQueue::empty() const
{
    return _current_cmds.empty();
}

IfMgrCommandIfClusteringQueue::Cmd&
IfMgrCommandIfClusteringQueue::front()
{
    return _current_cmds.front();
}

const IfMgrCommandIfClusteringQueue::Cmd&
IfMgrCommandIfClusteringQueue::front() const
{
    return _current_cmds.front();
}

void
IfMgrCommandIfClusteringQueue::pop_front()
{

    if (_current_cmds.empty() == false) {
	Cmd& c = _current_cmds.front();
	IfMgrIfCommandBase* ifcmd =
	    dynamic_cast<IfMgrIfCommandBase*>(c.get());
	XLOG_ASSERT(ifcmd != 0);
	_current_ifname = ifcmd->ifname();
	_current_cmds.pop_front();
    }

    if (_current_cmds.empty()) {
	change_active_interface();
    }
}

//
// Helper predicate class for
// IfMgrCommandIfClusteringQueue::change_active_interface()
//
class InterfaceNameOfQueuedCmdMatches {
public:
    inline InterfaceNameOfQueuedCmdMatches(const string& ifname)
	: _ifname(ifname)
    {}

    inline bool operator() (IfMgrCommandIfClusteringQueue::Cmd c)
    {
	IfMgrIfCommandBase* ifcmd = dynamic_cast<IfMgrIfCommandBase*>(c.get());
	XLOG_ASSERT(ifcmd != 0);
	return ifcmd->ifname() == _ifname;
    }
protected:
    const string& _ifname;
};

void
IfMgrCommandIfClusteringQueue::change_active_interface()
{
    XLOG_ASSERT(_current_cmds.empty());
    if (_future_cmds.empty()) {
	return;
    }

    // Take first command in future commands and use interface of that
    // as new current interface.
    Cmd& c = _future_cmds.front();
    IfMgrIfCommandBase* ifcmd =
	dynamic_cast<IfMgrIfCommandBase*>(c.get());
    XLOG_ASSERT(ifcmd != 0);
    _current_ifname = ifcmd->ifname();
    back_insert_iterator<CmdList> bi(_current_cmds);
    remove_copy_if(_future_cmds.begin(), _future_cmds.end(), bi,
		   InterfaceNameOfQueuedCmdMatches(_current_ifname));
}


// ----------------------------------------------------------------------------
// IfMgr*ToCommands

void
IfMgrIfTreeToCommands::convert(IfMgrCommandSinkBase& s) const
{
    const IfMgrIfTree::IfMap& ifs = _tree.ifs();
    IfMgrIfTree::IfMap::const_iterator cii;

    for (cii = ifs.begin(); cii != ifs.end(); ++cii) {
	IfMgrIfAtomToCommands(cii->second).convert(s);
    }
    s.push(new IfMgrHintTreeComplete());
}

void
IfMgrIfAtomToCommands::convert(IfMgrCommandSinkBase& s) const
{
    s.push(new IfMgrIfAdd(_i.name()));
    s.push(new IfMgrIfSetEnabled(_i.name(), _i.enabled()));
    s.push(new IfMgrIfSetMtu(_i.name(), _i.mtu_bytes()));
    s.push(new IfMgrIfSetMac(_i.name(), _i.mac()));
    s.push(new IfMgrIfSetPifIndex(_i.name(), _i.pif_index()));
    s.push(new IfMgrIfSetNoCarrier(_i.name(), _i.no_carrier()));

    const IfMgrIfAtom::VifMap& vifs = _i.vifs();
    IfMgrIfAtom::VifMap::const_iterator cvi;
    for (cvi = vifs.begin(); cvi != vifs.end(); ++cvi) {
	IfMgrVifAtomToCommands(_i.name(), cvi->second).convert(s);
    }
}

void
IfMgrVifAtomToCommands::convert(IfMgrCommandSinkBase& s) const
{
    const string& ifn = _ifn;
    const string& vifn = _v.name();

    s.push(new IfMgrVifAdd(ifn, vifn));
    s.push(new IfMgrVifSetEnabled(ifn, vifn, _v.enabled()));
    s.push(new IfMgrVifSetMulticastCapable(ifn, vifn, _v.multicast_capable()));
    s.push(new IfMgrVifSetBroadcastCapable(ifn, vifn, _v.broadcast_capable()));
    s.push(new IfMgrVifSetP2PCapable(ifn, vifn, _v.p2p_capable()));
    s.push(new IfMgrVifSetLoopbackCapable(ifn, vifn, _v.loopback()));
    s.push(new IfMgrVifSetPifIndex(ifn, vifn, _v.pif_index()));

    const IfMgrVifAtom::V4Map& v4s = _v.ipv4addrs();
    for (IfMgrVifAtom::V4Map::const_iterator cai = v4s.begin();
	 cai != v4s.end(); ++cai) {
	IfMgrIPv4AtomToCommands(ifn, vifn, cai->second).convert(s);
    }

    const IfMgrVifAtom::V6Map& v6s = _v.ipv6addrs();
    for (IfMgrVifAtom::V6Map::const_iterator cai = v6s.begin();
	 cai != v6s.end(); ++cai) {
	IfMgrIPv6AtomToCommands(ifn, vifn, cai->second).convert(s);
    }
}

void
IfMgrIPv4AtomToCommands::convert(IfMgrCommandSinkBase& s) const
{
    const string& ifn = _ifn;
    const string& vifn = _vifn;
    IPv4 addr = _a.addr();

    s.push(new IfMgrIPv4Add(ifn, vifn, addr));
    s.push(new IfMgrIPv4SetPrefix(ifn, vifn, addr, _a.prefix_len()));
    s.push(new IfMgrIPv4SetEnabled(ifn, vifn, addr, _a.enabled()));
    s.push(new IfMgrIPv4SetMulticastCapable(ifn, vifn, addr,
					    _a.multicast_capable()));
    s.push(new IfMgrIPv4SetLoopback(ifn, vifn, addr, _a.loopback()));
    s.push(new IfMgrIPv4SetBroadcast(ifn, vifn, addr, _a.broadcast_addr()));
    s.push(new IfMgrIPv4SetEndpoint(ifn, vifn, addr, _a.endpoint_addr()));
}

void
IfMgrIPv6AtomToCommands::convert(IfMgrCommandSinkBase& s) const
{
    const string& ifn = _ifn;
    const string& vifn = _vifn;
    const IPv6& addr = _a.addr();

    s.push(new IfMgrIPv6Add(ifn, vifn, addr));
    s.push(new IfMgrIPv6SetPrefix(ifn, vifn, addr, _a.prefix_len()));
    s.push(new IfMgrIPv6SetEnabled(ifn, vifn, addr, _a.enabled()));
    s.push(new IfMgrIPv6SetMulticastCapable(ifn, vifn, addr,
					    _a.multicast_capable()));
    s.push(new IfMgrIPv6SetLoopback(ifn, vifn, addr, _a.loopback()));
    s.push(new IfMgrIPv6SetEndpoint(ifn, vifn, addr, _a.endpoint_addr()));
}


syntax highlighted by Code2HTML, v. 0.9.1