/*
 * client.c -- Valerie client demo
 * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
 * Author: Charles Yates <charles.yates@pandora.be>
 *
 * 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-1307, USA.
 */

/* System header files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Application header files */
#include "client.h"
#include "io.h"

/** Clip navigation enumeration.
*/

typedef enum
{
	absolute,
	relative
}
dv_demo_whence;

/** Function prototype for menu handling. 
*/

typedef valerie_error_code (*demo_function)( dv_demo );

/** The menu structure. 
*/

typedef struct
{
	char *description;
	struct menu_item
	{
		char *option;
		demo_function function;
	}
	array[ 50 ];
}
*dv_demo_menu, dv_demo_menu_t;

/** Forward reference to menu runner.
*/

extern valerie_error_code dv_demo_run_menu( dv_demo, dv_demo_menu );

/** Foward references. 
*/

extern valerie_error_code dv_demo_list_nodes( dv_demo );
extern valerie_error_code dv_demo_add_unit( dv_demo );
extern valerie_error_code dv_demo_select_unit( dv_demo );
extern valerie_error_code dv_demo_execute( dv_demo );
extern valerie_error_code dv_demo_load( dv_demo );
extern valerie_error_code dv_demo_transport( dv_demo );
static void *dv_demo_status_thread( void * );

/** Connected menu definition. 
*/

dv_demo_menu_t connected_menu =
{
	"Connected Menu",
	{
		{ "Add Unit", dv_demo_add_unit },
		{ "Select Unit", dv_demo_select_unit },
		{ "Command Shell", dv_demo_execute },
		{ NULL, NULL }
	}
};

/** Initialise the demo structure.
*/

dv_demo dv_demo_init( valerie_parser parser )
{
	dv_demo this = malloc( sizeof( dv_demo_t ) );
	if ( this != NULL )
	{
		int index = 0;
		memset( this, 0, sizeof( dv_demo_t ) );
		strcpy( this->last_directory, "/" );
		for ( index = 0; index < 4; index ++ )
		{
			this->queues[ index ].unit = index;
			this->queues[ index ].position = -1;
		}
		this->parser = parser;
	}
	return this;
}

/** Display a status record.
*/

void dv_demo_show_status( dv_demo demo, valerie_status status )
{
	if ( status->unit == demo->selected_unit && demo->showing )
	{
		char temp[ 1024 ] = "";

		sprintf( temp, "U%d ", demo->selected_unit );

		switch( status->status )
		{
			case unit_offline:
				strcat( temp, "offline   " );
				break;
			case unit_undefined:
				strcat( temp, "undefined " );
				break;
			case unit_not_loaded:
				strcat( temp, "unloaded  " );
				break;
			case unit_stopped:
				strcat( temp, "stopped   " );
				break;
			case unit_playing:
				strcat( temp, "playing   " );
				break;
			case unit_paused:
				strcat( temp, "paused    " );
				break;
			case unit_disconnected:
				strcat( temp, "disconnect" );
				break;
			default:
				strcat( temp, "unknown   " );
				break;
		}

		sprintf( temp + strlen( temp ), " %9d %9d %9d ", status->in, status->position, status->out );
		strcat( temp, status->clip );

		printf( "%-80.80s\r", temp );
		fflush( stdout );
	}
}

/** Determine action to carry out as dictated by the client unit queue.
*/

void dv_demo_queue_action( dv_demo demo, valerie_status status )
{
	dv_demo_queue queue = &demo->queues[ status->unit ];

	/* SPECIAL CASE STATUS NOTIFICATIONS TO IGNORE */

	/* When we've issued a LOAD on the previous notification, then ignore this one. */
	if ( queue->ignore )
	{
		queue->ignore --;
		return;
	}

	if ( queue->mode && status->status != unit_offline && queue->head != queue->tail )
	{
		if ( ( status->position >= status->out && status->speed > 0 ) || status->status == unit_not_loaded )
		{
			queue->position = ( queue->position + 1 ) % 50;
			if ( queue->position == queue->tail )
				queue->position = queue->head;
			valerie_unit_load( demo->dv_status, status->unit, queue->list[ queue->position ] );
			if ( status->status == unit_not_loaded )
				valerie_unit_play( demo->dv, queue->unit );
			queue->ignore = 1;
		}
		else if ( ( status->position <= status->in && status->speed < 0 ) || status->status == unit_not_loaded )
		{
			if ( queue->position == -1 )
				queue->position = queue->head;
			valerie_unit_load( demo->dv_status, status->unit, queue->list[ queue->position ] );
			if ( status->status == unit_not_loaded )
				valerie_unit_play( demo->dv, queue->unit );
			queue->position = ( queue->position - 1 ) % 50;
			queue->ignore = 1;
		}
	}
}

/** Status thread.
*/

static void *dv_demo_status_thread( void *arg )
{
	dv_demo demo = arg;
	valerie_status_t status;
	valerie_notifier notifier = valerie_get_notifier( demo->dv_status );

	while ( !demo->terminated )
	{
		if ( valerie_notifier_wait( notifier, &status ) != -1 )
		{
			dv_demo_queue_action( demo, &status );
			dv_demo_show_status( demo, &status );
			if ( status.status == unit_disconnected )
				demo->disconnected = 1;
		}
	}

	return NULL;
}

/** Turn on/off status display.
*/

void dv_demo_change_status( dv_demo demo, int flag )
{
	if ( demo->disconnected && flag )
	{
		valerie_error_code error = valerie_connect( demo->dv );
		if ( error == valerie_ok )
			demo->disconnected = 0;
		else
			beep();
	}

	if ( flag )
	{
		valerie_status_t status;
		valerie_notifier notifier = valerie_get_notifier( demo->dv );
		valerie_notifier_get( notifier, &status, demo->selected_unit );
		demo->showing = 1;
		dv_demo_show_status( demo, &status );
	}
	else
	{
		demo->showing = 0;
		printf( "%-80.80s\r", " " );
		fflush( stdout );
	}
}

/** Add a unit.
*/

valerie_error_code dv_demo_add_unit( dv_demo demo )
{
	valerie_error_code error = valerie_ok;
	valerie_nodes nodes = valerie_nodes_init( demo->dv );
	valerie_units units = valerie_units_init( demo->dv );

	if ( valerie_nodes_count( nodes ) != -1 && valerie_units_count( units ) != -1 )
	{
		char pressed;
		valerie_node_entry_t node;
		valerie_unit_entry_t unit;
		int node_index = 0;
		int unit_index = 0;

		printf( "Select a Node\n\n" );

		for ( node_index = 0; node_index < valerie_nodes_count( nodes ); node_index ++ )
		{
			valerie_nodes_get( nodes, node_index, &node );
			printf( "%d: %s - %s ", node_index + 1, node.guid, node.name );
			for ( unit_index = 0; unit_index < valerie_units_count( units ); unit_index ++ )
			{
				valerie_units_get( units, unit_index, &unit );
				if ( !strcmp( unit.guid, node.guid ) )
					printf( "[U%d] ", unit.unit );
			}
			printf( "\n" );
		}

		printf( "0. Exit\n\n" );

		printf( "Node: " );

		while ( ( pressed = get_keypress( ) ) != '0' )
		{
			node_index = pressed - '1';
			if ( node_index >= 0 && node_index < valerie_nodes_count( nodes ) )
			{
				int unit;
				printf( "%c\n\n", pressed );
				valerie_nodes_get( nodes, node_index, &node );
				if ( valerie_unit_add( demo->dv, node.guid, &unit ) == valerie_ok )
				{
					printf( "Unit added as U%d\n", unit );
					demo->selected_unit = unit;
				}
				else
				{
					int index = 0;
					valerie_response response = valerie_get_last_response( demo->dv );
					printf( "Failed to add unit:\n\n" );
					for( index = 1; index < valerie_response_count( response ) - 1; index ++ )
						printf( "%s\n", valerie_response_get_line( response, index ) );
				}
				printf( "\n" );
				wait_for_any_key( NULL );
				break;
			}
			else
			{
				beep( );
			}
		}
	}
	else
	{
		printf( "Invalid response from the server.\n\n" );
		wait_for_any_key( NULL );
	}

	valerie_nodes_close( nodes );
	valerie_units_close( units );

	return error;
}

/** Select a unit.
*/

valerie_error_code dv_demo_select_unit( dv_demo demo )
{
	int terminated = 0;
	int refresh = 1;

	while ( !terminated )
	{
		valerie_units units = valerie_units_init( demo->dv );

		if ( valerie_units_count( units ) > 0 )
		{
			valerie_unit_entry_t unit;
			int index = 0;
			char key = '\0';

			if ( refresh )
			{
				printf( "Select a Unit\n\n" );

				for ( index = 0; index < valerie_units_count( units ); index ++ )
				{
					valerie_units_get( units, index, &unit );
					printf( "%d: U%d - %s [%s]\n", index + 1, 
												   unit.unit, 
												   unit.guid, 
												   unit.online ? "online" : "offline" );
				}
				printf( "0: Exit\n\n" );

				printf( "Unit [%d]: ", demo->selected_unit + 1 );
				refresh = 0;
			}

			key = get_keypress( );

			if ( key == '\r' )
				key = demo->selected_unit + '1';

			if ( key != '0' )
			{
				if ( key >= '1' && key < '1' + valerie_units_count( units ) )
				{
					demo->selected_unit = key - '1';
					printf( "%c\n\n", key );
					dv_demo_load( demo );
					refresh = 1;
				}
				else
				{
					beep( );
				}					
			}
			else
			{
				printf( "0\n\n" );
				terminated = 1;
			}
		}
		else if ( valerie_units_count( units ) == 0 )
		{
			printf( "No units added - add a unit first\n\n" );
			dv_demo_add_unit( demo );
		}
		else
		{
			printf( "Unable to obtain Unit List.\n" );
			terminated = 1;
		}

		valerie_units_close( units );
	}

	return valerie_ok;
}

/** Execute an arbitrary command.
*/

valerie_error_code dv_demo_execute( dv_demo demo )
{
	valerie_error_code error = valerie_ok;
	char command[ 10240 ];
	int terminated = 0;

	printf( "Miracle Shell\n" );
	printf( "Enter an empty command to exit.\n\n" );

	while ( !terminated )
	{
		terminated = 1;
		printf( "Command> " );

		if ( chomp( io_get_string( command, 10240, "" ) ) != NULL )
		{
			if ( strcmp( command, "" ) )
			{
				int index = 0;
				valerie_response response = NULL;
				error = valerie_execute( demo->dv, 10240, command );
				printf( "\n" );
				response = valerie_get_last_response( demo->dv );
				for ( index = 0; index < valerie_response_count( response ); index ++ )
				{
					char *line = valerie_response_get_line( response, index );
					printf( "%4d: %s\n", index, line );
				}
				printf( "\n" );
				terminated = 0;
			}
		}
	}

	printf( "\n" );

	return error;
}

/** Add a file to the queue.
*/

valerie_error_code dv_demo_queue_add( dv_demo demo, dv_demo_queue queue, char *file )
{
	valerie_status_t status;
	valerie_notifier notifier = valerie_get_notifier( demo->dv );

	if ( ( queue->tail + 1 ) % 50 == queue->head )
		queue->head = ( queue->head + 1 ) % 50;
	strcpy( queue->list[ queue->tail ], file );
	queue->tail = ( queue->tail + 1 ) % 50;

	valerie_notifier_get( notifier, &status, queue->unit );
	valerie_notifier_put( notifier, &status );

	return valerie_ok;
}

/** Basic queue maintenance and status reports.
*/

valerie_error_code dv_demo_queue_maintenance( dv_demo demo, dv_demo_queue queue )
{
	printf( "Queue Maintenance for Unit %d\n\n", queue->unit );

	if ( !queue->mode )
	{
		char ch;
		printf( "Activate queueing? [Y] " );
		ch = get_keypress( );
		if ( ch == 'y' || ch == 'Y' || ch == '\r' )
			queue->mode = 1;
		printf( "\n\n" );
	}

	if ( queue->mode )
	{
		int terminated = 0;
		int last_position = -2;

		term_init( );

		while ( !terminated )
		{
			int first = ( queue->position + 1 ) % 50;
			int index = first;

			if ( first == queue->tail )
				index = first = queue->head;

			if ( queue->head == queue->tail )
			{
				if ( last_position == -2 )
				{
					printf( "Queue is empty\n" );
					printf( "\n" );
					printf( "0 = exit, t = turn off queueing\n\n" );
					last_position = -1;
				}
			}
			else if ( last_position != queue->position )
			{
				printf( "Order of play\n\n" );

				do 
				{
					printf( "%c%02d: %s\n", index == first ? '*' : ' ', index, queue->list[ index ] + 1 );
					index = ( index + 1 ) % 50;
					if ( index == queue->tail )
						index = queue->head;
				}
				while( index != first );
	
				printf( "\n" );
				printf( "0 = exit, t = turn off queueing, c = clear queue\n\n" );
				last_position = queue->position;
			}

			dv_demo_change_status( demo, 1 );
			
			switch( term_read( ) )
			{
				case -1:
					break;
				case '0':
					terminated = 1;
					break;
				case 't':
					terminated = 1;
					queue->mode = 0;
					break;
				case 'c':
					queue->head = queue->tail = 0;
					queue->position = -1;
					last_position = -2;
					break;
			}

			dv_demo_change_status( demo, 0 );
		}

		term_exit( );
	}

	return valerie_ok;
}

/** Load a file to the selected unit. Horrible function - sorry :-/. Not a good
	demo....
*/

valerie_error_code dv_demo_load( dv_demo demo )
{
	valerie_error_code error = valerie_ok;
	int terminated = 0;
	int refresh = 1;
	int start = 0;

	strcpy( demo->current_directory, demo->last_directory );

	term_init( );

	while ( !terminated )
	{
		valerie_dir dir = valerie_dir_init( demo->dv, demo->current_directory );

		if ( valerie_dir_count( dir ) == -1 )
		{
			printf( "Invalid directory - retrying %s\n", demo->last_directory );
			valerie_dir_close( dir );
			dir = valerie_dir_init( demo->dv, demo->last_directory );
			if ( valerie_dir_count( dir ) == -1 )
			{
				printf( "Invalid directory - going back to /\n" );
				valerie_dir_close( dir );
				dir = valerie_dir_init( demo->dv, "/" );
				strcpy( demo->current_directory, "/" );
			}
			else
			{
				strcpy( demo->current_directory, demo->last_directory );
			}
		}

		terminated = valerie_dir_count( dir ) == -1;

		if ( !terminated )
		{
			int index = 0;
			int selected = 0;
			int max = 9;
			int end = 0;

			end = valerie_dir_count( dir );

			strcpy( demo->last_directory, demo->current_directory );

			while ( !selected && !terminated )
			{
				valerie_dir_entry_t entry;
				int pressed;

				if ( refresh )
				{
					char *action = "Load & Play";
					if ( demo->queues[ demo->selected_unit ].mode )
						action = "Queue";
					printf( "%s from %s\n\n", action, demo->current_directory );
					if ( strcmp( demo->current_directory, "/" ) )
						printf( "-: Parent directory\n" );
					for ( index = start; index < end && ( index - start ) < max; index ++ )
					{
						valerie_dir_get( dir, index, &entry );
						printf( "%d: %s\n", index - start + 1, entry.name );
					}
					while ( ( index ++ % 9 ) != 0 )
						printf( "\n" );
					printf( "\n" );
					if ( start + max < end )
						printf( "space = more files" );
					else if ( end > max )
						printf( "space = return to start of list" );
					if ( start > 0 )
						printf( ", b = previous files" );
					printf( "\n" );
					printf( "0 = abort, t = transport, x = execute command, q = queue maintenance\n\n" );
					refresh = 0;
				}

				dv_demo_change_status( demo, 1 );

				pressed = term_read( );
				switch( pressed )
				{
					case -1:
						break;
					case '0':
						terminated = 1;
						break;
					case 'b':
						refresh = start - max >= 0;
						if ( refresh )
							start = start - max;
						break;
					case ' ':
						refresh = start + max < end;
						if ( refresh )
						{
							start = start + max;
						}
						else if ( end > max )
						{
							start = 0;
							refresh = 1;
						}
						break;
					case '-':
						if ( strcmp( demo->current_directory, "/" ) )
						{
							selected = 1;
							( *strrchr( demo->current_directory, '/' ) ) = '\0';
							( *( strrchr( demo->current_directory, '/' ) + 1 ) ) = '\0';
						}
						break;
					case 't':
						dv_demo_change_status( demo, 0 );
						term_exit( );
						dv_demo_transport( demo );
						term_init( );
						selected = 1;
						break;
					case 'x':
						dv_demo_change_status( demo, 0 );
						term_exit( );
						dv_demo_execute( demo );
						term_init( );
						selected = 1;
						break;
					case 'q':
						dv_demo_change_status( demo, 0 );
						term_exit( );
						dv_demo_queue_maintenance( demo, &demo->queues[ demo->selected_unit ] );
						term_init( );
						selected = 1;
						break;
					default:
						if ( pressed >= '1' && pressed <= '9' )
						{
							if ( ( start + pressed - '1' ) < end )
							{
								valerie_dir_get( dir, start + pressed - '1', &entry );
								selected = 1;
								strcat( demo->current_directory, entry.name );
							}
						}
						break;
				}

				dv_demo_change_status( demo, 0 );
			}

			valerie_dir_close( dir );
		}

		if ( !terminated && demo->current_directory[ strlen( demo->current_directory ) - 1 ] != '/' )
		{
			if ( demo->queues[ demo->selected_unit ].mode == 0 )
			{
				error = valerie_unit_load( demo->dv, demo->selected_unit, demo->current_directory );
				valerie_unit_play( demo->dv, demo->selected_unit );
			}
			else
			{
				dv_demo_queue_add( demo, &demo->queues[ demo->selected_unit ], demo->current_directory );
				printf( "File %s added to queue.\n", demo->current_directory );
			}
			strcpy( demo->current_directory, demo->last_directory );
			refresh = 0;
		}
		else
		{
			refresh = 1;
			start = 0;
		}
	}

	term_exit( );

	return error;
}

/** Set the in point of the clip on the select unit.
*/

valerie_error_code dv_demo_set_in( dv_demo demo )
{
	int position = 0;
	valerie_status_t status;
	valerie_notifier notifier = valerie_parser_get_notifier( demo->parser );
	valerie_notifier_get( notifier, &status, demo->selected_unit );
	position = status.position;
	return valerie_unit_set_in( demo->dv, demo->selected_unit, position );
}

/** Set the out point of the clip on the selected unit.
*/

valerie_error_code dv_demo_set_out( dv_demo demo )
{
	int position = 0;
	valerie_status_t status;
	valerie_notifier notifier = valerie_parser_get_notifier( demo->parser );
	valerie_notifier_get( notifier, &status, demo->selected_unit );
	position = status.position;
	return valerie_unit_set_out( demo->dv, demo->selected_unit, position );
}

/** Clear the in and out points on the selected unit.
*/

valerie_error_code dv_demo_clear_in_out( dv_demo demo )
{
	return valerie_unit_clear_in_out( demo->dv, demo->selected_unit );
}

/** Goto a user specified frame on the selected unit.
*/

valerie_error_code dv_demo_goto( dv_demo demo )
{
	int frame = 0;
	printf( "Frame: " );
	if ( get_int( &frame, 0 ) )
		return valerie_unit_goto( demo->dv, demo->selected_unit, frame );
	return valerie_ok;
}

/** Manipulate playback on the selected unit.
*/

valerie_error_code dv_demo_transport( dv_demo demo )
{
	valerie_error_code error = valerie_ok;
	int refresh = 1;
	int terminated = 0;
	valerie_status_t status;
	valerie_notifier notifier = valerie_get_notifier( demo->dv );

	while ( !terminated )
	{
		if ( refresh )
		{
			printf( "  +----+ +------+ +----+ +------+ +---+ +-----+ +------+ +-----+ +---+  \n" );
			printf( "  |1=-5| |2=-2.5| |3=-1| |4=-0.5| |5=1| |6=0.5| |7=1.25| |8=2.5| |9=5|  \n" );
			printf( "  +----+ +------+ +----+ +------+ +---+ +-----+ +------+ +-----+ +---+  \n" );
			printf( "\n" );
			printf( "+----------------------------------------------------------------------+\n" );
			printf( "|              0 = quit, x = eXecute, 'space' = pause                  |\n" );
			printf( "|              g = goto a frame, q = queue maintenance                 |\n" );
			printf( "|     h = step -1, j = end of clip, k = start of clip, l = step 1      |\n" );
			printf( "|        eof handling: p = pause, r = repeat, t = terminate            |\n" );
			printf( "|       i = set in point, o = set out point, c = clear in/out          |\n" );
			printf( "|       u = use point settings, d = don't use point settings           |\n" );
			printf( "+----------------------------------------------------------------------+\n" );
			printf( "\n" );
			term_init( );
			refresh = 0;
		}

		dv_demo_change_status( demo, 1 );

		switch( term_read( ) )
		{
			case '0':
				terminated = 1;
				break;
			case -1:
				break;
			case ' ':
				error = valerie_unit_pause( demo->dv, demo->selected_unit );
				break;
			case '1':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -5000 );
				break;
			case '2':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -2500 );
				break;
			case '3':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -1000 );
				break;
			case '4':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -500 );
				break;
			case '5':
				error = valerie_unit_play( demo->dv, demo->selected_unit );
				break;
			case '6':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 500 );
				break;
			case '7':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 1250 );
				break;
			case '8':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 2500 );
				break;
			case '9':
				error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 5000 );
				break;
			case 's':
				error = valerie_unit_goto( demo->dv, demo->selected_unit, 0 );
				break;
			case 'h':
				error = valerie_unit_step( demo->dv, demo->selected_unit, -1 );
				break;
			case 'j':
				valerie_notifier_get( notifier, &status, demo->selected_unit );
				error = valerie_unit_goto( demo->dv, demo->selected_unit, status.tail_out );
				break;
			case 'k':
				valerie_notifier_get( notifier, &status, demo->selected_unit );
				error = valerie_unit_goto( demo->dv, demo->selected_unit, status.in );
				break;
			case 'l':
				error = valerie_unit_step( demo->dv, demo->selected_unit, 1 );
				break;
			case 'p':
				error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "pause" );
				break;
			case 'r':
				error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "loop" );
				break;
			case 't':
				error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "stop" );
				break;
			case 'i':
				error = dv_demo_set_in( demo );
				break;
			case 'o':
				error = dv_demo_set_out( demo );
				break;
			case 'g':
				dv_demo_change_status( demo, 0 );
				term_exit( );
				error = dv_demo_goto( demo );
				refresh = 1;
				break;
			case 'c':
				error = dv_demo_clear_in_out( demo );
				break;
			case 'u':
				error = valerie_unit_set( demo->dv, demo->selected_unit, "points", "use" );
				break;
			case 'd':
				error = valerie_unit_set( demo->dv, demo->selected_unit, "points", "ignore" );
				break;
			case 'x':
				dv_demo_change_status( demo, 0 );
				term_exit( );
				dv_demo_execute( demo );
				refresh = 1;
				break;
			case 'q':
				dv_demo_change_status( demo, 0 );
				term_exit( );
				dv_demo_queue_maintenance( demo, &demo->queues[ demo->selected_unit ] );
				refresh = 1;
				break;
		}

		dv_demo_change_status( demo, 0 );
	}

	term_exit( );

	return error;
}

/** Recursive menu execution.
*/

valerie_error_code dv_demo_run_menu( dv_demo demo, dv_demo_menu menu )
{
	char *items = "123456789abcdefghijklmnopqrstuvwxyz";
	int refresh_menu = 1;
	int terminated = 0;
	int item_count = 0;
	int item_selected = 0;
	int index = 0;
	char key;

	while( !terminated )
	{

		if ( refresh_menu )
		{
			printf( "%s\n\n", menu->description );
			for ( index = 0; menu->array[ index ].option != NULL; index ++ )
				printf( "%c: %s\n", items[ index ], menu->array[ index ].option );
			printf( "0: Exit\n\n" );
			printf( "Select Option: " );
			refresh_menu = 0;
			item_count = index;
		}

		key = get_keypress( );

		if ( demo->disconnected && key != '0' )
		{
			valerie_error_code error = valerie_connect( demo->dv );
			if ( error == valerie_ok )
				demo->disconnected = 0;
			else
				beep();
		}

		if ( !demo->disconnected || key == '0' )
		{
			item_selected = strchr( items, key ) - items;

			if ( key == '0' )
			{
				printf( "%c\n\n", key );
				terminated = 1;
			}
			else if ( item_selected >= 0 && item_selected < item_count )
			{
				printf( "%c\n\n", key );
				menu->array[ item_selected ].function( demo );
				refresh_menu = 1;
			}
			else
			{
				beep( );
			}
		}
	}

	return valerie_ok;
}

/** Entry point for main menu.
*/

void dv_demo_run( dv_demo this )
{
	this->dv = valerie_init( this->parser );
	this->dv_status = valerie_init( this->parser );
	if ( valerie_connect( this->dv ) == valerie_ok )
	{
		pthread_create( &this->thread, NULL, dv_demo_status_thread, this );
		dv_demo_run_menu( this, &connected_menu );
		this->terminated = 1;
		pthread_join( this->thread, NULL );
		this->terminated = 0;
	}
	else
	{
		printf( "Unable to connect." );
		wait_for_any_key( "" );
	}

	valerie_close( this->dv_status );
	valerie_close( this->dv );

	printf( "Demo Exit.\n" );
}

/** Close the demo structure.
*/

void dv_demo_close( dv_demo demo )
{
	free( demo );
}
