// clipper.cxx -- top level routines to take a series of arbitrary areas and
//                produce a tight fitting puzzle pieces that combine to make a
//                tile
//
// Written by Curtis Olson, started February 1999.
//
// Copyright (C) 1999  Curtis L. Olson  - curt@flightgear.org
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
// (Log is kept at end of this file)
 


#include "clipper.hxx"

#include <Debug/logstream.hxx>
#include <Include/fg_constants.h>
#include <Misc/fgstream.hxx>
#include <Polygon/names.hxx>


#define EXTRA_SAFETY_CLIP

#define FG_MAX_VERTICES 100000

static gpc_vertex_list v_list;
// static gpc_polygon poly;
static FGPolyList polys_in, polys_out;


// Initialize Clipper (allocate and/or connect structures)
bool fgClipperInit() {
    v_list.num_vertices = 0;
    v_list.vertex = new gpc_vertex[FG_MAX_VERTICES];;

    return true;
}


// Load a polygon definition file
bool fgClipperLoadPolygons(const string& path) {
    string poly_name;
    AreaType poly_type;
    int contours, count, i, j;
    double startx, starty, x, y, lastx, lasty;

    FG_LOG( FG_CLIPPER, FG_INFO, "Loading " << path << " ..." );

    fg_gzifstream in( path );

    if ( !in ) {
        FG_LOG( FG_CLIPPER, FG_ALERT, "Cannot open file: " << path );
	exit(-1);
    }

    in >> skipcomment;
    while ( !in.eof() ) {
	in >> poly_name;
	cout << "poly name = " << poly_name << endl;
	poly_type = get_area_type( poly_name );
	cout << "poly type (int) = " << (int)poly_type << endl;
	in >> contours;
	cout << "num contours = " << contours << endl;

	for ( i = 0; i < contours; ++i ) {
	    in >> count;

	    if ( count < 3 ) {
		FG_LOG( FG_CLIPPER, FG_ALERT, 
			"Polygon with less than 3 data points." );
		exit(-1);
	    }

	    in >> startx;
	    in >> starty;
	    v_list.vertex[0].x = startx;
	    v_list.vertex[0].y = starty;
	    FG_LOG( FG_CLIPPER, FG_BULK, "0 = " 
		    << startx << ", " << starty );

	    for ( j = 1; j < count - 1; ++j ) {
		in >> x;
		in >> y;
		v_list.vertex[j].x = x;
		v_list.vertex[j].y = y;
		FG_LOG( FG_CLIPPER, FG_BULK, j << " = " << x << ", " << y );
	    }
	    v_list.num_vertices = count - 1;

	    in >> lastx;
	    in >> lasty;

	    if ( (fabs(startx - lastx) < FG_EPSILON) 
		 && (fabs(starty - lasty) < FG_EPSILON) ) {
		// last point same as first, discard
	    } else {
		v_list.vertex[count - 1].x = lastx;
		v_list.vertex[count - 1].y = lasty;
		++v_list.num_vertices;
		FG_LOG( FG_CLIPPER, FG_BULK, count - 1 << " = " 
			<< lastx << ", " << lasty );
	    }

	    gpc_polygon *poly = new gpc_polygon;
	    poly->num_contours = 0;
	    poly->contour = NULL;
	    gpc_add_contour( poly, &v_list );

	    int area = (int)poly_type;
	    if ( area < FG_MAX_AREAS ) {
		polys_in.polys[area].push_back(poly);
	    } else {
		FG_LOG( FG_CLIPPER, FG_ALERT, "Polygon type out of range = " 
			<< poly_type);
		exit(-1);
	    }
	}

	in >> skipcomment;
    }

    // FILE *ofp= fopen("outfile", "w");
    // gpc_write_polygon(ofp, &polys.landuse);

    return true;
}


// Do actually clipping work
bool fgClipperMaster(const point2d& min, const point2d& max) {
    gpc_polygon accum, result_diff, result_union, tmp;
    polylist_iterator current, last;

    FG_LOG( FG_CLIPPER, FG_INFO, "Running master clipper" );

    accum.num_contours = 0;

    cout << "  (" << min.x << "," << min.y << ") (" 
	 << max.x << "," << max.y << ")" << endl;

    // set up clipping tile
    v_list.vertex[0].x = min.x;
    v_list.vertex[0].y = min.y;

    v_list.vertex[1].x = max.x;
    v_list.vertex[1].y = min.y;

    v_list.vertex[2].x = max.x;
    v_list.vertex[2].y = max.y;

    v_list.vertex[3].x = min.x;
    v_list.vertex[3].y = max.y;

    v_list.num_vertices = 4;

    polys_in.safety_base.num_contours = 0;
    polys_in.safety_base.contour = NULL;
    gpc_add_contour( &polys_in.safety_base, &v_list );

    // process polygons in priority order
    for ( int i = 0; i < FG_MAX_AREAS; ++i ) {

	current = polys_in.polys[i].begin();
	last = polys_in.polys[i].end();
	for ( ; current != last; ++current ) {
	    FG_LOG( FG_CLIPPER, FG_DEBUG, get_area_name( (AreaType)i ) 
		    << " = " << (*current)->contour->num_vertices );

#ifdef EXTRA_SAFETY_CLIP
	    // clip to base tile
	    gpc_polygon_clip(GPC_INT, &polys_in.safety_base, *current, &tmp);
#else
	    &tmp = *current;
#endif

	    // clip current polygon against previous higher priority
	    // stuff
	    if ( accum.num_contours == 0 ) {
		result_diff = tmp;
		result_union = tmp;
	    } else {
		gpc_polygon_clip(GPC_DIFF, &accum, &tmp, &result_diff);
		gpc_polygon_clip(GPC_UNION, &accum, &tmp, &result_union);
	    }
	    
	    polys_out.polys[i].push_back(&result_diff);
	    accum = result_union;
	}
    }

    // finally, what ever is left over goes to base terrain

    // clip to accum against original base tile
    gpc_polygon_clip(GPC_DIFF, &polys_in.safety_base, &accum, 
		     &polys_out.safety_base);

    // tmp output accum
    FILE *ofp= fopen("accum", "w");
    gpc_write_polygon(ofp, &accum);

    // tmp output safety_base
    ofp= fopen("safety_base", "w");
    gpc_write_polygon(ofp, &polys_out.safety_base);

    return true;
}


// $Log$
// Revision 1.1  1999/03/01 15:39:39  curt
// Initial revision.
//