/*
    Stack filter

    Copyright (C) 2002 Robert Lipe, robertlipe@usa.net

    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., 59 Temple Place - Suite 330, Boston, MA 02111 USA

 */

#include "defs.h"
#include "filterdefs.h"

#if FILTERS_ENABLED

#define MYNAME "Stack filter"

static char *opt_push = NULL;
static char *opt_copy = NULL;
static char *opt_pop = NULL;
static char *opt_append = NULL;
static char *opt_discard = NULL;
static char *opt_replace = NULL;
static char *opt_swap = NULL;
static char *opt_depth = NULL;
static char *nowarn = NULL;
static int  warnings_enabled = 1;
static int  swapdepth = 0;

static
arglist_t stackfilt_args[] = {
	{"push", &opt_push, "Push waypoint list onto stack", NULL, 
		ARGTYPE_BEGIN_EXCL | ARGTYPE_BEGIN_REQ | ARGTYPE_BOOL, ARG_NOMINMAX},
	{"pop", &opt_pop, "Pop waypoint list from stack", NULL,
		ARGTYPE_BOOL, ARG_NOMINMAX},
	{"swap", &opt_swap, "Swap waypoint list with <depth> item on stack", 
		NULL, ARGTYPE_END_EXCL | ARGTYPE_END_REQ | ARGTYPE_BOOL, ARG_NOMINMAX},
	{"copy", &opt_copy, "(push) Copy waypoint list", NULL,
		ARGTYPE_BOOL, ARG_NOMINMAX},
	{"append", &opt_append, "(pop) Append list", NULL,
		ARGTYPE_BEGIN_EXCL | ARGTYPE_BOOL, ARG_NOMINMAX},
	{"discard", &opt_discard, "(pop) Discard top of stack", 
		NULL, ARGTYPE_BOOL, ARG_NOMINMAX},
	{"replace", &opt_replace, "(pop) Replace list (default)", 
		NULL, ARGTYPE_END_EXCL | ARGTYPE_BOOL, ARG_NOMINMAX},
	{"depth", &opt_depth, "(swap) Item to use (default=1)", 
		NULL, ARGTYPE_INT, "0", NULL},
	{"nowarn", &nowarn, "Suppress cleanup warning", NULL, 
		ARGTYPE_BOOL | ARGTYPE_HIDDEN, ARG_NOMINMAX},
	ARG_TERMINATOR
};

struct stack_elt {
	queue waypts;
        queue routes;
        queue tracks;	
	unsigned int waypt_ct;
	int route_count;
	int track_count;
	struct stack_elt *next;
} *stack = NULL;


void 
stackfilt_process(void)
{
	struct stack_elt *tmp_elt = NULL;
	queue *elem = NULL;
	queue *tmp = NULL;
	queue tmp_queue;
	unsigned int tmp_count;
	
	if ( opt_push ) {
		tmp_elt = (struct stack_elt *)xmalloc(sizeof(struct stack_elt));
		
		QUEUE_MOVE(&(tmp_elt->waypts), &waypt_head);
		tmp_elt->waypt_ct = waypt_count();
		set_waypt_count(0);
		tmp_elt->next = stack;
		stack = tmp_elt;
		if ( opt_copy ) {
			QUEUE_FOR_EACH( &(stack->waypts), elem, tmp ) {
				waypt_add( waypt_dupe((waypoint *)elem));
			}
		}	
		
		tmp = NULL;
		route_backup( &(tmp_elt->route_count), &tmp );
		QUEUE_MOVE( &(tmp_elt->routes), tmp );
		xfree( tmp );
		if ( !opt_copy ) {
			route_flush_all_routes();
		}
		
		tmp = NULL;
	        track_backup( &(tmp_elt->track_count), &tmp );
		QUEUE_MOVE( &(tmp_elt->tracks), tmp );
		xfree( tmp );
		if ( !opt_copy ) {
			route_flush_all_tracks();
		}
		
	}
	else if ( opt_pop ) { 
		tmp_elt = stack;
		if ( !tmp_elt ) {
			fatal( MYNAME ": stack empty\n");
		}
		if ( opt_append ) {
			QUEUE_FOR_EACH( &(stack->waypts), elem, tmp ) {
				waypt_add( (waypoint *)elem);
			}
			route_append( &(stack->routes));
			route_flush( &(stack->routes));
			track_append( &(stack->tracks));
			route_flush( &(stack->tracks));
		}
		else if ( opt_discard ) {
			waypt_flush( &(stack->waypts));
			route_flush( &(stack->routes));
			route_flush( &(stack->tracks));
		}
		else {
			waypt_flush( &waypt_head );
			QUEUE_MOVE(&(waypt_head), &(stack->waypts) );
			set_waypt_count(stack->waypt_ct);
			
			route_restore( &(stack->routes));
			track_restore( &(stack->tracks));
		} 

		stack = tmp_elt->next;
		xfree( tmp_elt );
	}
	else if ( opt_swap ) {
		tmp_elt = stack;
		while ( swapdepth > 1 ) {
			if ( !tmp_elt->next ) {
        			fatal (MYNAME ": swap with nonexistent element\n");
			}
			tmp_elt = tmp_elt->next;
			swapdepth--;
		}
		QUEUE_MOVE(&tmp_queue, &(tmp_elt->waypts) );
		QUEUE_MOVE(&(tmp_elt->waypts), &waypt_head );
		QUEUE_MOVE(&waypt_head, &tmp_queue );
		
		QUEUE_MOVE(&tmp_queue, &(tmp_elt->routes));
		tmp = NULL;
		route_backup( &(tmp_elt->route_count), &tmp);
		QUEUE_MOVE(&(tmp_elt->routes), tmp );
		xfree( tmp );
		route_restore( &tmp_queue );
		
		QUEUE_MOVE(&tmp_queue, &(tmp_elt->tracks));
		tmp = NULL;
		track_backup( &(tmp_elt->track_count), &tmp);
		QUEUE_MOVE(&(tmp_elt->tracks), tmp );
		xfree( tmp );
		track_restore( &tmp_queue );
		
		tmp_count = waypt_count();
		set_waypt_count( tmp_elt->waypt_ct );
		tmp_elt->waypt_ct = tmp_count;
	}
}

void
stackfilt_init(const char *args) {
	
	int invalid = 0;
	
	if ( nowarn ) {
		warnings_enabled = 0;
	}
	
	if ( opt_depth ) {
		swapdepth = atoi( opt_depth );
	}
	if ( opt_push ) {
		if ( opt_pop || opt_append || opt_discard || opt_replace || 
				opt_swap || opt_depth ) {
			invalid = 1;
		}
	}
	else if ( opt_pop ) {
		if ( opt_push || opt_copy || opt_swap || opt_depth ) {
			invalid = 1;
		}
		if ( !!opt_append + !!opt_discard + !!opt_replace > 1 ) {
			invalid = 1;
		}
	}
	else if ( opt_swap ) {
		if ( opt_push || opt_copy  || opt_pop || opt_append || 
				opt_discard || opt_replace ) {
			invalid = 1;
		}
	}
	else {
		invalid = 1;
	}
	
	if ( invalid ) {
        	fatal (MYNAME ": invalid combination of options\n");
	}	
	
}

void
stackfilt_deinit(void) {
	swapdepth = 0;
}

void
stackfilt_exit( void ) {
	struct stack_elt *tmp_elt = NULL;
 
	if ( warnings_enabled && stack ) {
		warning( MYNAME " Warning: leftover stack entries; "
			"check command line for mistakes\n" );
	}	
	while ( stack ) {
		waypt_flush( &(stack->waypts) );
		tmp_elt = stack;
		stack = stack->next;
		xfree(tmp_elt);
	}
}

filter_vecs_t stackfilt_vecs = {
	stackfilt_init,
	stackfilt_process,
	stackfilt_deinit,
	stackfilt_exit,
	stackfilt_args
};

#endif // FILTERS_ENABLED
