/*
 * Copyright (c) 1987, 1988, 1989 Stanford University
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Stanford not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Stanford makes no representations about
 * the suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
 * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Implementation of Rectangles and Polygons, objects derived from Graphic.
 */

#include <InterViews/transformer.h>
#include <InterViews/Graphic/grclasses.h>
#include <InterViews/Graphic/polygons.h>

boolean Rect::read (PFile* f) {
    return Graphic::read(f) && _patbr.Read(f) &&
	f->Read(x0) && f->Read(y0) && f->Read(x1) && f->Read(y1);
}

boolean Rect::write (PFile* f) {
    return Graphic::write(f) && _patbr.Write(f) &&
	f->Write(x0) && f->Write(y0) && f->Write(x1) && f->Write(y1);
}

void Rect::draw (Canvas *c, Graphic* gs) {
    if (gs->GetBrush()->Width() != NO_WIDTH) {
	update(gs);
	pRect(c, x0, y0, x1, y1);
    }
}

ClassId Rect::GetClassId () { 
    return _RECT;
}

boolean Rect::IsA (ClassId id) { 
    return _RECT == id || Graphic::IsA(id);
}

Rect::Rect () { }

Rect::Rect (
    Coord x0, Coord y0, Coord x1, Coord y1, Graphic* gr
) : Graphic (gr) {
    if (gr == nil) {
	SetBrush(nil);
    } else {
	SetBrush(gr->GetBrush());
    }
    this->x0 = min(x0, x1);
    this->y0 = min(y0, y1);
    this->x1 = max(x0, x1);
    this->y1 = max(y0, y1);
}

Graphic* Rect::Copy () { 
    return new Rect(x0, y0, x1, y1, this);
}

void Rect::GetOriginal (Coord& x0, Coord& y0, Coord& x1, Coord& y1) {
    x0 = this->x0;
    y0 = this->y0;
    x1 = this->x1;
    y1 = this->y1;
}

void Rect::getExtent (
    float& l, float& b, float& cx, float& cy, float& tol, Graphic* gs
) {
    float width, dummy1, dummy2;

    width = float(gs->GetBrush()->Width());
    tol = (width > 1) ? width/2 : 0;
    transformRect(x0, y0, x1, y1, l, b, dummy1, dummy2, gs);
    transform(float(x0+x1)/2, float(y0+y1)/2, cx, cy, gs);
}

boolean Rect::contains (PointObj& po, Graphic* gs) {
    PointObj pt (&po);
    invTransform(pt.x, pt.y, gs);
    return (
        ((pt.x == x0 || pt.x == x1) && y0 <= pt.y && pt.y <= y1) ||
	((pt.y == y0 || pt.y == y1) && x0 <= pt.x && pt.x <= x1)
    );
}

boolean Rect::intersects (BoxObj& userb, Graphic* gs) {
    Coord x[4], tx[5];
    Coord y[4], ty[5];
    
    x[0] = x[3] = this->x0;
    y[0] = y[1] = this->y0;
    x[2] = x[1] = this->x1;
    y[2] = y[3] = this->y1;
    transformList(x, y, 4, tx, ty, gs);
    tx[4] = tx[0];
    ty[4] = ty[0];
    MultiLineObj ml (tx, ty, 5);
    return ml.Intersects(userb) || ml.Within(userb);
}

void Rect::SetBrush (PBrush* brush) {
    if (br() != Ref(brush)) {
        br(Ref(brush));
	invalidateCaches();
    }
}

PBrush* Rect::GetBrush () { return (PBrush*) br()(); }
void Rect::SetPattern (PPattern* pattern) { pat(Ref(pattern)); }
PPattern* Rect::GetPattern () { return (PPattern*) pat()(); }

void Rect::pat (Ref) { }
Ref Rect::pat () { return (PPattern*)nil; }
void Rect::br (Ref r) { _patbr = r; }
Ref Rect::br () { return _patbr; };

/**************************************************************************/

GFillRect::GFillRect () { }

GFillRect::GFillRect (
    Coord x0, Coord y0, Coord x1, Coord y1, Graphic* gr
) : (x0, y0, x1, y1, gr) {
    if (gr == nil) {
	SetPattern(nil);
    } else {
	SetPattern(gr->GetPattern());
    }
}

void GFillRect::draw (Canvas *c, Graphic* gs) {
    update(gs);
    pFillRect(c, x0, y0, x1, y1);
}

ClassId GFillRect::GetClassId () { return _FILLRECT; }
boolean GFillRect::IsA (ClassId id) { return _FILLRECT == id || Rect::IsA(id); }

Graphic* GFillRect::Copy () {
    return new GFillRect(x0, y0, x1, y1, this);
}

void GFillRect::getExtent (
    float& l, float& b, float& cx, float& cy, float& tol, Graphic* gs
){
    float dummy1, dummy2;

    transformRect(x0, y0, x1, y1, l, b, dummy1, dummy2, gs);
    transform(float(x0+x1)/2, float(y0+y1)/2, cx, cy, gs);
    tol = 0;
}

boolean GFillRect::contains (PointObj& po, Graphic* gs) {
    PointObj pt (&po);
    invTransform(pt.x, pt.y, gs);
    BoxObj b (x0, y0, x1, y1);
    return b.Contains(pt);
}

boolean GFillRect::intersects (BoxObj& userb, Graphic* gs) {
    Transformer* t = gs->GetTransformer();
    Coord tx0, ty0, tx1, ty1;

    if (t != nil && t->Rotated()) {
	Coord x[4], tx[5];
	Coord y[4], ty[5];

	x[0] = x[3] = this->x0;
	y[0] = y[1] = this->y0;
	x[2] = x[1] = this->x1;
	y[2] = y[3] = this->y1;
	transformList(x, y, 4, tx, ty, gs);
	tx[4] = tx[0];
	ty[4] = ty[0];
	FillPolygonObj fp (tx, ty, 5);
	return fp.Intersects(userb);

    } else if (t != nil) {
	t->Transform(x0, y0, tx0, ty0);
	t->Transform(x1, y1, tx1, ty1);
	BoxObj b1 (tx0, ty0, tx1, ty1);
	return b1.Intersects(userb);

    } else {
	BoxObj b2 (x0, y0, x1, y1);
	return b2.Intersects(userb);
    }
}

void GFillRect::pat (Ref r) { _patbr = r; }
Ref GFillRect::pat () { return _patbr; }
void GFillRect::br (Ref) { }
Ref GFillRect::br () { return (PBrush*)nil; }

void GFillRect::SetBrush (PBrush*) { }

/*****************************************************************************/

void GPolygon::draw (Canvas *c, Graphic* gs) {
    if (gs->GetBrush()->Width() != NO_WIDTH) {
	update(gs);
	pPolygon(c, x, y, count);
    }
}

ClassId GPolygon::GetClassId () {
    return _POLYGON;
}

boolean GPolygon::IsA (ClassId id) {
    return _POLYGON == id || MultiLine::IsA(id);
}

GPolygon::GPolygon () { }

GPolygon::GPolygon (
    Coord* x, Coord* y, int count, Graphic* gr
) : (x, y, count, gr) { }

Graphic* GPolygon::Copy () {
    return new GPolygon(x, y, count, this);
}

boolean GPolygon::contains (PointObj& po, Graphic* gs) {
    BoxObj b;
    PointObj pt (&po);
    
    getBox(b, gs);
    if (b.Contains(pt)) {
	MultiLineObj ml (x, y, count);
	LineObj l (x[count - 1], y[count - 1], *x, *y);
	invTransform(pt.x, pt.y, gs);
	return ml.Contains(pt) || l.Contains(pt);
    }
    return false;
}

boolean GPolygon::intersects (BoxObj& userb, Graphic* gs) {
    Coord* convx, *convy;
    BoxObj b;
    boolean result = false;

    getBox(b, gs);
    if (b.Intersects(userb)) {
	convx = new Coord[count + 1];
	convy = new Coord[count + 1];
	transformList(x, y, count, convx, convy, gs);
	convx[count] = *convx;
	convy[count] = *convy;
	MultiLineObj ml (convx, convy, count + 1);
	result = ml.Intersects(userb);
	delete convx;
	delete convy;
    }
    return result;
}

/*****************************************************************************/

void FillPolygon::draw (Canvas *c, Graphic* gs) {
    update(gs);
    pFillPolygon(c, x, y, count);
}

ClassId FillPolygon::GetClassId () {
    return _FILLPOLYGON;
}

boolean FillPolygon::IsA (ClassId id) {
    return _FILLPOLYGON == id || GPolygon::IsA(id);
}

FillPolygon::FillPolygon () { }

FillPolygon::FillPolygon (
    Coord* x, Coord* y, int count, Graphic* gr
) : (x, y, count, gr) {
    if (gr == nil) {
	SetPattern(nil);
    } else {
	SetPattern(gr->GetPattern());
    }
}

Graphic* FillPolygon::Copy () { 
    return new FillPolygon(x, y, count, this);
}

void FillPolygon::getExtent (
    float& l, float& b, float& cx, float& cy, float& tol, Graphic* gs
) {
    register int i;
    float bx0, by0, bx1, by1, tcx, tcy, dummy1, dummy2;
	
    if (extentCached()) {
	getCachedExtent(bx0, by0, tcx, tcy, tol);
	bx1 = 2*tcx - bx0;
	by1 = 2*tcy - by0;
    } else {
	bx0 = bx1 = x[0]; by0 = by1 = y[0];
	for (i = 1; i < count; ++i) {
	    bx0 = min(bx0, float(x[i]));
	    by0 = min(by0, float(y[i]));
	    bx1 = max(bx1, float(x[i]));
	    by1 = max(by1, float(y[i]));
	}
	tcx = (bx0 + bx1) / 2;
	tcy = (by0 + by1) / 2;
	tol = 0;
	cacheExtent(bx0, by0, tcx, tcy, tol);
    }
    transformRect(bx0, by0, bx1, by1, l, b, dummy1, dummy2, gs);
    transform(tcx, tcy, cx, cy, gs);
}

boolean FillPolygon::contains (PointObj& po, Graphic* gs) {
    BoxObj b;
    PointObj pt (&po);

    getBox(b, gs);
    if (b.Contains(pt)) {
	FillPolygonObj fp (x, y, count);
	invTransform(pt.x, pt.y, gs);
	return fp.Contains(pt);
    }
    return false;
}

boolean FillPolygon::intersects (BoxObj& userb, Graphic* gs) {
    Coord* convx, *convy;
    BoxObj b;
    boolean result = false;
    
    getBox(b, gs);
    if (b.Intersects(userb)) {
	convx = new Coord[count + 1];
	convy = new Coord[count + 1];
	transformList(x, y, count, convx, convy, gs);
	FillPolygonObj fp (convx, convy, count);
	result = fp.Intersects(userb);
	delete convx;
	delete convy;
    }
    return result;    
}

void FillPolygon::pat (Ref r) { _patbr = r; }
Ref FillPolygon::pat () { return _patbr; }
void FillPolygon::br (Ref) { }
Ref FillPolygon::br () { return (PBrush*)nil; }

void FillPolygon::SetBrush (PBrush*) { }
