/******************************************************************************

  Test BLU link controls using HPI functions

  Usage: asihpibl --help

Copyright (C) 1997-2017 AudioScience, Inc. All rights reserved.

This software is provided 'as-is', without any express or implied warranty.
In no event will AudioScience Inc. be held liable for any damages arising
from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This copyright notice and list of conditions may not be altered or removed
   from any source distribution.

AudioScience, Inc. <support@audioscience.com>

( This license is GPL compatible see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses )

******************************************************************************/

#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <getopt.h>
#ifdef __APPLE__
#include <stdio.h>
#include <hpi/hpi.h>
#include <hpi/hpidebug.h>
#else
#include <stdio.h>
#include <hpi.h>
#include <hpidebug.h>
#endif

#if 0
#include <hpi_entity_print.c>
#endif

#define verbose_printf if (verbose) printf

typedef struct {
	uint16_t wControlType;	// HPI_CONTROL_METER, _VOLUME etc
	uint16_t wSrcNodeType;
	uint16_t wSrcNodeIndex;
	uint16_t wDstNodeType;
	uint16_t wDstNodeIndex;
} hpi_control_t;

// local protos
static void HandleError(hpi_err_t err);

// global

/* Option variables */
static int do_show = 0;
static int onramp = -1;
static int offramp = -1;
static int priority = -1;
static int channel_value = 0;
static int wAdapterIndex = 0;
static int verbose = 0;

static struct option long_options[] = {
	{"adapter", required_argument, 0, 'a'},
	{"onramp", required_argument, 0, 'n'},
	{"offramp", required_argument, 0, 'f'},
	{"channel", required_argument, 0, 'c'},
	{"priority", required_argument, 0, 'p' },
	{"show", no_argument, 0, 's' },
	{"verbose", no_argument, 0, 'v'},
	{"help", no_argument, 0, 'h'},
	{0, 0, 0, 0}
};

static const char *short_options = "a:n:f:c:p:svh?";

static const char *option_help[] = {
	"<adapter number> to test.",
	"<decimal value> set onramp",
	"<decimal value> set offramp",
	"<decimal value> channel value to set",
	"<decimal value> set mastership priority",
	"show state of all BLU link settings",
	"turn on verbose output",
	"Show this text."
};

static void help(void)
{
	int i = 0;
	printf("\nASIHPIBL - BLU Link channel access");
	printf("\nUsage - asihpibl [options]\n");
	while (long_options[i].name != 0) {
		printf("--%s -%c %s\n",
			long_options[i].name,
			(char)(long_options[i].val), option_help[i]);
		i++;
	}
	exit(0);
}

static void parse_options(
	int argc,
	char *argv[]
)
{
	int c;
    /*********** Parse the command line options ***************/
	while (1) {
		// int this_option_optind = optind ? optind : 1;
		int option_index = 0;

		c = getopt_long(argc, argv, short_options,
			long_options, &option_index);
		if (c == -1)
			break;

		switch (c) {
		case 0:
			printf("option %s", long_options[option_index].name);
			if (optarg)
				printf(" with arg %s", optarg);
			printf("\n");
			break;
		case 'a':
			wAdapterIndex = atoi(optarg);
			break;
		case 's':
			do_show = 1;
			break;
		case 'n':
			onramp = atoi(optarg);
			break;
		case 'f':
			offramp = atoi(optarg);
			break;
		case 'c':
			channel_value = atoi(optarg);
			break;
		case 'p':
			priority = atoi(optarg);
			break;
		case 'v':
			verbose = 1;
			break;
		case '?':
		case 'h':
			help();
			break;

		default:
			printf("?? getopt returned character code 0%o ??\n",
				c);
		}
	}

	if (optind < argc) {
		// printf ("non-option ARGV-elements: ");
	}

}

/*******************************************************************/
static hpi_err_t set_onramp(hpi_handle_t hMixer)
{
	struct hpi_control_t asihpi_control;
	hpi_handle_t block;
	hpi_handle_t param;
	hpi_err_t err = 0;

	/* set source and destination node */
	asihpi_control.wSrcNodeType = HPI_SOURCENODE_NONE;
	asihpi_control.wSrcNodeIndex = 0;
	asihpi_control.wDstNodeType = HPI_DESTNODE_BLULINK;
	asihpi_control.wDstNodeIndex = onramp;

	err = HPI_Object_BlockHandle(hMixer,
			asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
			asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
			"BLU link Channel",
			&block );

	if ( !err ) {
		err = HPI_Object_ParameterHandle( hMixer, block, "Channel", &param);
	}

	if ( !err ) {
		err = HPI_Object_SetValue(param, entity_type_int, 1,
			    &channel_value, sizeof(channel_value) );
	}

	if ( !err ) {
		printf("Set onramp %d to %d ", onramp, channel_value);
		if ( channel_value ) {
			printf("(channel index %d).\n",channel_value - 1);
		}else{
			printf("(off).\n");
		}
	}else{
		printf("Error %d setting onramp.\n",err);
	}
	return err;
}

static hpi_err_t set_offramp(hpi_handle_t hMixer)
{
	struct hpi_control_t asihpi_control;
	hpi_handle_t block;
	hpi_handle_t param;
	hpi_err_t err = 0;

	/* set source and destination node */
	asihpi_control.wSrcNodeType = HPI_SOURCENODE_BLULINK;
	asihpi_control.wSrcNodeIndex = offramp;
	asihpi_control.wDstNodeType = HPI_DESTNODE_NONE;
	asihpi_control.wDstNodeIndex = 0;

	err = HPI_Object_BlockHandle(hMixer,
			asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
			asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
			"BLU link Channel",
			&block );

	if ( !err ) {
		err = HPI_Object_ParameterHandle( hMixer, block, "Channel", &param);
	}

	if ( !err ) {
		err = HPI_Object_SetValue(param, entity_type_int, 1,
			    &channel_value, sizeof(channel_value) );
	}

	if ( !err ) {
		printf("Set offramp %d to %d ", offramp, channel_value);
		if ( channel_value ) {
			printf("(channel index %d).\n",channel_value - 1);
		}else{
			printf("(off).\n");
		}
	}else{
		printf("Error %d setting offramp.\n",err);
	}
	return err;
}

static hpi_err_t set_priority(hpi_handle_t hMixer)
{
	struct hpi_control_t asihpi_control;
	hpi_handle_t block;
	hpi_handle_t param;
	hpi_err_t err = 0;
	char szError[256];

	/* set source and destination node */
	asihpi_control.wSrcNodeType = HPI_SOURCENODE_ADAPTER;
	asihpi_control.wSrcNodeIndex = 0;
	asihpi_control.wDstNodeType = HPI_DESTNODE_NONE;
	asihpi_control.wDstNodeIndex = 0;

	err = HPI_Object_BlockHandle(hMixer,
		asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
		asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
		"BLU link",
		&block);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s looking up [BLU link]\n", szError);
		return err;
	}

	err = HPI_Object_ParameterHandle(hMixer, block, "Mastership Priority", &param);
	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s looking up [BLU link][Mastership Priority]\n", szError);
		return err;
	}

	err = HPI_Object_SetValue(param, entity_type_int, 1,
		&priority, sizeof(priority));

	if (!err) {
		printf("Success, Mastership Priority set to %d\n", priority);
	} else {
		printf("Error %d setting Mastership Priority\n", err);
	}
	return err;
}

static int read_integer_parameter(
	hpi_handle_t hMixer,
	hpi_handle_t hBlock,
	char *ParamName)
{
	int iData;
	hpi_handle_t hParam;
	hpi_err_t err = 0;
	size_t value_size = 0;
	size_t value_items = 0;
	char szError[256];


	err = HPI_Object_ParameterHandle(hMixer, hBlock, ParamName, &hParam);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s looking up %s parameter\n",
			szError, ParamName);
		return -1;
	}

	err = HPI_Object_GetInfo(hParam,
		entity_type_int, entity_role_value,
		NULL, &value_size, &value_items);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s calling HPI_Object_GetInfo(%s)\n", szError, ParamName);
		return -1;
	}

	err = HPI_Object_GetValue(hParam, entity_type_int, value_items,
		&iData, sizeof(iData));

	if (err)
	{
		HPI_GetErrorText(err, szError);
		printf("Error %s reading [%s] parameter\n", szError, ParamName);
		return -1;
	}

	printf("%s: %d\n", ParamName, iData);
	return 0;
}

static int read_string_parameter(
	hpi_handle_t hMixer,
	hpi_handle_t hBlock,
	char *ParamName)
{
	char szData[64];
	hpi_handle_t hParam;
	hpi_err_t err = 0;
	size_t value_size = 0;
	size_t value_items = 0;
	char szError[256];


	err = HPI_Object_ParameterHandle(hMixer, hBlock, ParamName, &hParam);
	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s looking up %s parameter\n",
			szError, ParamName);
		return -1;
	}

#if 0
	{
		struct hpi_entity *info_entity;
		/* Sart - test code to explore Info entity */
		err = HPI_Object_GetInfoEntity(hParam, &info_entity);
		if (err) {
			HPI_GetErrorText(err, szError);
			printf("Error %s using HPI_Object_GetInfoEntity() with %s\n",
				szError, ParamName);
			return -1;
		}
		hpi_entity_print(info_entity, 0, 0, NULL);
		HPI_Entity_Free(info_entity);
		printf("\n");
	}
#endif

	err = HPI_Object_GetInfo(hParam,
		entity_type_cstring, entity_role_value,
		NULL, &value_size, &value_items);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s calling HPI_Object_GetInfo(%s)\n", szError, ParamName);
		return -1;
	}

	//printf("\t\t\t dgb param \"%s\" value_size %d, value_items %d\n", ParamName, value_size, value_items);

	err = HPI_Object_GetValue(hParam, entity_type_cstring, value_items,
		&szData, sizeof(szData));

	if (err)
	{
		HPI_GetErrorText(err, szError);
		printf("Error %s reading [%s] parameter\n", szError, ParamName);
		return -1;
	}

	printf("%s: %s\n", ParamName, szData);
	return 0;
}

static int read_bool_parameter(
	hpi_handle_t hMixer,
	hpi_handle_t hBlock,
	char *ParamName)
{
	char bData;
	hpi_handle_t hParam;
	hpi_err_t err = 0;
	size_t value_size = 0;
	size_t value_items = 0;
	char szError[256];


	err = HPI_Object_ParameterHandle(hMixer, hBlock, ParamName, &hParam);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s looking up %s parameter\n",
			szError, ParamName);
		return -1;
	}

	err = HPI_Object_GetInfo(hParam,
		entity_type_boolean, entity_role_value,
		NULL, &value_size, &value_items);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s calling HPI_Object_GetInfo(%s)\n", szError, ParamName);
		return -1;
	}

	err = HPI_Object_GetValue(hParam, entity_type_boolean, value_items,
		&bData, sizeof(bData));

	if (err)
	{
		HPI_GetErrorText(err, szError);
		printf("Error %s reading [%s] parameter\n", szError, ParamName);
		return -1;
	}

	printf("%s: %c\n", ParamName, bData);
	return 0;
}

static void show_blulink(hpi_handle_t hMixer)
{
	struct hpi_control_t asihpi_control;
	hpi_handle_t block;
	hpi_err_t err = 0;
	char szError[256];
	int status = 0;

	/* set source and destination node */
	asihpi_control.wSrcNodeType = HPI_SOURCENODE_ADAPTER;
	asihpi_control.wSrcNodeIndex = 0;
	asihpi_control.wDstNodeType = HPI_DESTNODE_NONE;
	asihpi_control.wDstNodeIndex = 0;

	printf("\nBLU link status/control\n\n");

	err = HPI_Object_BlockHandle(hMixer,
		asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
		asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
		"BLU link",
		&block);

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("Error %s looking up [BLU link]\n", szError);
		return;
	}

	status = read_string_parameter(hMixer, block, "BLU link Version");
	if (status)
		return;

	status = read_string_parameter(hMixer, block, "Sample Rate");
	if (status)
		return;

	status = read_integer_parameter(hMixer, block, "Mastership Priority");
	if (status)
		return;

	status = read_string_parameter(hMixer, block, "Sync");
	if (status)
		return;

	status = read_bool_parameter(hMixer, block, "In - Link");
	if (status)
		return;

	status = read_bool_parameter(hMixer, block, "In - Valid");
	if (status)
		return;

	status = read_integer_parameter(hMixer, block, "In - Single Error");
	if (status)
		return;

	status = read_integer_parameter(hMixer, block, "In - Double Error");
	if (status)
		return;

	status = read_bool_parameter(hMixer, block, "Out - Link");
	if (status)
		return;

	status = read_bool_parameter(hMixer, block, "Out - Valid");
	if (status)
		return;

	status = read_integer_parameter(hMixer, block, "Out - Single Error");
	if (status)
		return;

	status = read_integer_parameter(hMixer, block, "Out - Double Error");
	if (status)
		return;
}

static void show_channel_settings(hpi_handle_t hMixer)
{
	struct hpi_control_t asihpi_control;
	hpi_handle_t block;
	hpi_handle_t param;
	hpi_err_t err = 0;
	int Channel;
	char szError[256];

	/* set source and destination node */
	asihpi_control.wSrcNodeType = HPI_SOURCENODE_BLULINK;
	asihpi_control.wSrcNodeIndex = 0;
	asihpi_control.wDstNodeType = HPI_DESTNODE_NONE;
	asihpi_control.wDstNodeIndex = 0;

	printf("\nofframp channel\n\n");

	do {
		err = HPI_Object_BlockHandle(hMixer,
				asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
				asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
				"BLU link Channel",
				&block );

		if (err) {
			/* Error at node index 64 is expected, so don't log the error */
			if (asihpi_control.wSrcNodeIndex < 64) {
				HPI_GetErrorText(err, szError);
				printf("Error %s looking up Out [BLU link Channel] for source index %d \n",
					szError, asihpi_control.wSrcNodeIndex);
			}
			break;
		}

		err = HPI_Object_ParameterHandle( hMixer, block, "Channel", &param);

		if (err) {
			HPI_GetErrorText(err, szError);
			printf("Error %s looking up Out [Channel] parameter in [BLU link Channel] "
				"for source index %d \n",
				szError, asihpi_control.wSrcNodeIndex);
			break;
		}

		err = HPI_Object_GetValue(param, entity_type_int, 1,
			    &Channel, sizeof(Channel) );

		if (err)
		{
			HPI_GetErrorText(err, szError);
			printf("Error %s reading Out [Channel] parameter "
				"for source index %d \n",
				szError, asihpi_control.wSrcNodeIndex);
			break;
		}

		printf("%7d %7d\n",asihpi_control.wSrcNodeIndex,Channel);
		asihpi_control.wSrcNodeIndex++;
	}while(1);

	/* set source and destination node */
	asihpi_control.wSrcNodeType = HPI_SOURCENODE_NONE;
	asihpi_control.wSrcNodeIndex = 0;
	asihpi_control.wDstNodeType = HPI_DESTNODE_BLULINK;
	asihpi_control.wDstNodeIndex = 0;

	printf("\nonramp channel\n\n");

	do {
		err = HPI_Object_BlockHandle(hMixer,
				asihpi_control.wSrcNodeType, asihpi_control.wSrcNodeIndex,
				asihpi_control.wDstNodeType, asihpi_control.wDstNodeIndex,
				"BLU link Channel",
				&block );

		if (err) {
			/* Error at node index 64 is expected, so don't log the error */
			if (asihpi_control.wDstNodeIndex < 64) {
				HPI_GetErrorText(err, szError);
				printf("Error %s looking up In [BLU link Channel] for destination index %d \n",
					szError, asihpi_control.wDstNodeIndex);
			}
			break;
		}

		err = HPI_Object_ParameterHandle( hMixer, block, "Channel", &param);

		if (err) {
			HPI_GetErrorText(err, szError);
			printf("Error %s looking up In [Channel] parameter in [BLU link Channel] "
				"for destination index %d \n",
				szError, asihpi_control.wDstNodeIndex);
			break;
		}

		err = HPI_Object_GetValue(param, entity_type_int, 1,
			    &Channel, sizeof(Channel) );

		if (err)
		{
			HPI_GetErrorText(err, szError);
			printf("Error %s reading In [Channel] parameter "
				"for destination index %d \n",
				szError, asihpi_control.wDstNodeIndex);
			break;
		}

		printf("%6d %7d\n",asihpi_control.wDstNodeIndex,Channel);
		asihpi_control.wDstNodeIndex++;
	}while(1);
}

/************************************** MAIN ***********************/
int main(int argc, char *argv[])
{
	hpi_hsubsys_t *hSubSys;
	hpi_err_t err = 0;
	uint16_t wVersion;
	uint32_t dwSerialNumber;
	uint16_t wType;
	uint16_t wNumOutStreams;
	uint16_t wNumInStreams;
	hpi_handle_t hMixer = 0;

	parse_options(argc, argv);

	verbose_printf
		("********************************************************************\n");
	verbose_printf("\n** Test HPI using Functions **\n");

	/////////////////////////////////////////////////////////////////////////////
	// open subsystem and find adapters
	verbose_printf
		("********************************************************************\n");
	verbose_printf("HPI_SubSysCreate\n");
	hSubSys = HPI_SubSysCreate();
	if (hSubSys == NULL) {
		printf("hSubSys==NULL\n");
		exit(1);
	}

	err = HPI_AdapterClose(hSubSys, wAdapterIndex);
	verbose_printf("HPI_AdapterClose \n");
	HandleError(err);

	////////////////////////////////////////////////////////////////////////////
	// open 1st adapter
	err = HPI_AdapterOpen(hSubSys, wAdapterIndex);
	verbose_printf("HPI_AdapterOpen \n");
	HandleError(err);

	err = HPI_AdapterGetInfo(hSubSys,
		wAdapterIndex,
		&wNumOutStreams,
		&wNumInStreams, &wVersion, &dwSerialNumber, &wType);
	verbose_printf("HPI_AdapterGetInfo\n");
	HandleError(err);
	verbose_printf("Adapter ID=%4X Index=%d NumOutStreams=%d NumInStreams=%d S/N=%d\nHw Version %c%d DSP code version %03d\n", wType, wAdapterIndex, wNumOutStreams, wNumInStreams, dwSerialNumber, ((wVersion >> 3) & 0xf) + 'A',	// Hw version major
		wVersion & 0x7,	// Hw version minor
		((wVersion >> 13) * 100) + ((wVersion >> 7) & 0x3f)	// DSP code version
		);

	////////////////////////////////////////////////////////////////////////////
	// open the mixer of this adapter
	err = HPI_MixerOpen(hSubSys, wAdapterIndex, &hMixer);
	verbose_printf("HPI_MixerOpen: handle=%X\n", hMixer);
	HandleError(err);

	if ( onramp != -1 ) {
		if ( offramp != -1 ) {
			printf("Cannot use both onramp ('n') and offramp ('f') parameters.\n");
		} else {
			set_onramp(hMixer);
		}
	} else if ( offramp != -1 ) {
		set_offramp(hMixer);
	}else if ( priority != -1 ) {
		set_priority(hMixer);
	}
	else if (!do_show) {
		printf("Nothing to do!\n");
	}
	if ( do_show ) {  // Show all BLU link settings
		show_blulink(hMixer);
		show_channel_settings(hMixer);
	}

	err = HPI_MixerClose(hSubSys, hMixer);
	verbose_printf("\nHPI_MixerClose\n");
	HandleError(err);

	err = HPI_AdapterClose(hSubSys, wAdapterIndex);
	verbose_printf("HPI_AdapterClose\n");
	HandleError(err);

	HPI_SubSysFree(hSubSys);
	return 0;
}

/****************************** HandleError **********************/
static int getch(void)
{
	return getchar();
}

static void HandleError(hpi_err_t err)
{
	char szError[256];
	char nK = 0;

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("ERROR %d %s\n", err, szError);
		printf("press Enter to continue, (q,Enter) to exit...\n");
		nK = getch();
		if (nK == 'q')
			exit(0);
	}
}
/* END_OF_CODE */
