/* special function that returns true only when sphere fully fits inside the frustum. */
	bool PCZFrustum::isFullyVisible(const Sphere& bound) const
	{
		// Check originplane if told to
		if (mUseOriginPlane)
		{
			if (mOriginPlane.getDistance(bound.getCenter()) <= bound.getRadius() ||
				mOriginPlane.getSide(bound.getCenter()) != Plane::POSITIVE_SIDE)
			{
				return false;
			}
		}

		// For each extra active culling plane,
		// see if the sphere is not on the positive side
		// If so, object is not fully visible
		PCPlaneList::const_iterator pit = mActiveCullingPlanes.begin();
		while ( pit != mActiveCullingPlanes.end() )
		{
			PCPlane* plane = *pit;

			if (plane->getDistance(bound.getCenter()) <= bound.getRadius() ||
				plane->getSide(bound.getCenter()) != Plane::POSITIVE_SIDE)
			{
				return false;
			}

			pit++;
		}
		return true;
	}
    bool PCZFrustum::isVisible( const Sphere & bound) const
    {
        // Check originplane if told to
        if (mUseOriginPlane)
        {
            Plane::Side side = mOriginPlane.getSide(bound.getCenter());
            if (side == Plane::NEGATIVE_SIDE)
            {
				Real dist = mOriginPlane.getDistance(bound.getCenter());
				if (dist > bound.getRadius())
				{
					return false;
				}
            }
        }

        // For each extra active culling plane, see if the entire sphere is on the negative side
        // If so, object is not visible
        PCPlaneList::const_iterator pit = mActiveCullingPlanes.begin();
        while ( pit != mActiveCullingPlanes.end() )
        {
            PCPlane * plane = *pit;
            Plane::Side xside = plane->getSide(bound.getCenter());
            if (xside == Plane::NEGATIVE_SIDE)
            {
				Real dist = plane->getDistance(bound.getCenter());
				if (dist > bound.getRadius())
				{
					return false;
				}
            }
            pit++;
        }
		return true;
    }
	/* A 'more detailed' check for visibility of an AAB.  This function returns
	  none, partial, or full for visibility of the box.  This is useful for 
	  stuff like Octree leaf culling */
	PCZFrustum::Visibility PCZFrustum::getVisibility( const AxisAlignedBox &bound )
	{

		// Null boxes always invisible
		if ( bound.isNull() )
			return NONE;

		// Get centre of the box
		Vector3 centre = bound.getCenter();
		// Get the half-size of the box
		Vector3 halfSize = bound.getHalfSize();

		bool all_inside = true;

        // Check originplane if told to
        if (mUseOriginPlane)
        {
            Plane::Side side = mOriginPlane.getSide(centre, halfSize);
            if (side == Plane::NEGATIVE_SIDE)
            {
				return NONE;
            }
			// We can't return now as the box could be later on the negative side of another plane.
			if(side == Plane::BOTH_SIDE) 
            {
                all_inside = false;
            }
        }

        // For each active culling plane, see if the entire aabb is on the negative side
        // If so, object is not visible
        PCPlaneList::iterator pit = mActiveCullingPlanes.begin();
        while ( pit != mActiveCullingPlanes.end() )
        {
            PCPlane * plane = *pit;
            Plane::Side xside = plane->getSide(centre, halfSize);
			if(xside == Plane::NEGATIVE_SIDE) 
            {
                return NONE;
            }
			// We can't return now as the box could be later on the negative side of a plane.
			if(xside == Plane::BOTH_SIDE) 
            {
                all_inside = false;
				break;
            }
            pit++;
        }

		if ( all_inside )
			return FULL;
		else
			return PARTIAL;

	}
	// remove culling planes created from the given portal
	void PCZFrustum::removePortalCullingPlanes(PortalBase* portal)
	{
        PCPlaneList::iterator pit = mActiveCullingPlanes.begin();
        while ( pit != mActiveCullingPlanes.end() )
        {
            PCPlane * plane = *pit;
			if (plane->getPortal() == portal)
			{
				// put the plane back in the reservoir
				mCullingPlaneReservoir.push_front(plane);
				// erase the entry from the active culling plane list
                pit = mActiveCullingPlanes.erase(pit);
			}
            else
            {
                pit++;
            }
        }

    }
    bool PCZFrustum::isVisible( const AxisAlignedBox & bound) const
    {
        // Null boxes are always invisible
        if (bound.isNull()) return false;

        // Infinite boxes are always visible
        if (bound.isInfinite()) return true;

		// Get centre of the box
        Vector3 centre = bound.getCenter();
        // Get the half-size of the box
        Vector3 halfSize = bound.getHalfSize();

        // Check originplane if told to
        if (mUseOriginPlane)
        {
            Plane::Side side = mOriginPlane.getSide(centre, halfSize);
            if (side == Plane::NEGATIVE_SIDE)
            {
				return false;
            }
        }

        // For each extra active culling plane, see if the entire aabb is on the negative side
        // If so, object is not visible
        PCPlaneList::const_iterator pit = mActiveCullingPlanes.begin();
        while ( pit != mActiveCullingPlanes.end() )
        {
            PCPlane * plane = *pit;
            Plane::Side xside = plane->getSide(centre, halfSize);
            if (xside == Plane::NEGATIVE_SIDE)
            {
				return false;
            }
            pit++;
        }
		return true;
    }
	// calculate  culling planes from portal and frustum 
	// origin and add to list of  culling planes
	// NOTE: returns 0 if portal was completely culled by existing planes
	//		 returns > 0 if culling planes are added (# is planes added)
	int PCZFrustum::addPortalCullingPlanes(PortalBase* portal)
	{
		int addedcullingplanes = 0;

		// If portal is of type aabb or sphere, add a plane which is same as frustum
		// origin plane (ie. redundant).  We do this because we need the plane as a flag
		// to prevent infinite recursion 
		if (portal->getType() == PortalBase::PORTAL_TYPE_AABB ||
			portal->getType() == PortalBase::PORTAL_TYPE_SPHERE)
		{
            PCPlane * newPlane = getUnusedCullingPlane();
			newPlane->setFromOgrePlane(mOriginPlane);
            newPlane->setPortal(portal);
            mActiveCullingPlanes.push_front(newPlane);
			addedcullingplanes++;
			return addedcullingplanes;
		}

		// only do this check if it's an anti portal since it's double facing.
		bool flipPlane = false;
		if (portal->getTypeFlags() == AntiPortalFactory::FACTORY_TYPE_FLAG)
		{
			// check if the portal norm is facing the frustum
			Vector3 frustumToPortal = portal->getDerivedCP() - mOrigin;
			Vector3 portalDirection = portal->getDerivedDirection();
			Real dotProduct = frustumToPortal.dotProduct(portalDirection);

			// it's facing away from the frustum. Flip the planes.
			if (dotProduct > 0) flipPlane = true;
		}

        // For portal Quads: Up to 4 planes can be added by the sides of a portal quad.
        // Each plane is created from 2 corners (world space) of the portal and the
        // frustum origin (world space).
		int i,j;
		Plane::Side pt0_side, pt1_side;
		bool visible;
        PCPlaneList::iterator pit;
        for (i=0;i<4;i++)
        {
            // first check if both corners are outside of one of the existing planes
			j = i+1;
			if (j > 3)
			{
				j = 0;
			}
			visible = true;
            pit = mActiveCullingPlanes.begin();
            while ( pit != mActiveCullingPlanes.end() )
            {
                PCPlane * plane = *pit;
				pt0_side = plane->getSide(portal->getDerivedCorner(i));
				pt1_side = plane->getSide(portal->getDerivedCorner(j));
				if (pt0_side == Plane::NEGATIVE_SIDE &&
					pt1_side == Plane::NEGATIVE_SIDE)
				{
					// the portal edge was actually completely culled by one of  culling planes
					visible = false;
					break;
				}
				pit++;
            }
			if (visible)
			{
				// add the plane created from the two portal corner points and the frustum location
				// to the  culling plane
                PCPlane * newPlane = getUnusedCullingPlane();
				if (mProjType == PT_ORTHOGRAPHIC) // use camera direction if projection is orthographic.
				{
					if (flipPlane)
					{
						newPlane->redefine(portal->getDerivedCorner(j) + mOriginPlane.normal,
							portal->getDerivedCorner(i), portal->getDerivedCorner(j));
					}
					else
					{
						newPlane->redefine(portal->getDerivedCorner(j) + mOriginPlane.normal,
							portal->getDerivedCorner(j), portal->getDerivedCorner(i));
					}
				}
				else
				{
					if (flipPlane)
					{
						newPlane->redefine(mOrigin,
							portal->getDerivedCorner(i),
							portal->getDerivedCorner(j));
					}
					else
					{
						newPlane->redefine(mOrigin,
							portal->getDerivedCorner(j),
							portal->getDerivedCorner(i));
					}
				}
                newPlane->setPortal(portal);
                mActiveCullingPlanes.push_front(newPlane);
				addedcullingplanes++;
			}
        }
		// if we added ANY planes from the quad portal, we should add the plane of the
		// portal itself as an additional culling plane.
		if (addedcullingplanes > 0)
		{
			PCPlane * newPlane = getUnusedCullingPlane();

			if (flipPlane)
			{
				newPlane->redefine(
					portal->getDerivedCorner(2),
					portal->getDerivedCorner(0),
					portal->getDerivedCorner(1));
			}
			else
			{
				newPlane->redefine(
					portal->getDerivedCorner(2),
					portal->getDerivedCorner(1),
					portal->getDerivedCorner(0));
			}

			newPlane->setPortal(portal);
			mActiveCullingPlanes.push_back(newPlane);
			addedcullingplanes++;
		}
		return addedcullingplanes;
    }
	/* special function that returns true only when portal fully fits inside the frustum. */
	bool PCZFrustum::isFullyVisible(const PortalBase* portal) const
	{
		// if portal isn't enabled, it's not visible
		if (!portal->getEnabled()) return false;

		// if the frustum has no planes, just return true
		if (mActiveCullingPlanes.empty())
		{
			return true;
		}
		// check if this portal is already in the list of active culling planes (avoid
		// infinite recursion case)
		PCPlaneList::const_iterator pit = mActiveCullingPlanes.begin();
		while ( pit != mActiveCullingPlanes.end() )
		{
			PCPlane * plane = *pit;
			if (plane->getPortal() == portal)
			{
				return false;
			}
			pit++;
		}
		// if portal is of type AABB or Sphere, then use simple bound check against planes
		if (portal->getType() == PortalBase::PORTAL_TYPE_AABB)
		{
			AxisAlignedBox aabb;
			aabb.setExtents(portal->getDerivedCorner(0), portal->getDerivedCorner(1));
			return isFullyVisible(aabb);
		}
		else if (portal->getType() == PortalBase::PORTAL_TYPE_SPHERE)
		{
			return isFullyVisible(portal->getDerivedSphere());
		}

		// only do this check if it's a portal. (anti portal doesn't care about facing)
		if (portal->getTypeFlags() == PortalFactory::FACTORY_TYPE_FLAG)
		{
			// check if the portal norm is facing the frustum
			Vector3 frustumToPortal = portal->getDerivedCP() - mOrigin;
			Vector3 portalDirection = portal->getDerivedDirection();
			Real dotProduct = frustumToPortal.dotProduct(portalDirection);
			if ( dotProduct > 0 )
			{
				// portal is faced away from Frustum 
				return false;
			}
		}

		// Check originPlane if told to
		if (mUseOriginPlane)
		{
			// we have to check each corner of the portal
			for (int corner = 0; corner < 4; corner++)
			{
				Plane::Side side = mOriginPlane.getSide(portal->getDerivedCorner(corner));
				if (side == Plane::NEGATIVE_SIDE) return false;
			}
		}

		// For each active culling plane, see if any portal points are on the negative 
		// side. If so, the portal is not fully visible
		pit = mActiveCullingPlanes.begin();
		while ( pit != mActiveCullingPlanes.end() )
		{
			PCPlane * plane = *pit;
			// we have to check each corner of the portal
			for (int corner = 0; corner < 4; corner++)
			{
				Plane::Side side =plane->getSide(portal->getDerivedCorner(corner));
				if (side == Plane::NEGATIVE_SIDE) return false;
			}
			pit++;
		}
		// no plane culled all the portal points and the norm
		// was facing the frustum, so this portal is fully visible
		return true;
	}
    // NOTE: Everything needs to be updated spatially before this function is
    //       called including portal corners, frustum planes, etc.
    bool PCZFrustum::isVisible(Portal * portal)
    {
		// if portal isn't open, it's not visible
		if (!portal->isOpen())
		{
			return false;
		}

		// if the frustum has no planes, just return true
        if (mActiveCullingPlanes.size() == 0)
        {
            return true;
        }
		// check if this portal is already in the list of active culling planes (avoid
		// infinite recursion case)
        PCPlaneList::const_iterator pit = mActiveCullingPlanes.begin();
        while ( pit != mActiveCullingPlanes.end() )
        {
            PCPlane * plane = *pit;
			if (plane->getPortal() == portal)
			{
				return false;
			}
            pit++;
        }
		// if portal is of type AABB or Sphere, then use simple bound check against planes
		if (portal->getType() == Portal::PORTAL_TYPE_AABB)
		{
			AxisAlignedBox aabb;
			aabb.setExtents(portal->getDerivedCorner(0), portal->getDerivedCorner(1));
			return isVisible(aabb);
		}
		else if (portal->getType() == Portal::PORTAL_TYPE_SPHERE)
		{
			return isVisible(portal->getDerivedSphere());
		}

        // check if the portal norm is facing the frustum
		Vector3 frustumToPortal = portal->getDerivedCP() - mOrigin;
		Vector3 portalDirection = portal->getDerivedDirection();
		Real dotProduct = frustumToPortal.dotProduct(portalDirection);
		if ( dotProduct > 0 )
        {
            // portal is faced away from Frustum 
            return false;
        }

        // check against frustum culling planes
        bool visible_flag;

        // Check originPlane if told to
        if (mUseOriginPlane)
        {
            // set the visible flag to false
            visible_flag = false;
            // we have to check each corner of the portal
            for (int corner = 0; corner < 4; corner++)
            {
                Plane::Side side = mOriginPlane.getSide(portal->getDerivedCorner(corner));
                if (side != Plane::NEGATIVE_SIDE)
                {
                    visible_flag = true;
                }
            }
            // if the visible_flag is still false, then the origin plane
            // culled all the portal points
            if (visible_flag == false)
            {
                // ALL corners on negative side therefore out of view
                return false;
            }
        }

        // For each active culling plane, see if all portal points are on the negative 
        // side. If so, the portal is not visible
        pit = mActiveCullingPlanes.begin();
        while ( pit != mActiveCullingPlanes.end() )
        {
            PCPlane * plane = *pit;
            // set the visible flag to false
            visible_flag = false;
            // we have to check each corner of the portal
            for (int corner = 0; corner < 4; corner++)
            {
                Plane::Side side =plane->getSide(portal->getDerivedCorner(corner));
                if (side != Plane::NEGATIVE_SIDE)
                {
                    visible_flag = true;
                }
            }
            // if the visible_flag is still false, then this plane
            // culled all the portal points
            if (visible_flag == false)
            {
                // ALL corners on negative side therefore out of view
                return false;
            }
            pit++;
        }
        // no plane culled all the portal points and the norm
        // was facing the frustum, so this portal is visible
        return true;

    }