/*
** GiST support function. Called from gserialized_gist_consistent below.
*/
static inline bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
{
	bool retval;

	POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
	               strategy, g2d_counter_internal++, box2df_to_string(query), box2df_to_string(key) );

	switch (strategy)
	{
	case RTOverlapStrategyNumber:
		retval = (bool) box2df_overlaps(key, query);
		break;
	case RTSameStrategyNumber:
	case RTContainsStrategyNumber:
	case RTOldContainsStrategyNumber:
		retval = (bool) box2df_contains(key, query);
		break;
	case RTContainedByStrategyNumber:
	case RTOldContainedByStrategyNumber:
		retval = (bool) box2df_overlaps(key, query);
		break;
	default:
		retval = FALSE;
	}

	return (retval);
}
static inline bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
{
	bool retval;
	
	switch (strategy)
	{
		
	/* Basic overlaps */
	case RTOverlapStrategyNumber:
		retval = (bool) box2df_overlaps(key, query);
		break;
	case RTSameStrategyNumber:
	case RTContainsStrategyNumber:
	case RTOldContainsStrategyNumber:
		retval = (bool) box2df_contains(key, query);
		break;
	case RTContainedByStrategyNumber:
	case RTOldContainedByStrategyNumber:
		retval = (bool) box2df_overlaps(key, query);
		break;
		
	/* To one side */
	case RTAboveStrategyNumber:
		retval = (bool)(!box2df_overbelow(key, query));
		break;
	case RTBelowStrategyNumber:
		retval = (bool)(!box2df_overabove(key, query));
		break;
	case RTRightStrategyNumber:
		retval = (bool)(!box2df_overleft(key, query));
		break;
	case RTLeftStrategyNumber:
		retval = (bool)(!box2df_overright(key, query));
		break;

	/* Overlapping to one side */
	case RTOverAboveStrategyNumber:
		retval = (bool)(!box2df_below(key, query));
		break;
	case RTOverBelowStrategyNumber:
		retval = (bool)(!box2df_above(key, query));
		break;
	case RTOverRightStrategyNumber:
		retval = (bool)(!box2df_left(key, query));
		break;
	case RTOverLeftStrategyNumber:
		retval = (bool)(!box2df_right(key, query));
		break;
		
	default:
		retval = FALSE;
	}

	return (retval);
}
/*
** GiST support function. Called from gserialized_gist_consistent below.
*/
static inline bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
{
	bool retval;

	POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
	               strategy, g2d_counter_leaf++);

	switch (strategy)
	{
	case RTOverlapStrategyNumber:
		retval = (bool) box2df_overlaps(key, query);
		break;
	case RTSameStrategyNumber:
		retval = (bool) box2df_equals(key, query);
		break;
	case RTContainsStrategyNumber:
	case RTOldContainsStrategyNumber:
		retval = (bool) box2df_contains(key, query);
		break;
	case RTContainedByStrategyNumber:
	case RTOldContainedByStrategyNumber:
		retval = (bool) box2df_contains(query, key);
		break;
	default:
		retval = FALSE;
	}

	return (retval);
}
/**
* Calculate the box->box distance.
*/
static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
{
    /* Check for overlap */
    if ( box2df_overlaps(a, b) )
        return 0.0;

    if ( box2df_left(a, b) )
    {
        if ( box2df_above(a, b) )
			return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
		if ( box2df_below(a, b) )
			return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
		else
			return b->xmin - a->xmax;
	}
	if ( box2df_right(a, b) )
	{
        if ( box2df_above(a, b) )
			return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
		if ( box2df_below(a, b) )
			return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
		else
			return a->xmin - b->xmax;
	}
	if ( box2df_above(a, b) )
	{
		if ( box2df_left(a, b) )
			return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
		if ( box2df_right(a, b) )
			return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
		else
			return a->ymin - b->ymax;
	}
	if ( box2df_below(a, b) )
	{
		if ( box2df_left(a, b) )
			return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
		if ( box2df_right(a, b) )
			return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
		else
			return b->ymin - a->ymax;
	}
	
	return MAXFLOAT;
}
/**
* Calculate the The node_box_edge->query_centroid distance 
* between the boxes.
*/
static double box2df_distance_node_centroid(const BOX2DF *node, const BOX2DF *query)
{
    BOX2DF q;
    double qx, qy;
    double d = 0.0;

    /* Turn query into point */
    q.xmin = q.xmax = (query->xmin + query->xmax) / 2.0;
    q.ymin = q.ymax = (query->ymin + query->ymax) / 2.0;
    qx = q.xmin;
    qy = q.ymin;

    /* Check for overlap */
    if ( box2df_overlaps(node, &q) == LW_TRUE )
        return 0.0;

    /* Above or below */
    if ( qx >= node->xmin && qx <= node->xmax )
    {
        if( qy > node->ymax )
            d = qy - node->ymax;
        else if ( qy < node->ymin )
            d = node->ymin - qy;
        return d;
    }
    /* Left or right */
    else if ( qy >= node->ymin && qy <= node->ymax )
    {
        if ( qx > node->xmax )
            d = qx - node->xmax;
        else if ( qx < node->xmin )
            d = node->xmin - qx;
        return d;
    }
    /* Corner quadrants */
    else
    {
        /* below/left of xmin/ymin */
        if ( qx < node->xmin && qy < node->ymin )
        {
            d = (node->xmin - qx) * (node->xmin - qx) +
                (node->ymin - qy) * (node->ymin - qy);
        }
        /* above/left of xmin/ymax */
        else if ( qx < node->xmin && qy > node->ymax )
        {
            d = (node->xmin - qx) * (node->xmin - qx) +
                (node->ymax - qy) * (node->ymax - qy);
        }
        /* above/right of xmax/ymax */
        else if ( qx > node->xmax && qy > node->ymax )
        {
            d = (node->xmax - qx) * (node->xmax - qx) +
                (node->ymax - qy) * (node->ymax - qy);
        }
        /* below/right of xmax/ymin */
        else if ( qx > node->xmin && qy < node->ymin )
        {
            d = (node->xmax - qx) * (node->xmax - qx) +
                (node->ymin - qy) * (node->ymin - qy);
        }
        else
        {
            /*ERROR*/
        }
    }
    
    return sqrt(d);
}