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

// Copyright (c) 2001-2011 XORP, Inc and Others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2, June
// 1991 as published by the Free Software Foundation. Redistribution
// and/or modification of this program under the terms of any other
// version of the GNU General Public License is not permitted.
// 
// 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. For more details,
// see the GNU General Public License, Version 2, a copy of which can be
// found in the XORP LICENSE.gpl file.
// 
// XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
// http://xorp.net

// $XORP: xorp/pim/pim_rp.hh,v 1.14 2008/10/02 21:57:55 bms Exp $


#ifndef __PIM_PIM_RP_HH__
#define __PIM_PIM_RP_HH__


//
// PIM RP information definitions
//




#include "libxorp/ipvx.hh"
#include "libxorp/ipvxnet.hh"
#include "libxorp/timer.hh"
#include "libproto/proto_unit.hh"


//
// Constants definitions
//


//
// Structures/classes, typedefs and macros
//

class PimNode;
class PimRp;
class RpTable;
class PimMre;

// The PIM RP class
class PimRp {
public:
    enum rp_learned_method_t {
	RP_LEARNED_METHOD_AUTORP,
	RP_LEARNED_METHOD_BOOTSTRAP,
	RP_LEARNED_METHOD_STATIC,
	RP_LEARNED_METHOD_UNKNOWN
    };
    
    PimRp(RpTable& rp_table, const IPvX& rp_addr, uint8_t rp_priority,
	  const IPvXNet& group_prefix, uint8_t hash_mask_len,
	  rp_learned_method_t rp_learned_method);
    PimRp(RpTable& rp_table, const PimRp& pim_rp);
    ~PimRp();
    
    RpTable&	rp_table()		{ return (_rp_table);		}
    
    const IPvX&	rp_addr()	const	{ return (_rp_addr);		}
    uint8_t	rp_priority()	const	{ return (_rp_priority);	}
    void	set_rp_priority(uint8_t v) { _rp_priority = v;		}
    const IPvXNet& group_prefix() const	{ return (_group_prefix);	}
    uint8_t	hash_mask_len()	const	{ return (_hash_mask_len);	}
    void	set_hash_mask_len(uint8_t v) { _hash_mask_len = v;	}
    rp_learned_method_t rp_learned_method() const { return (_rp_learned_method); }
    static const string rp_learned_method_str(rp_learned_method_t rp_learned_method);
    const string rp_learned_method_str() const;
    bool	is_updated()	const	{ return (_is_updated);		}
    void	set_is_updated(bool v)	{ _is_updated = v;		}
    
    list<PimMre *>& pim_mre_wc_list()	{ return (_pim_mre_wc_list);	}
    list<PimMre *>& pim_mre_sg_list()	{ return (_pim_mre_sg_list);	}
    list<PimMre *>& pim_mre_sg_rpt_list() { return (_pim_mre_sg_rpt_list); }
    list<PimMfc *>& pim_mfc_list()	{ return (_pim_mfc_list);	}
    list<PimMre *>& processing_pim_mre_wc_list() {
	return (_processing_pim_mre_wc_list);
    }
    list<PimMre *>& processing_pim_mre_sg_list() {
	return (_processing_pim_mre_sg_list);
    }
    list<PimMre *>& processing_pim_mre_sg_rpt_list() {
	return (_processing_pim_mre_sg_rpt_list);
    }
    list<PimMfc *>& processing_pim_mfc_list() {
	return (_processing_pim_mfc_list);
    }
    void	init_processing_pim_mre_wc();
    void	init_processing_pim_mre_sg();
    void	init_processing_pim_mre_sg_rpt();
    void	init_processing_pim_mfc();
    
    bool	i_am_rp() const { return (_i_am_rp); }
    
    
private:
    RpTable&	_rp_table;		// The RP table I belong to
    IPvX	_rp_addr;		// The RP address
    uint8_t	_rp_priority;		// The RP priority
    IPvXNet	_group_prefix;		// The group address prefix
    uint8_t	_hash_mask_len;		// The RP hash mask length
    rp_learned_method_t _rp_learned_method; // How learned about this RP
    bool	_is_updated;		// True if just created or updated
    list<PimMre *> _pim_mre_wc_list;	// List of all related (*,G) entries
					// for this RP.
    list<PimMre *> _pim_mre_sg_list;	// List of all related (S,G) entries
					// for this RP that have no (*,G) entry
    list<PimMre *> _pim_mre_sg_rpt_list;// List of all related (S,G,rpt)
					// entries for this RP that have no
					// (*,G) entry
    list<PimMfc *> _pim_mfc_list;	// List of all related MFC entries
					// for this RP.
					// NOTE: those MFC entries _may_ have
					// existing (*,G) entry.
    list<PimMre *> _processing_pim_mre_wc_list; // List of all related
					// (*,G) entries for this RP that are
					// awaiting to be processed.
    list<PimMre *> _processing_pim_mre_sg_list; // List of all related
					// (S,G) entries for this RP that
					// have no (*,G) entry and that are
					// awaiting to be processed.
    list<PimMre *> _processing_pim_mre_sg_rpt_list; // List of all related
					// (S,G,rpt) entries for this RP that
					// have no (*,G) entry and that are
					// awaiting to be processed.
    list<PimMfc *> _processing_pim_mfc_list; // List of all related
					// MFC entries for this RP that are
					// awaiting to be processed.
					// NOTE: those MFC entries _may_ have
					// existing (*,G) entry.
    
    bool	_i_am_rp;		// True if this RP is me
};

// The RP table class
class RpTable : public ProtoUnit {
public:
    RpTable(PimNode& pim_node);
    ~RpTable();

    void	clear();    
    int		start();
    int		stop();
    PimNode&	pim_node()	{ return (_pim_node);			}
    PimRp	*rp_find(const IPvX& group_addr);
    PimRp	*add_rp(const IPvX& rp_addr,
			uint8_t rp_priority,
			const IPvXNet& group_prefix,
			uint8_t hash_mask_len,
			PimRp::rp_learned_method_t rp_learned_method);
    int		delete_rp(const IPvX& rp_addr,
			  const IPvXNet& group_prefix,
			  PimRp::rp_learned_method_t rp_learned_method);
    int		delete_all_group_prefixes_rp(const IPvX& rp_addr,
					     PimRp::rp_learned_method_t rp_learned_method);
    int		delete_all_rps(PimRp::rp_learned_method_t rp_learned_method);
    bool	apply_rp_changes();
    
    void	add_pim_mre(PimMre *pim_mre);
    void	add_pim_mfc(PimMfc *pim_mfc);
    void	delete_pim_mre(PimMre *pim_mre);
    void	delete_pim_mfc(PimMfc *pim_mfc);
    
    list<PimRp *>& rp_list() { return (_rp_list); }
    list<PimRp *>& processing_rp_list() { return (_processing_rp_list); }
    
    void	init_processing_pim_mre_wc(const IPvX& rp_addr);
    void	init_processing_pim_mre_sg(const IPvX& rp_addr);
    void	init_processing_pim_mre_sg_rpt(const IPvX& rp_addr);
    void	init_processing_pim_mfc(const IPvX& rp_addr);
    PimRp	*find_processing_pim_mre_wc(const IPvX& rp_addr);
    PimRp	*find_processing_pim_mre_sg(const IPvX& rp_addr);
    PimRp	*find_processing_pim_mre_sg_rpt(const IPvX& rp_addr);
    PimRp	*find_processing_pim_mfc(const IPvX& rp_addr);
    PimRp	*find_processing_rp_by_addr(const IPvX& rp_addr);
    bool	has_rp_addr(const IPvX& rp_addr);
    
private:
    PimRp	*compare_rp(const IPvX& group_addr, PimRp *rp1,
			    PimRp *rp2) const;
    uint32_t	derived_addr(const IPvX& addr) const;
    
    PimNode&	_pim_node;		// The associated PIM node
    
    list<PimRp *> _rp_list;		// The list of RPs
    list<PimRp *> _processing_rp_list;	// The list of obsolete RPs to process:
    // XXX: the PimRp entry with address of IPvX::ZERO(family()) contains
    // the list of PimMre and PimMfc entries that have no RP.
};

//
// Global variables
//


//
// Global functions prototypes
//

#endif // __PIM_PIM_RP_HH__
