/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <gdefines.h>
#include <Box.hpp>
#include <Projection.hpp>

#define TRUE 1
#define FALSE 0

Box ::Box(const Box& r) :
    _bll(r._bll),
    _bur(r._bur) {}

Box ::Box(const Point& o, const Point& c)
{
    _bll = o;
    _bur = c;
}

Box Box ::operator=(Box& b)
{
    _bll = b._bll;
    _bur = b._bur;
    return *this;
}

void Box ::print()
{
    printf("Box : Lower Left (%.1f,%.1f) UpperRight (%.1f,%.1f)\n", _bll.x(), _bll.y(), _bur.x(), _bur.y());
}

Box Box ::intersection(const Box& b) const
{
    float urx     = b.upperRightX();
    float ury     = b.upperRightY();
    float llx     = b.lowerLeftX();
    float lly     = b.lowerLeftY();
    float thisurx = this->upperRightX();
    float thisury = this->upperRightY();
    float thisllx = this->lowerLeftX();
    float thislly = this->lowerLeftY();
    float iurx, iury, illx, illy;
    float aux;

    if (urx < llx) {
        aux = llx;
        llx = urx;
        urx = aux;
    }
    if (ury < lly) {
        aux = lly;
        lly = ury;
        ury = aux;
    }
    if (thisurx < thisllx) {
        aux     = thisllx;
        thisllx = thisurx;
        thisurx = aux;
    }
    if (thisury < thislly) {
        aux     = thislly;
        thislly = thisury;
        thisury = aux;
    }


    illx = (thisllx < llx) ? llx : thisllx;
    illy = (thislly < lly) ? lly : thislly;
    iurx = (thisurx < urx) ? thisurx : urx;
    iury = (thisury < ury) ? thisury : ury;

    return Box(Point(illx, illy), Point(iurx, iury));
}

Box Box ::unionx(const Box& b) const
{
    return Box(_bll.pmin(b._bll), _bur.pmax(b._bur));
}

short Box ::contains(const Point& p) const
{
    return (_bll <= p && p <= _bur);
}

short Box ::contains(const Box& r) const
{
    return (contains(r._bll) && contains(r._bur));
}

short Box ::intersects(const Box& r) const
{
    float urx     = r.upperRightX();
    float ury     = r.upperRightY();
    float llx     = r.lowerLeftX();
    float lly     = r.lowerLeftY();
    float thisurx = this->upperRightX();
    float thisury = this->upperRightY();
    float thisllx = this->lowerLeftX();
    float thislly = this->lowerLeftY();
    float aux;

    if (urx < llx) {
        aux = llx;
        llx = urx;
        urx = aux;
    }
    if (ury < lly) {
        aux = lly;
        lly = ury;
        ury = aux;
    }
    if (thisurx < thisllx) {
        aux     = thisllx;
        thisllx = thisurx;
        thisurx = aux;
    }
    if (thisury < thislly) {
        aux     = thislly;
        thislly = thisury;
        thisury = aux;
    }

    if (urx < thisllx || thisurx < llx ||
        ury < thislly || thisury < lly)
        return FALSE;
    return TRUE;
}

void Box ::remap(SProjection* in, SProjection* out)
{
    float aux,
        step,
        x1   = _bll.x(),
        x2   = _bur.x(),
        y1   = _bll.y(),
        y2   = _bur.y(),
        xmin = MAXFLOAT,
        xmax = -MAXFLOAT,
        ymin = MAXFLOAT,
        ymax = -MAXFLOAT;

    float bigval = MAXFLOAT / 10.;

    Point p;
    step = (x2 - x1) / NSTEPS;
    for (aux = x1; aux < x2; aux += step) {
        /* lower edge */
        p.x(aux);
        p.y(y1);
        p = in->PC2LL(p);
        if (p.x() < bigval) {
            p = out->LL2PC(p);
            if (p.x() < bigval) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.x() < xmin)
                    xmin = p.x();
                if (p.x() > xmax)
                    xmax = p.x();
                if (p.y() < ymin)
                    ymin = p.y();
                if (p.y() > ymax)
                    ymax = p.y();
            }
        }

        /* upper edge */
        p.x(aux);
        p.y(y2);
        p = in->PC2LL(p);
        if (p.x() < bigval) {
            p = out->LL2PC(p);
            if (p.x() < bigval) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.x() < xmin)
                    xmin = p.x();
                if (p.x() > xmax)
                    xmax = p.x();
                if (p.y() < ymin)
                    ymin = p.y();
                if (p.y() > ymax)
                    ymax = p.y();
            }
        }
    }

    step = (y2 - y1) / NSTEPS;
    for (aux = y1; aux < y2; aux += step) {
        /* left edge */
        p.x(x1);
        p.y(aux);
        p = in->PC2LL(p);
        if (p.x() < bigval) {
            p = out->LL2PC(p);
            if (p.x() < bigval) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.x() < xmin)
                    xmin = p.x();
                if (p.x() > xmax)
                    xmax = p.x();
                if (p.y() < ymin)
                    ymin = p.y();
                if (p.y() > ymax)
                    ymax = p.y();
            }
        }


        /* right edge */
        p.x(x2);
        p.y(aux);
        p = in->PC2LL(p);
        if (p.x() < bigval) {
            p = out->LL2PC(p);
            if (p.x() < bigval) {
                /* search for minimum and maximum coordinates,*/
                /* if a valid remapped point is calculated */
                if (p.x() < xmin)
                    xmin = p.x();
                if (p.x() > xmax)
                    xmax = p.x();
                if (p.y() < ymin)
                    ymin = p.y();
                if (p.y() > ymax)
                    ymax = p.y();
            }
        }
    }

    _bll.x(xmin);
    _bll.y(ymin);
    if (_bll.tooBig())
        _bll.init(-MAXFLOAT, -MAXFLOAT);
    _bur.x(xmax);
    _bur.y(ymax);
    if (_bur.tooBig())
        _bur.init(MAXFLOAT, MAXFLOAT);

    return;
}

void Box ::remap(SProjection* out)
{
    float aux,
        step,
        x1   = _bll.x(),
        x2   = _bur.x(),
        y1   = _bll.y(),
        y2   = _bur.y(),
        xmin = MAXFLOAT,
        xmax = -MAXFLOAT,
        ymin = MAXFLOAT,
        ymax = -MAXFLOAT;
    Point p;
    step = (x2 - x1) / NSTEPS;
    for (aux = x1; aux < x2; aux += step) {
        /* lower edge */
        p.x(aux);
        p.y(y1);
        p = out->LL2PC(p);
        if (p.x() < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.x() < xmin)
                xmin = p.x();
            if (p.x() > xmax)
                xmax = p.x();
            if (p.y() < ymin)
                ymin = p.y();
            if (p.y() > ymax)
                ymax = p.y();
        }


        /* upper edge */
        p.x(aux);
        p.y(y2);
        p = out->LL2PC(p);
        if (p.x() < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.x() < xmin)
                xmin = p.x();
            if (p.x() > xmax)
                xmax = p.x();
            if (p.y() < ymin)
                ymin = p.y();
            if (p.y() > ymax)
                ymax = p.y();
        }
    }

    step = (y2 - y1) / NSTEPS;
    for (aux = y1; aux < y2; aux += step) {
        /* left edge */
        p.x(x1);
        p.y(aux);
        p = out->LL2PC(p);
        if (p.x() < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.x() < xmin)
                xmin = p.x();
            if (p.x() > xmax)
                xmax = p.x();
            if (p.y() < ymin)
                ymin = p.y();
            if (p.y() > ymax)
                ymax = p.y();
        }


        /* right edge */
        p.x(x2);
        p.y(aux);
        p = out->LL2PC(p);
        if (p.x() < (MAXFLOAT / 10.)) {
            /* search for minimum and maximum coordinates,*/
            /* if a valid remapped point is calculated */
            if (p.x() < xmin)
                xmin = p.x();
            if (p.x() > xmax)
                xmax = p.x();
            if (p.y() < ymin)
                ymin = p.y();
            if (p.y() > ymax)
                ymax = p.y();
        }
    }
    _bll.x(xmin);
    _bll.y(ymin);
    _bur.x(xmax);
    _bur.y(ymax);

    return;
}


short Box ::load(FILE* fp)
{
    float x1 = 0,
          x2 = 0,
          y1 = 0,
          y2 = 0;

    if (fscanf(fp, "%f%f%f%f", &x1, &y1, &x2, &y2) == EOF)
        return FALSE;
    _bll.x(x1);
    _bll.y(y1);
    _bur.x(x2);
    _bur.y(y2);

    return TRUE;
}

short Box ::save(FILE* fp)
{
    if (fprintf(fp, "%f %f %f %f ", _bll.x(), _bll.y(), _bur.x(), _bur.y()) == EOF)
        return FALSE;
    return TRUE;
}
