/* Agent Tcl
   Bob Gray
   26 January 1997

   agentPermit.cc

   This file implements the class Permit. 

   Copyright (c) 1995-1997, Robert S. Gray Dartmouth College

   See the file "agent.terms" for information on usage and redistribution
   of this file and for a DISCLAIMER OF ALL WARRANTIES.
*/

#ifndef NO_PRAGMAS
#pragma implementation
#endif

#include "agentPermit.h"       // interface definition
#include "platMath.h"	       // MathUtil
#include "platTimeval.h"       // TimevalUtil
#include "genUtility.h"        // fast_memcpy

/* Permit::add

   Purpose: Replace each component of the permit with the sum of that component
	    and the corresponding component in the second permit.  If a
	    component is not set in one of the permits, that component is
	    ignored.

     Input: permit = the second permit
		     (const class Permit &)

    Output: The procedure does the component-by-component addition.
*/

void Permit::add (const Permit& permit)
{
    if ((permit.flags & e_PERMIT_WALL) && (flags & e_PERMIT_WALL)) {
	wall = TimevalUtil::addTimevals (wall, permit.wall);
    } 

    if ((permit.flags & e_PERMIT_CPU) && (flags & e_PERMIT_CPU)) {
	cpu = TimevalUtil::addTimevals (cpu, permit.cpu);
    }

    if ((permit.flags & e_PERMIT_JUMPS) && (flags & e_PERMIT_JUMPS)) {
	jumps = MathUtil::addWithCeiling (jumps, permit.jumps);
    }

    if ((permit.flags & e_PERMIT_CHILDREN) && (flags & e_PERMIT_CHILDREN)) {
	children = MathUtil::addWithCeiling (children, permit.children);
    }

    if ((permit.flags & e_PERMIT_DEPTH) && (flags & e_PERMIT_DEPTH)) {
	depth = MathUtil::addWithCeiling (depth, permit.depth);
    }
}

/* Permit::merge

   Purpose: Replace each component of the permit with the corresponding
            component in a second permit IF AND ONLY IF that component of
            the second permit is set

     Input: permit = the second permit
		     (const class Permit &)

    Output: The procedure does the component-by-component replacement.
*/

void Permit::merge (const Permit& permit)
{
    if (permit.flags & e_PERMIT_WALL) { 
	setWallLimit (permit.wall);
    }

    if (permit.flags & e_PERMIT_CPU) { 
	setCpuLimit (permit.cpu);
    }

    if (permit.flags & e_PERMIT_JUMPS) {
	setJumpLimit (permit.jumps);
    }

    if (permit.flags & e_PERMIT_CHILDREN) {
	setChildrenLimit (permit.children);
    }

    if (permit.flags & e_PERMIT_DEPTH) {
	setDepthLimit (permit.depth);
    }
}

/* Permit::setToMinimum

   Purpose: Set the permit to the component-by-component minimum of two
            other permits

     Input: first  = the first permit
		     (const class Permit &)

            second = the second permit
		     (const class Permit &)

    Output: The procedure sets the permit to the component-by-component minimum
            of the two given permits.
*/

void Permit::setToMinimum (const Permit& first, const Permit& second)
{
	// assume that the first permit is the minimum

    flags    = first.flags;
    wall     = first.wall;
    cpu      = first.cpu;
    jumps    = first.jumps;
    children = first.children;
    depth    = first.depth;

	// then check against the second permit

    if (second.flags & e_PERMIT_WALL) {
	if (!(flags & e_PERMIT_WALL) || (TimevalUtil::compareTimevals (wall, second.wall) > 0)) {
	    setWallLimit (second.wall);
	}
    }

    if (second.flags & e_PERMIT_CPU) {
	if (!(flags & e_PERMIT_CPU) || (TimevalUtil::compareTimevals (cpu, second.cpu) > 0)) {
	    setCpuLimit (second.cpu);
	}
    }

    if (second.flags & e_PERMIT_JUMPS) {
	if (!(flags & e_PERMIT_JUMPS) || (jumps > second.jumps)) {
	    setJumpLimit (second.jumps);
	}
    }

    if (second.flags & e_PERMIT_CHILDREN) {
	if (!(flags & e_PERMIT_CHILDREN) || (children > second.children)) {
	    setChildrenLimit (second.children);
	}
    }

    if (second.flags & e_PERMIT_DEPTH) {
	if (!(flags & e_PERMIT_DEPTH) || (depth > second.depth)) {
	    setDepthLimit (second.depth);
	}
    }
}

/* PermitPacker::getPackedSize

   Purpose: Determine the number of bytes needed to represent the Permit as
            a byte stream
*/

UINT_32 PermitPacker::getPackedSize (void)
{
    UINT_32 size = sizeof(UINT_16);

    if (m_permit.flags & Permit::e_PERMIT_WALL) {
	TimevalPacker packer (m_permit.wall);
	size += packer.getPackedSize();
    }

    if (m_permit.flags & Permit::e_PERMIT_CPU) {
	TimevalPacker packer (m_permit.cpu);
	size += packer.getPackedSize();
    }

    if (m_permit.flags & Permit::e_PERMIT_JUMPS) {
	size += sizeof(UINT_32);
    }

    if (m_permit.flags & Permit::e_PERMIT_CHILDREN) {
	size += sizeof(UINT_32);
    }

    if (m_permit.flags & Permit::e_PERMIT_DEPTH) {
	size += sizeof(UINT_32);
    }
  
    return (size);	
}

/* PermitPacker::pack 

   Purpose: Pack the permit into a byte stream

     Input: buffer = character buffer in which to place the byte stream
                     (char *)

    Output: The procedure returns a char * that points into the buffer
            immediately after the newly added bytes.
*/

char *PermitPacker::pack (char *bp)
{
    UINT_16 flags; 
    UINT_16 netShort;
    UINT_32 netLong;

	/* get the flags */

    flags    = (UINT_16) m_permit.flags; 
    netShort = htons (flags);

	/* output the flags and the components that are set */

    bp = fast_memcpy (bp, (char *) &netShort, sizeof(UINT_16));

    if (m_permit.flags & Permit::e_PERMIT_WALL) {
	TimevalPacker packer (m_permit.wall);
	bp = packer.pack (bp);
    }

    if (m_permit.flags & Permit::e_PERMIT_CPU) {
	TimevalPacker packer (m_permit.cpu);
	bp = packer.pack (bp);
    }
	
    if (m_permit.flags & Permit::e_PERMIT_JUMPS) {
	netLong = htonl (m_permit.jumps);
	bp = fast_memcpy (bp, (char *) &netLong, sizeof(UINT_32));
    }
	
    if (m_permit.flags & Permit::e_PERMIT_CHILDREN) {
	netLong = htonl (m_permit.children);
	bp = fast_memcpy (bp, (char *) &netLong, sizeof(UINT_32));
    }
	
    if (m_permit.flags & Permit::e_PERMIT_DEPTH) {
	netLong = htonl (m_permit.depth);
	bp = fast_memcpy (bp, (char *) &netLong, sizeof(UINT_32));
    }

    return (bp);
}

/* PermitUnpacker::unpack

   Purpose: Unpack a permit from a byte stream

     Input: bp     = start of the buffer containing the permit
	             (char *)

            end_bp = end of the buffer containing the permit
                     (char *)

    Output: The procedure returns NULL on error.  Otherwise the procedure
	    returns a pointer to the buffer position immediately after the
            permit.
*/

char *PermitUnpacker::unpack (char *bp, char *end_bp)
{
    UINT_16 hostShort;

	/* get the permit flags */

    if ((bp = breakout_short (bp, end_bp, hostShort)) == NULL) {
	return (NULL);
    }

    m_permit.flags = (UINT_8) hostShort;

	/* get the permit components */ 

    if (m_permit.flags & Permit::e_PERMIT_WALL) {

	TimevalUnpacker unpacker (m_permit.wall);

	if ((bp = unpacker.unpack (bp, end_bp)) == NULL) {
	    return ((char *) NULL);
        }
    }

    if (m_permit.flags & Permit::e_PERMIT_CPU) {

	TimevalUnpacker unpacker (m_permit.cpu);

	if ((bp = unpacker.unpack (bp, end_bp)) == NULL) {
	    return ((char *) NULL);
        }
    }

    if (m_permit.flags & Permit::e_PERMIT_JUMPS) {

	if ((bp = breakout_long (bp, end_bp, m_permit.jumps)) == NULL) {
	    return (NULL);
	}
    }

    if (m_permit.flags & Permit::e_PERMIT_CHILDREN) {

	if ((bp = breakout_long (bp, end_bp, m_permit.children)) == NULL) {
	    return (NULL);
	}
    }

    if (m_permit.flags & Permit::e_PERMIT_DEPTH) {

	if ((bp = breakout_long (bp, end_bp, m_permit.depth)) == NULL) {
	    return (NULL);
	}
    }

    return (bp);
}

/* TimevalPacker::pack 

   Purpose: Pack the timeval into a byte stream

     Input: buffer = character buffer in which to place the byte stream
                     (char *)

    Output: The procedure returns a char * that points into the buffer
            immediately after the newly added bytes.
*/

char *TimevalPacker::pack (char *bp)
{
   UINT_32 hostLong;
   UINT_32 netLong;

   hostLong = m_tv.tv_sec;
   netLong = htonl (hostLong);
   bp = fast_memcpy (bp, (char *) &netLong, sizeof(UINT_32));
   netLong = htonl (hostLong = m_tv.tv_usec);
   bp = fast_memcpy (bp, (char *) &netLong, sizeof(UINT_32));
   return (bp);
}
 
/* TimevalUnpacker::unpack

   Purpose: Unpack a timeval from a byte stream

     Input: bp     = start of the buffer containing the permit
	             (char *)

            end_bp = end of the buffer containing the permit
                     (char *)

    Output: The procedure returns NULL on error.  Otherwise the procedure
	    returns a pointer to the buffer position immediately after the
    	    timeval. 
*/

char *TimevalUnpacker::unpack (char *bp, char *end_bp)
{
    UINT_32 hostLong;

	// break out seconds and microseconds

    if ((bp = breakout_long (bp, end_bp, hostLong)) == NULL) {
	return (NULL);
    }

    m_tv.tv_sec = hostLong;

    if ((bp = breakout_long (bp, end_bp, hostLong)) == NULL) {
	return (NULL);
    }

    m_tv.tv_usec = hostLong;

	// make sure seconds and microseconds are in range

    if (m_tv.tv_sec < 0) {
	return (NULL);
    }
   
    if ((m_tv.tv_usec < 0) || (m_tv.tv_usec >= 1000000)) {
	return (NULL);
    }

    return (bp);
} 
