#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>

#include "../util/memory.h"
#include "../util/error.h"
#include "../util/reader.h"
#include "zone.h"

#define zones_IMPORT
#include "zones.h"


struct zones_Type {
	
	/** Path to the zones file. */
	char *path;
	
	/* List of zones. */
	int zones_capacity;
	int zones_length;
	zone_Type **zones;
	
	/**
	 * Scheduled next update time (s). Ignore load requests since then to reduce
	 * overload.
	 */
	time_t next_zone_update;
};


static void zones_destruct(void *p)
{
	//printf("DEBUG: %s\n", __FUNCTION__);
	zones_Type *this = (zones_Type *) p;
	while( this->zones_length > 0 ){
		this->zones_length--;
		memory_dispose(this->zones[ this->zones_length ]);
	}
	memory_dispose(this->zones);
	memory_dispose(this->path);
}


static void zones_parseZone(zones_Type *this, reader_Type *in, char **argv, int argc)
{
	if( argc != 5 )
		error_external("%s:%d: expected 5 fields, found %d", reader_getPath(in), reader_getLineNumber(in), argv);
	double lat1, lat2, lon1, lon2;
	if( ! earth_parseLatitude(argv[0], &lat1) )
		error_external("%s:%d: invalid latitude: %s", reader_getPath(in), reader_getLineNumber(in), argv[0]);
	if( ! earth_parseLatitude(argv[1], &lat2) )
		error_external("%s:%d: invalid latitude: %s", reader_getPath(in), reader_getLineNumber(in), argv[1]);
	if( ! earth_parseLongitude(argv[2], &lon1) )
		error_external("%s:%d: invalid longitude: %s", reader_getPath(in), reader_getLineNumber(in), argv[2]);
	if( ! earth_parseLongitude(argv[3], &lon2) )
		error_external("%s:%d: invalid longitude: %s", reader_getPath(in), reader_getLineNumber(in), argv[3]);
	if( !(lat1 < lat2 && lon1 < lon2) )
		error_external("%s:%d: check latitude and longitude ordering", reader_getPath(in), reader_getLineNumber(in));
	
	// Check the scenery file is indeed there:
	char *path = reader_resolveRelativePath(reader_getPath(in), argv[4]);
	if( ! reader_isReadable(path) )
		error_external("%s:%d: file %s does not exist or it is not readable", reader_getPath(in), reader_getLineNumber(in), path);
		
	// Create zone object:
	zone_Type *zone = zone_new(path, lat1, lat2, lon1, lon2);
	memory_dispose(path);
	
	// Check if this zone overlaps with any other zone:
	int i;
	for(i = this->zones_length-1; i >= 0; i--){
		if( zone_overlaps(zone, this->zones[i]) )
			error_external("%s:%d: zone %s overlaps with zone %s",
				reader_getPath(in), reader_getLineNumber(in),
				zone_getPath(zone), zone_getPath(this->zones[i]));
	}
		
	// Ok, add this zone:
	if( this->zones_length >= this->zones_capacity ){
		this->zones_capacity += 100;
		this->zones = memory_realloc(this->zones, this->zones_capacity * sizeof(zone_Type *));
	}
	this->zones[this->zones_length++] = zone;
}


zones_Type * zones_new(char *path)
{
	zones_Type * this = memory_allocate(sizeof(zones_Type), zones_destruct);
	this->path = memory_strdup(path);
	this->zones_capacity = 0;
	this->zones_length = 0;
	this->zones = NULL;
	this->next_zone_update = 0;
	
	reader_Type *in = reader_new(path);
	char line[999];
	while( reader_getLine(in, line, sizeof(line)) ){
		char *s = line;
		while( isspace(*s) )
			s++;
		if( *s == 0 || *s == '#' )
			continue;
		char *argv[5];
		int argc;
		if( ! reader_split(line, &argc, argv, 5) )
			error_external("%s:%d: too many fields", reader_getPath(in), reader_getLineNumber(in));
		zones_parseZone(this, in, argv, argc);
	}
	if( reader_getError(in) != NULL )
		error_external("%s: %s", reader_getPath(in), reader_getError(in));
	memory_dispose(in);
	return this;
}


zone_Type * zones_load(zones_Type *this, earth_LatLonAlt *p, zone_Type *last_known, int forced)
{
	time_t now = time(NULL);
	if( ! forced && now < this->next_zone_update )
		return last_known;
	zone_Type *current_zone = NULL;
	this->next_zone_update = now + 10;
	int i;
	for( i = this->zones_length - 1; i >= 0; i--){
		zone_Type *zone = this->zones[i];
		if( zone_isClose(zone, p) ){
			zone_load(zone);
			if( zone_includesLocation(zone, p) )
				current_zone = zone;
		} else {
			zone_purge(zone, 0);
		}
	}
	return current_zone;
}
