/***************************************************************************
*	NAME:  MIXBLD.C $Revision: 1.9 $
**	COPYRIGHT:
**	"Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: mixbld.c $
* Revision 1.9  1996/01/25 15:34:06  mleibow
* Revision 1.8  1995/10/13 17:19:44  mleibow
* Added effects mixer slider.
* Added code to make it easier to change mixer node map in future.
* Added code to make master volumes change at 10dB drop offs instead of 6dB.
* Revision 1.7  1995/06/08 11:54:34  mleibow
* Added multiple language support for mixer labels.
* Revision 1.6  1995/04/26 16:28:30  sdsmith
* Fixed public function commentary
* Revision 1.5  1995/04/14 09:19:51  sdsmith
* Added support for B0 silicon
* Revision 1.4  1995/03/30 09:47:47  sdsmith
* New mixer fixes
* Revision 1.3  1995/03/27 08:05:12  sdsmith
* Added type fix 
* Revision 1.2  1995/03/27 07:59:07  sdsmith
* New mixer node stuff
* Revision 1.1  1995/02/23 11:07:47  unknown
* Initial revision
***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include "iw.h"
#include "iwl.h"
#include "digital.h"
#include "codec.h"

/* Callbacks provided by application or wrapper */
extern int iw_mix_report_nodes(int);
extern int iw_mix_add_node(short, short, short, iw_mixer_node_type,
							iw_mixer_node_subtype, short, short, short,
							int, int, int, int, int, int,
							unsigned char RFAR *, unsigned char RFAR *);
extern int iw_add_weighted_path(int, int);

extern int MAX_CODEC_NODES;
extern struct iw_mixer_node iw_mixer_nodes[];

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

LOCAL DEFINITION:
weighted_paths - array of weighted paths discovered in mixer node list

SEE ALSO: 
iw_build_mixer

*/
struct wpaths {
	int snode;
	int dnode;
};
#define MAX_WEIGHTED_PATHS 6
static struct wpaths weighted_paths[MAX_WEIGHTED_PATHS];

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

FUNCTION DEFINITION:
path_found - see if a weighted path has been found 

DESCRIPTION:
Due to the way the mixer nodes are stored, it is possible to encounter
the same feedback more than once when traversing the mixer nodes.  path_found
searches the weighted paths already discovered to see if the 
source/destination node pair from the caller have already been found and
identified as a weighted path.

RETURNS: 1 - if weighted path already found
         0 - otherwise
*/
static int path_found(int snode, int dnode)
{
	int i;

	for (i=0; i<MAX_WEIGHTED_PATHS; i++)
		if (weighted_paths[i].snode == snode &&
			weighted_paths[i].dnode == dnode)
			return(1);
	return(0);
}

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

FUNCTION DEFINITION:
find_node - find a mixer node with the specified id

DESCRIPTION:
The mixer nodes are stored in a globally available array.  find_node searches
the array to find the node with the id matching the caller's spec.

RETURNS: int - array index of node with id
               -1 if no node with id exists
*/
static int find_node(int id)
{
	int i, found = 0;

	for (i=0; i<MAX_CODEC_NODES; i++)
		if (iw_mixer_nodes[i].id == (unsigned short)id) {
			found = 1;
			break;
		}
	if (!found)
		i = -1;
	return(i);
}

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

FUNCTION DEFINITION:
iw_build_mixer - report the mixer nodes to the caller

DESCRIPTION:
iw_build_mixer informs the caller of the mixer nodes through a series of
callbacks.  The callback are provides by the calling application or wrapper.
The callbacks are NOT kernel routines.  The application or wrapper must
provide the following three callbacks:

  iw_mix_report_nodes
  iw_mix_add_node
  iw_describe_node
  iw_add_weighted_path

First, iw_build_mixer calles iw_mix_report_nodes to report the number of
mixer nodes that will be coming in subsequent callbacks.  For each mixer
node, iw_build_mixer calls iw_mix_add_node to tell the caller the id,
parent, and type of node.  iw_build_mixer then calls iw_describe_node to
report the appropriate description record for that type of node.

After all the nodes have been reported, iw_build_mixer searches for 
feedback maths in the mixer nodes.  A feedback path will occur when
IWN_TEE node has one output path to a terminal node, and one output
path to an IWN_MIXER node or an IWN_MULTIPLEXER node.  Any feedback
paths are reported to the caller.

RETURNS: void

SEE ALSO:
InterWave Kernel Reference for a discussion of mixer nodes and
mixer node types.
*/
void iw_build_mixer()
{
	int rc,i,j,parent1,parent2,pid;
	iw_mixer_node_type tp1, tp2;
	iw_mixer_node_subtype stp1, stp2;
	int wpath = -1;
	int num_usable_nodes = MAX_CODEC_NODES;

	for (i=0; i < MAX_CODEC_NODES; i++) {
	    if (iw_mixer_nodes[i].id == (unsigned short)-2) num_usable_nodes--;
	}
	rc = iw_mix_report_nodes(num_usable_nodes);
	if (rc == IW_OK) {
		for (i=0; (i < MAX_CODEC_NODES && rc == IW_OK); i++) {
//			/* Since AUX 2 does not have a fixed function
//			   we should read the usage string and change
//			   it */
//			if ((iw_mixer_nodes[i].type == INPUT) &&
//			   (iw_mixer_nodes[i].subtype == AUX) &&
//			   (iw_mixer_nodes[i].subordinal == 2)) {
//				   iwu_get_profile_string("AUX 2","usage","Aux 2",
//				   						  iw_mixer_nodes[i].name,
//										  iwu_strlen(iw_mixer_nodes[i].name),
//										  os_getenv("INTERWAVE"));
//			}
			if (iw_mixer_nodes[i].id != (unsigned short)-2) {
				rc = iw_mix_add_node(iw_mixer_nodes[i].id,
						 iw_mixer_nodes[i].parent_id,
						 iw_mixer_nodes[i].owner_id,
						 iw_mixer_nodes[i].type,
						 iw_mixer_nodes[i].subtype,
						 iw_mixer_nodes[i].ordinal,
						 iw_mixer_nodes[i].subordinal,
						 iw_mixer_nodes[i].attributes,
						 iw_mixer_nodes[i].range.min,
						 iw_mixer_nodes[i].range.max,
						 iw_mixer_nodes[i].range.inc_integer,
						 iw_mixer_nodes[i].range.inc_fraction,
						 iw_mixer_nodes[i].current.stereo.left,
						 iw_mixer_nodes[i].current.stereo.right,
						 iw_mixer_nodes[i].description,
						 iw_mixer_nodes[i].name
						 );
			}
		}
    /* Hunt down and report the feedback paths. */
		parent1 = -1;
		for (i=0; i<MAX_CODEC_NODES; i++) {
			if (iw_mixer_nodes[i].subtype == TEE) {
        /* walk the first branch of the tee until IWN_OUTPUT, INPUT, MUX, MIX */
				pid = find_node(iw_mixer_nodes[i].parent_id);
				while (pid >= 0) {
					if (iw_mixer_nodes[pid].type == OUTPUT ||
						iw_mixer_nodes[pid].type == INPUT ||
						(iw_mixer_nodes[pid].type == JUNCTION && iw_mixer_nodes[pid].subtype == MIXER) ||
						(iw_mixer_nodes[pid].type == JUNCTION && iw_mixer_nodes[pid].subtype == MUX)) {
						parent1 = pid;
						pid = -1;
					}
					else {
						pid = find_node(iw_mixer_nodes[pid].parent_id);
					}
				}
	/* find the next branch of the tee */
				for (j=0; j<MAX_CODEC_NODES; j++) {
					if (iw_mixer_nodes[j].subtype == TEE &&
						iw_mixer_nodes[j].id == iw_mixer_nodes[i].id &&
						iw_mixer_nodes[j].parent_id != iw_mixer_nodes[i].parent_id) {
						pid = find_node(iw_mixer_nodes[j].parent_id);
						while (pid >=0 ) {
							if (iw_mixer_nodes[pid].type == OUTPUT ||
								iw_mixer_nodes[pid].type == INPUT ||
								(iw_mixer_nodes[pid].type == JUNCTION && iw_mixer_nodes[pid].subtype == MIXER) ||
								(iw_mixer_nodes[pid].type == JUNCTION && iw_mixer_nodes[pid].subtype == MUX)) {
								parent2 = pid;
								pid = -1;
							}
							else {
								pid = find_node(iw_mixer_nodes[pid].parent_id);
							}
						}
						if (parent1 > -1 && parent2 > -1) {
							tp1 = iw_mixer_nodes[parent1].type;
							stp1 = iw_mixer_nodes[parent1].subtype;
							tp2 = iw_mixer_nodes[parent2].type;
							stp2 = iw_mixer_nodes[parent2].subtype;
							if (stp1 == MIXER || stp1 == MUX) {
								if (tp2 == OUTPUT || tp2 == INPUT) {
									if (!path_found(iw_mixer_nodes[i].id,
													iw_mixer_nodes[i].id)) {
										wpath++;
										weighted_paths[wpath].snode = iw_mixer_nodes[i].id;
										weighted_paths[wpath].dnode = iw_mixer_nodes[i].parent_id;
									}
								}
							}
							else if (tp1 == INPUT || tp1 == OUTPUT) {
								if (stp2 == MIXER || stp2 == MUX) {
									if (!path_found(iw_mixer_nodes[i].id,
													iw_mixer_nodes[j].id)) {
										wpath++;
										weighted_paths[wpath].snode = iw_mixer_nodes[i].id;
										weighted_paths[wpath].dnode = iw_mixer_nodes[j].parent_id;
									}
								}
							}
						}
					}
				}
			}
			parent1 = parent2 = -1;
		}
		for (i=0; i<=wpath; i++)
			iw_add_weighted_path(weighted_paths[i].snode, weighted_paths[i].dnode);
	}
}
