ChangeAdjust::ChangeAdjust(const AbstractChangeRecordSessionPacket& packet, PT_DocPosition iRemoteDocPos, const UT_UTF8String& sRemoteDocUUID)
    : m_pPacket(static_cast<const AbstractChangeRecordSessionPacket*>(packet.clone())),
      m_iLocalPos( m_pPacket->getPos() ),
      m_iRemoteDocPos(iRemoteDocPos),
      m_sRemoteDocUUID(sRemoteDocUUID)
{
}
bool AbiCollab_ImportRuleSet::_isSaveInsert(const ChangeAdjust& ca, const AbstractChangeRecordSessionPacket& acrsp, UT_sint32 iRemotePosAdjust)
{
	UT_return_val_if_fail(ca.m_pPacket, false);

	// if the packets share the same insertion position, then there is nothing we can do
	if (ca.getLocalPos() == acrsp.getPos())
		return false;
	
	// allowing overlapping deletions is _really_ tricky; for now, we just disallow it
	if (ca.getLocalLength() <= 0 || acrsp.getLength() <= 0)
		return false; 

	if (ca.m_pPacket->getClassType() != PCT_GlobSessionPacket && acrsp.getClassType() != PCT_GlobSessionPacket)
	{
		// overlapping inserts are just fine in the case of non-globs, as long as the start positions differ
		return ca.getLocalPos() != (acrsp.getPos()+iRemotePosAdjust);
	}

	//
	// if we get there, then at least one of the packets is a glob; this makes it a bit harder
	//
	
	// first, check that there are no 'delete' changerecords in the glob(s);
	// as stated above, deletes are really tricky, so we just disallow those
	if (ca.m_pPacket->getClassType() == PCT_GlobSessionPacket)
		for (std::vector<SessionPacket*>::const_iterator cit = static_cast<const GlobSessionPacket*>(ca.m_pPacket)->getPackets().begin(); cit != static_cast<const GlobSessionPacket*>(ca.m_pPacket)->getPackets().end(); cit++)
			if (AbstractChangeRecordSessionPacket::isInstanceOf(**cit) && 
				static_cast<AbstractChangeRecordSessionPacket*>(*cit)->getAdjust() < 0)
					return false;

	if (acrsp.getClassType() == PCT_GlobSessionPacket)
		for (std::vector<SessionPacket*>::const_iterator cit = static_cast<const GlobSessionPacket&>(acrsp).getPackets().begin(); cit != static_cast<const GlobSessionPacket&>(acrsp).getPackets().end(); cit++)
			if (AbstractChangeRecordSessionPacket::isInstanceOf(**cit) && 
				static_cast<AbstractChangeRecordSessionPacket*>(*cit)->getAdjust() < 0)
					return false;
	
	//
	// TODO: allow globs/insertions that really don't touch eachother, caused by the fact that
	// the 'first' insertion moves up the other packet's position
	//
	
	
	return false; // just to be on the save side
}
/*!
 * Scan back through the CR's we've emitted since this remote CR was sent
 * and see if any overlap this one.
 * return true if there is a collision.
 */
bool ABI_Collab_Import::_checkForCollision(const AbstractChangeRecordSessionPacket& acrsp, UT_sint32& iRev, UT_sint32& iImportAdjustment)
{
	UT_DEBUGMSG(("ABI_Collab_Import::_checkForCollision() - pos: %d, length: %d, UUID: %s, remoterev: %d\n", 
				 acrsp.getPos(), acrsp.getLength(), acrsp.getDocUUID().utf8_str(), acrsp.getRemoteRev()));

	ABI_Collab_Export* pExport = m_pAbiCollab->getExport();
	UT_return_val_if_fail(pExport, false);

	const UT_GenericVector<ChangeAdjust *>* pExpAdjusts = pExport->getAdjusts();
	UT_return_val_if_fail(pExpAdjusts, false);

	iImportAdjustment = 0;

	// get the collision sequence (if any)
	UT_sint32 iStart = 0;
	UT_sint32 iEnd = 0;
	_calculateCollisionSeqence(acrsp.getRemoteRev(), acrsp.getDocUUID(), iStart, iEnd);
	UT_return_val_if_fail(iStart >= 0 && iEnd >= 0, false);
	if (iStart == iEnd)
	{
		UT_DEBUGMSG(("Empty collision sequence, no possible collision\n"));
		return false;
	}

	std::deque<int> incAdjs;
	UT_sint32 iIncomingStateAdjust = _getIncomingAdjustmentForState(pExpAdjusts, iStart, iEnd, acrsp.getPos(), acrsp.getLength(), acrsp.getDocUUID(), incAdjs);
	UT_DEBUGMSG(("IINCOMMINGSTATEADJUST: %d\n", iIncomingStateAdjust));

	// Now scan forward and look for an overlap of the new changerecord with the collision sequence
	UT_DEBUGMSG(("Checking collision sequence [%d..%d) for overlapping changerecords\n", iStart, iEnd));
	bool bDenied = false;
	for (UT_sint32 i = iStart; i < iEnd; i++)
	{
		ChangeAdjust* pChange = pExpAdjusts->getNthItem(i);
		if (pChange)
		{
			UT_DEBUGMSG(("Looking at pChange->getRemoteDocUUID(): %s\n", pChange->getRemoteDocUUID().utf8_str()));

			if (pChange->getRemoteDocUUID() != acrsp.getDocUUID())
			{
				if (_isOverlapping(acrsp.getPos()+iIncomingStateAdjust, acrsp.getLength(), pChange->getLocalPos(), pChange->getLocalLength()) &&
					!AbiCollab_ImportRuleSet::isOverlapAllowed(*pChange, acrsp, iIncomingStateAdjust))
				{
					UT_DEBUGMSG(("Fatal overlap detected for incoming pos: %d, incoming length: %d, pChange->getLocalPos(): %d, pChange->getLocalLength(): %d\n", 
							acrsp.getPos(), acrsp.getLength(), pChange->getLocalPos(), pChange->getLocalLength()));
					iRev = pChange->getLocalRev();
					bDenied = true;
					break;
				}
				else
                {
					UT_DEBUGMSG(("No (fatal) overlap detected for incoming pos: %d, incoming length: %d, pChange->getLocalPos(): %d, pChange->getLocalLength(): %d\n", 
							acrsp.getPos(), acrsp.getLength(), pChange->getLocalPos(), pChange->getLocalLength()));
                }
				
				if (pChange->getLocalPos() < acrsp.getPos()+iIncomingStateAdjust)
				{
					UT_DEBUGMSG(("Normal Upward influence detected\n"));
					iIncomingStateAdjust += pChange->getLocalAdjust();
				}
			}
			else
			{
				UT_DEBUGMSG(("Skipping overlap detection: changerecords came from the same document; incoming pos: %d, incoming length: %d, pChange->getLocalPos(): %d, pChange->getLocalLength(): %d\n", 
							acrsp.getPos(), acrsp.getLength(), pChange->getLocalPos(), pChange->getLocalLength()));
				if (!incAdjs.empty())
				{
					iIncomingStateAdjust += incAdjs.front();
					incAdjs.pop_front();
				}
				else
				{
					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
				}
			}

			UT_DEBUGMSG(("Now: iIncomingStateAdjust: %d\n", iIncomingStateAdjust));
		}
		else
			UT_return_val_if_fail(false, false);
	}

	if (!bDenied && !incAdjs.empty())
	{
		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
	}

	while (!incAdjs.empty())
	{
		UT_DEBUGMSG(("Adding left-over incoming adjustment: %d\n", incAdjs.front()));
		iIncomingStateAdjust += incAdjs.front();
		incAdjs.pop_front();
	}

	iImportAdjustment = iIncomingStateAdjust;
	UT_DEBUGMSG(("Full import adjustment: %d\n", iImportAdjustment));

	return bDenied;
}