// main.cxx -- process shapefiles and extract polygon outlines,
//             clipping against and sorting them into the revelant
//             tiles.
//
// 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: main.cxx,v 1.3 1999/05/12 01:12:47 curt Exp $
 

// Include Geographic Foundation Classes library

// libgfc.a includes need this bit o' strangeness
#if defined ( linux )
#  define _LINUX_
#endif
#include <gfc/gadt_polygon.h>
#include <gfc/gdbf.h>
#include <gfc/gshapefile.h>
#undef E
#undef DEG_TO_RAD
#undef RAD_TO_DEG

// include Generic Polygon Clipping Library
extern "C" {
#include <gpc.h>
}

#include <Include/compiler.h>

#include STL_STRING

#include <Debug/logstream.hxx>

#include <Polygon/index.hxx>
#include <Polygon/names.hxx>
#include "shape.hxx"


// return the type of the shapefile record
AreaType get_shapefile_type(GDBFile *dbf, int rec) {
    // GDBFieldDesc *fdesc[128];	// 128 is an arbitrary number here
    GDBFValue *fields;		//an array of field values
    char* dbf_rec;		//a record containing all the fields

    // grab the meta-information for all the fields
    // this applies to all the records in the DBF file.
    // for ( int i = 0; i < dbf->numFields(); i++ ) {
    //   fdesc[i] = dbf->getFieldDesc(i);
    //   cout << i << ") " << fdesc[i]->name << endl;
    // }

    // this is the whole name record
    dbf_rec = dbf->getRecord( rec );

    // parse it into individual fields
    if ( dbf_rec ) {
	fields = dbf->recordDeform( dbf_rec );
    } else {
	return UnknownArea;
    }

    string area = fields[4].str_v;
    // strip leading spaces
    while ( area[0] == ' ' ) {
	area = area.substr(1, area.length() - 1);
    }
    // strip trailing spaces
    while ( area[area.length() - 1] == ' ' ) {
	area = area.substr(0, area.length() - 1);
    }
    // strip other junk encountered
    while ( (int)area[area.length() - 1] == 9 ) {
	area = area.substr(0, area.length() - 1);
    }

    return get_area_type( area );
}


int main( int argc, char **argv ) {
    gpc_polygon gpc_shape;
    int i, j;

    fglog().setLogLevels( FG_ALL, FG_DEBUG );

    if ( argc < 3 ) {
	FG_LOG( FG_GENERAL, FG_ALERT, "Usage: " << argv[0] 
		<< " <shape_file> <work_dir> [ area_string ]" );
	exit(-1);
    }

    FG_LOG( FG_GENERAL, FG_DEBUG, "Opening " << argv[1] << " for reading." );

    // make work directory
    string work_dir = argv[2];
    string command = "mkdir -p " + work_dir;
    system( command.c_str() );

    // allow us to override the area type from the command line.  All
    // polygons in the processed shape file will be assigned this area
    // type
    string force_area_type = "";
    if ( argc == 4 ) {
	force_area_type = argv[3];
    }
	
    // initialize persistant polygon counter
    string counter_file = work_dir + "/../work.counter";
    poly_index_init( counter_file );

    // initialize structure for building gpc polygons
    shape_utils_init();

    GShapeFile * sf = new GShapeFile( argv[1] );
    GDBFile *dbf = new GDBFile( argv[1] );
    string path = argv[2];

    GPolygon shape;
    double  *coords; // in decimal degrees
    int	n_vertices;

    FG_LOG( FG_GENERAL, FG_INFO, "shape file records = " << sf->numRecords() );

    GShapeFile::ShapeType t = sf->shapeType();
    if ( t != GShapeFile::av_Polygon ) {
	FG_LOG( FG_GENERAL, FG_ALERT, "Can't handle non-polygon shape files" );
	exit(-1);
    }

    for ( i = 0; i < sf->numRecords(); i++ ) {
	//fetch i-th record (shape)
	sf->getShapeRec(i, &shape); 
	FG_LOG( FG_GENERAL, FG_DEBUG, "Record = " << i << "  rings = " 
		<< shape.numRings() );

	AreaType area;
	if ( force_area_type.length() == 0 ) {
	    area = get_shapefile_type(dbf, i);
	    FG_LOG( FG_GENERAL, FG_DEBUG, "area type = " << get_area_name(area) 
		    << " (" << (int)area << ")" );
	}

	FG_LOG( FG_GENERAL, FG_INFO, "  record = " << i 
		<< " ring = " << 0 );

	if ( force_area_type.length() > 0 ) {
	    // interior of polygon is assigned to force_area_type,
	    // holes are maintained

	    area = get_area_type( force_area_type );

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == MarshArea ) {
	    // interior of polygon is marsh, holes are water

	    // do main outline first
	    init_shape(&gpc_shape);
	    n_vertices = shape.getRing(0, coords);
	    add_to_shape(n_vertices, coords, &gpc_shape);
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);

	    // do lakes (individually) next
	    for (  j = 1; j < shape.numRings(); j++ ) {
		FG_LOG( FG_GENERAL, FG_INFO, "  record = " << i 
			<< " ring = " << j );
		init_shape(&gpc_shape);
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
		process_shape(path, LakeArea, &gpc_shape);
		free_shape(&gpc_shape);
	    }
	} else if ( area == OceanArea ) {
	    // interior of polygon is ocean, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == LakeArea ) {
	    // interior of polygon is lake, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == DryLakeArea ) {
	    // interior of polygon is dry lake, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == IntLakeArea ) {
	    // interior of polygon is intermittent lake, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == ReservoirArea ) {
	    // interior of polygon is reservoir, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == IntReservoirArea ) {
	    // interior of polygon is intermittent reservoir, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == StreamArea ) {
	    // interior of polygon is stream, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == CanalArea ) {
	    // interior of polygon is canal, holes are islands

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == GlacierArea ) {
	    // interior of polygon is glacier, holes are dry land

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == VoidArea ) {
	    // interior is ????

	    // skip for now
	    FG_LOG(  FG_GENERAL, FG_ALERT, "Void area ... SKIPPING!" );

	    if ( shape.numRings() > 1 ) {
		FG_LOG(  FG_GENERAL, FG_ALERT, "  Void area with holes!" );
		// exit(-1);
	    }

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    // process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else if ( area == NullArea ) {
	    // interior is ????

	    // skip for now
	    FG_LOG(  FG_GENERAL, FG_ALERT, "Null area ... SKIPPING!" );

	    if ( shape.numRings() > 1 ) {
		FG_LOG(  FG_GENERAL, FG_ALERT, "  Null area with holes!" );
		// exit(-1);
	    }

	    init_shape(&gpc_shape);
	    for (  j = 0; j < shape.numRings(); j++ ) {
		n_vertices = shape.getRing(j, coords);
		add_to_shape(n_vertices, coords, &gpc_shape);
	    }
	    // process_shape(path, area, &gpc_shape);
	    free_shape(&gpc_shape);
	} else {
	    FG_LOG(  FG_GENERAL, FG_ALERT, "Uknown area!" );
	    exit(-1);
	}
    }

    return 0;
}


