/*
 * ts2k_menu.c	(C) Copyright 2002 by Dale E. Edmons.  All rights reserved.
 */

/*
 * License:	GNU
 */

/*
 * status:	Never been compiled!
 */

/*
 * Functions to initialize, read, set, and list menus
 * for the TS-2000.  These functions will be added to
 * src/rig.c as rig_*menu_*() as time permits.
 *
 * This is likely a can of worms nobody has wanted to
 * deal with--until now.  The ts2k is especially a
 * pain as you'll soon seen (but cool nonetheless.)
 */

#include <rig.h>
#include "ts2k.h"
#include "ts2k_menu.h"
#include <ctype.h>

/*
 * Set the menu number(s) and terminate with ";" (not ';')
 */
int ts2k_set_menu_no(RIG *rig, ts2k_menu_t *menu, int main, int sub)
{
	int i;

	menu->menu_no[0] = main;
	menu->menu_no[1] = sub;
	menu->menu_no[2] = menu->menu_no[3] = 0;

	i = sprintf( &menu->cmd[0], "ex%03u%02u%01u%01u;", main, sub, 0, 0);

	if(i != 10)
		return -RIG_EINVAL;

	return RIG_OK;
}

/*
 * Get the menu number(s) from cmd[]
 */
int ts2k_get_menu_no(RIG *rig, ts2k_menu_t *menu, int *main, int *sub)
{
	char tmp[30];
	int m, s;

	if(menu==NULL)
		return -RIG_EINVAL;

	m = int_n(tmp, &menu->cmd[2], 3);
	s = int_n(tmp, &menu->cmd[7], 2);

	menu->menu_no[0] = m; 
	menu->menu_no[1] = s; 
	menu->menu_no[2] = menu->menu_no[3] = 0;

	if(main != NULL)
		*main = m;
	if(sub != NULL)
		*sub = s;

	return RIG_OK;
}

/*
 * When called by ts2k_pm_init we read all of
 * the rig's current menu options and set them
 * in the current menu array.  We don't do the
 * memory allocation so you can init as many
 * as you want.  The reason for this is huge.
 * First, after full reset the rig can access
 * menuA or menuB and different values retained.
 * Second, by sending "pm...;" we can select
 * Programmable Memory 0-5, with 0 being the
 * the default.  Amazingly, each PM seems to
 * have its own menuA and menuB.  Thus, all
 * told, we have up to twelve distinct menus
 * each with (potentially) unique values.  We
 * do the allocation in the PM routines and
 * leave this one much simpler and just call
 * it up to twelve times with uniq ts2k_menu_t
 * pointers.  Third, the user may wish to use
 * this to obtain a private copy for local use
 * and only has to call this routine to make
 * sure its current.  (A ts2k_pm_t should be
 * allocated as well!)
 */
int ts2k_menu_init(RIG *rig, ts2k_menu_t *menu[])
{
	int retval, i;
	ts2k_menu_t *m, *mref;

	if(menu == NULL || menu == ts2k_menus) {
		rig_debug(rig, __FUNCTION__": invalid menu pointer\n");
		return -RIG_EINVAL;
	}

	// One set of defaults has been globally defined (mref)
	for(i=0, i<(sizeof(menu)/sizeof(ts2k_menu_t)); i++) {
		m = menu[i];
		mref = ts2k_menus[i];
		retval = ts2k_set_menu_no(rig, m, mref->menu[0], mref->menu[1]);
		if(retval != RIG_OK)
			return retval;
		retval = ts2k_get_menu(rig, m);
		if(retval != RIG_OK)
			return retval;
		// FIXME: set some debug traces here?
	}

	return RIG_OK;
}

/*
 * read the rig and get the parameters for menu[n].
 * convert them to a ts2k_menu_t, and eventually to
 * a rig_menu_t.  I do a lot of checking to ensure
 * nothing breaks (hi!).
 */
int ts2k_get_menu(RIG *rig, ts2k_menu_t *menu, )
{
	int retval, i, j, k, acklen;
	char ack[30];

	if(menu == NULL)
		return -RIG_EINVAL;
	else if( (toupper(menu->cmd[0]) != 'E')
		|| (toupper(menu->cmd[1]) != 'X') )
		return -RIG_EINVAL;
	else if(menu->cmd[9] != ';')
		retval = -RIG_EINVAL;

	retval = ts2k_transaction(rig, menu->cmd, 10, ack, &acklen);
	if(retval != RIG_OK)
		return retval;

	strncpy(menu->cmd, ack, 30);
	retval = ts2k_menu_parse(rig, menu);
	if(retval != RIG_OK)
		return retval;

	return retval;
}

/*
 * Here, I expect everything to be setup for me.
 * All we do is determe the value and param_txt
 * for the menu item we are given.
 *
 *	status: real ugly!
 */
int ts2k_menu_parse(RIG *rig, ts2k_menu_t *menu)
{
	ts2k_menu_t *mref, *m;
	char *vptr;
	int on_off[] = { 01, 03, 04, 05, 07, 09, 17, 18, 23, 25, 26,
		 27, 30, 34, 35, 36, 37, 43, 44, 52, 53, 63, 54, 55,
		-1},
	    zero2nine = {

	m = menu;	// to lazy to spell menu-> all the time

	if(m->cmd[0] == '\0' || m->cmd[1] == '\0')
		return -RIG_EINVAL;	// Caller has blown it!

	// find matching refence menu
	i=0;
	do {
		mref = ts2k_menus[i];
		if(mref == NULL)	// Either Caller or I blew it!
			return -RIG_EINTERNAL;
		if(mref->menu_no[i] == NULL)
			return -RIG_EINVAL;	// It has to match one!
		if(menu == ts2k_menus[i])	// Nobody changes our REF!
			return -RIG_EINTERNAL;
			
		if(mref->menu[0] == m->menu[0])
		   && mref->menu[1] == m->menu[1]
		   && mref->menu[2] == m->menu[2]
		   /*&& mref->menu[3] == m->menu[3]*/) {
			break;
		}
	} while (++i)

	/* Match the main menu and only look for sub-menus later */

	// check for menus that are simple on/off first
	// FIXME: this isn't fast, it just eliminates alot of cases!
	for(i=0; on_off == -1; i++) {
		if(m->menu[0] == m->menu[0]) {
			m->val = m->cmd[9] - '0';
			m->param_txt = (m->val == 1)? m_on : m_off;
			return RIG_OK;
		}
	}

	// Use mref to do all we can
	retval = -RIG_EINTERNAL;
	for(i=0; mref->param_txt != NULL; i++) {
		if(retval == RIG_OK) {
			return retval;
		}
		switch( mref->param_txt[i] ) {
		case m_tbd:	// menu item under development!
			m->param_txt = m_tbd;
			vptr = malloc( 17 );
			if(vptr == NULL)
				return -RIG_NOMEM;

			for(j=0; vptr[i] != ';' && i<17; i++) {
				vptr[i] == m->cmd[i];
			}
			vptr[i] = '\0';
			return -RIG_NIMPL;

		case m_off:
			if(m->cmd[9] == '0') {
				m->param_txt = mref->param_txt[i];
				m->val = 0;
				retval = RIG_OK;
			}
			break;
		case m_num:
		default:
			return -RIG_NIMPL;
		}
	}

	switch( m->menu[0] ) {

	case 00:
	case 00:
	case 00:
	case 00:
	case 00:
	case 00:
	case 00:
	case 00:

	default:
		return -RIG_EINTERNAL;	// I'm requiring 100% match 
	}

	return RIG_OK;
}

// End of ts2k_menu.c
