//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please 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.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// fcospechelper.cpp
//

#include "stdfco.h"
#include "fcospechelper.h"
#include "core/debug.h"
#include "core/serializer.h"
#include "twfactory.h"
#include "fconametranslator.h"

//#############################################################################
// iFCOSpecHelper
//#############################################################################
///////////////////////////////////////////////////////////////////////////////
// Read
///////////////////////////////////////////////////////////////////////////////
void iFCOSpecHelper::Read (iSerializer* pSerializer, int32 version)
{
	// read the start point
    pSerializer->ReadObject(&mStartPoint);
}

///////////////////////////////////////////////////////////////////////////////
// Write
///////////////////////////////////////////////////////////////////////////////
void iFCOSpecHelper::Write(iSerializer* pSerializer) const
{
	// write the start point
    pSerializer->WriteObject(&mStartPoint);
}



//#############################################################################
// cFCOSpecStopPointSet
//#############################################################################
IMPLEMENT_TYPEDSERIALIZABLE(cFCOSpecStopPointSet, _T("cFCOSpecStopPointSet"), 0, 1)


///////////////////////////////////////////////////////////////////////////////
// cFCOSpecStopPointSet
///////////////////////////////////////////////////////////////////////////////
cFCOSpecStopPointSet::cFCOSpecStopPointSet()
:	mRecurseDepth( cFCOSpecStopPointSet::RECURSE_INFINITE )
{
	
}

///////////////////////////////////////////////////////////////////////////////
// ContainsFCO
///////////////////////////////////////////////////////////////////////////////
bool cFCOSpecStopPointSet::ContainsFCO(const cFCOName& name) const
{
	// make sure name is at or below start point
	cFCOName::Relationship rel = name.GetRelationship(GetStartPoint());
	if((rel != cFCOName::REL_BELOW) && (rel != cFCOName::REL_EQUAL))
		return false;

	// chack all the stop points
    std::set<cFCOName>::const_iterator itr;
    for (itr = mStopPoints.begin(); itr != mStopPoints.end(); ++itr)
    {
		rel = name.GetRelationship(*itr);
		if((rel == cFCOName::REL_BELOW) || (rel == cFCOName::REL_EQUAL))
			return false;
    }

	return true;
}

///////////////////////////////////////////////////////////////////////////////
// SetStartPoint
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecStopPointSet::SetStartPoint(const cFCOName& startPoint)
{
	cDebug d("cFCOSpecStopPointSet::SetStartPoint");

	// make sure the start point is above all stop points...
	std::set<cFCOName>::iterator i;
	for(i = mStopPoints.begin(); i != mStopPoints.end(); ++i)
	{	
		if(i->GetRelationship(startPoint) != cFCOName::REL_BELOW)
		{
			TOSTRINGSTREAM str;
			str << "Bad start point [" 
				<< iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( startPoint ) 
				<< "] added to spec with stop point "
				<< iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( *i ) 
				<< std::ends;

			d.TraceError("%s\n", str.str().c_str());
            throw eSerializerInputStreamFmt(str.str().c_str());
		}
	}

	// ok, it is acceptable to add this 
	inherited::SetStartPoint(startPoint);
}

///////////////////////////////////////////////////////////////////////////////
// ShouldStopDescent -- this returns true if we have just crossed the 
//		"boundary" between what is in and what is out of the spec's set. For
//		StopPointSets, this means that the fco name matches a stop point
///////////////////////////////////////////////////////////////////////////////
bool cFCOSpecStopPointSet::ShouldStopDescent(const cFCOName& name) const 
{
	cDebug d("cFCOSpecStopPointSet::ShouldStopDescent");
	//
	// first, check the recurse depth...
	//
	if( GetRecurseDepth() != RECURSE_INFINITE )
	{
		if( (name.GetSize() - mStartPoint.GetSize()) > GetRecurseDepth() )
			return true;
	}
	

	std::set<cFCOName>::const_iterator i = mStopPoints.find(name);
	return (i != mStopPoints.end());
}

///////////////////////////////////////////////////////////////////////////////
// TraceContents
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecStopPointSet::TraceContents(int dl) const
{
	cDebug d("cFCOSpecStopPointSet::TraceContents");
	if(dl == -1)
		dl = cDebug::D_DEBUG;

	d.Trace(dl, _T("Start Point:\t%s\n"), mStartPoint.AsString().c_str());
	for(std::set<cFCOName>::const_iterator i = mStopPoints.begin();
		i != mStopPoints.end(); ++i)
	{
		d.Trace(dl, _T("Stop Point :\t%s\n"), i->AsString().c_str());
	}
}

///////////////////////////////////////////////////////////////////////////////
// Add
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecStopPointSet::Add(const cFCOName& name)
{
	cDebug d("cFCOSpecImpl::AddStopPoint");

	// first, make sure this stop point is below the start point
	if(name.GetRelationship(mStartPoint) != cFCOName::REL_BELOW)
	{
		d.TraceError("Attempt to add stop point that is not below start point!\n");
		d.TraceError("\tstart point = %s stop point = %s\n", mStartPoint.AsString().c_str(), name.AsString().c_str());

		TOSTRINGSTREAM str;
		str << "Attempt to add stop point that is not below start point!" 
			<< " start point = " 
			<< iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( mStartPoint )
			<< " stop point = "  
			<< iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( name )
			<< std::ends;

		d.TraceError(_T("%s\n"), str.str().c_str());
		throw eSerializerInputStreamFmt(str.str().c_str());
	}

    std::set<cFCOName>::iterator i;
  	for(i = mStopPoints.begin(); i != mStopPoints.end(); )
	{
		cFCOName::Relationship rel = name.GetRelationship(*i);
		switch(rel)
		{
		case cFCOName::REL_EQUAL:
			d.TraceDebug(_T("Attempt to add stop point %s that already exists in spec %s\n"), name.AsString().c_str(), mStartPoint.AsString().c_str());
			return;
		case cFCOName::REL_BELOW:
			d.TraceDebug(_T("Attempt to add stop point %s to spec %s, but stop point %s already exists!\n"), 
				name.AsString().c_str(), mStartPoint.AsString().c_str(), i->AsString().c_str());
			return;
		case cFCOName::REL_ABOVE:	
			// this stop point will replace the encountered stop point, so remove it!
			d.TraceDebug(_T("Attempt to add stop point %s to spec %s, so removing stop point %s\n"), 
							name.AsString().c_str(), mStartPoint.AsString().c_str(), i->AsString().c_str());
			// we have to be careful we don't decrement past the beginning of the set!
			if(i == mStopPoints.begin())
			{
				mStopPoints.erase(i);
				i = mStopPoints.begin();
			}
			else
				mStopPoints.erase(i++);
			break;

		default:
			i++;
		}
	}

	// finally, insert the name
	mStopPoints.insert(name);
}

///////////////////////////////////////////////////////////////////////////////
// Clone
///////////////////////////////////////////////////////////////////////////////
iFCOSpecHelper* cFCOSpecStopPointSet::Clone() const
{
	cFCOSpecStopPointSet* pNew = new cFCOSpecStopPointSet;

	pNew->mStartPoint = mStartPoint;

	std::set<cFCOName>::const_iterator itr;
    for (itr = mStopPoints.begin(); itr != mStopPoints.end(); ++itr)
    {
		pNew->Add(*itr);
    }

	return pNew;
}

///////////////////////////////////////////////////////////////////////////////
// Read
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecStopPointSet::Read (iSerializer* pSerializer, int32 version)
{
	// read the start point
    //pSerializer->ReadObject(&mStartPoint);
	inherited::Read(pSerializer, version);

	// read all the stop points	
	int32 size;
    pSerializer->ReadInt32(size);
	ASSERT(size >= 0);
    for (int i = 0; i < size; ++i)
    {
        cFCOName fcoName;
        pSerializer->ReadObject(&fcoName);
		mStopPoints.insert(fcoName);
    }

}

///////////////////////////////////////////////////////////////////////////////
// Write
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecStopPointSet::Write(iSerializer* pSerializer) const
{
	// write the start point
    //pSerializer->WriteObject(&mStartPoint);
	inherited::Write(pSerializer);

	// write all the stop points
    pSerializer->WriteInt32(mStopPoints.size());
    std::set<cFCOName>::const_iterator itr;
    for (itr = mStopPoints.begin(); itr != mStopPoints.end(); ++itr)
    {
		pSerializer->WriteObject(&(*itr));
    }
}


///////////////////////////////////////////////////////////////////////////////
// Compare
///////////////////////////////////////////////////////////////////////////////
iFCOSpecHelper::CompareResult cFCOSpecStopPointSet::Compare(const iFCOSpecHelper* pRhs) const
{
	// if different types, order on mType address :-)
	if(pRhs->GetType() != GetType())
		return ((&pRhs->GetType() < &GetType()) ? CMP_LT : CMP_GT);

	const cFCOSpecStopPointSet* pStopPtSet = static_cast<const cFCOSpecStopPointSet*>(pRhs);
	if(GetStartPoint() != pStopPtSet->GetStartPoint())
		return ((GetStartPoint() < pStopPtSet->GetStartPoint()) ? CMP_LT : CMP_GT);

	if(GetSize() != pStopPtSet->GetSize())
		return ((GetSize() < pStopPtSet->GetSize()) ? CMP_LT : CMP_GT);

	std::set<cFCOName>::const_iterator	myIter	= mStopPoints.begin(),
										rhsIter = pStopPtSet->mStopPoints.begin();
	for(; myIter != mStopPoints.end(); myIter++, rhsIter++)
	{
		if(*myIter != *rhsIter)
			return ((*myIter < *rhsIter) ? CMP_LT : CMP_GT);
	}

	// they must be equal! 
	return CMP_EQ;
}

//#############################################################################
// cFCOSpecNoChildren
//#############################################################################
IMPLEMENT_TYPEDSERIALIZABLE(cFCOSpecNoChildren, _T("cFCOSpecNoChildren"), 0, 1)

///////////////////////////////////////////////////////////////////////////////
// ContainsFCO
///////////////////////////////////////////////////////////////////////////////
bool cFCOSpecNoChildren::ContainsFCO(const cFCOName& name) const
{
	return (name == GetStartPoint());
}

///////////////////////////////////////////////////////////////////////////////
// Clone
///////////////////////////////////////////////////////////////////////////////
iFCOSpecHelper* cFCOSpecNoChildren::Clone() const
{
	iFCOSpecHelper* pNew = new cFCOSpecNoChildren;
	pNew->SetStartPoint(GetStartPoint());
	return pNew;
}

///////////////////////////////////////////////////////////////////////////////
// ShouldStopDescent
///////////////////////////////////////////////////////////////////////////////
bool cFCOSpecNoChildren::ShouldStopDescent(const cFCOName& name) const
{
	// we should stop for everything except the start point...
	return ( name != GetStartPoint() );
}

///////////////////////////////////////////////////////////////////////////////
// Read
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecNoChildren::Read (iSerializer* pSerializer, int32 version)
{
	inherited::Read(pSerializer, version);
}

///////////////////////////////////////////////////////////////////////////////
// Write
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecNoChildren::Write(iSerializer* pSerializer) const
{
	inherited::Write(pSerializer);
}

///////////////////////////////////////////////////////////////////////////////
// Compare
///////////////////////////////////////////////////////////////////////////////
iFCOSpecHelper::CompareResult cFCOSpecNoChildren::Compare(const iFCOSpecHelper* pRhs) const
{
	// if different types, order on mType address :-)
	if(pRhs->GetType() != GetType())
		return ((&pRhs->GetType() < &GetType()) ? CMP_LT : CMP_GT);

	if(GetStartPoint() != pRhs->GetStartPoint())
		return ((GetStartPoint() < pRhs->GetStartPoint()) ? CMP_LT : CMP_GT);

	return CMP_EQ;
}

///////////////////////////////////////////////////////////////////////////////
// TraceContents
///////////////////////////////////////////////////////////////////////////////
void cFCOSpecNoChildren::TraceContents(int dl) const
{
	cDebug d("cFCOSpecNoChildren::TraceContents");
	if(dl == -1)
		dl = cDebug::D_DEBUG;

	d.Trace(dl, "Single FCO Spec (all children are stop points): %s\n", mStartPoint.AsString().c_str());
}


