/************************************************************************
 ************************************************************************
    FAUST compiler
	Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
    ---------------------------------------------------------------------
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
 ************************************************************************
 ************************************************************************/


/*****************************************************************************
	HISTORY
	22/01/05 : added int cast in se"lect2 and select3 selection signal
*****************************************************************************/



#include "signals.hh"


////////////////////////////////////////////////////////////////////////
/**
 * Les Signaux.
 * Les différents constructeurs et deconstructeurs de signaux
 */
///////////////////////////////////////////////////////////////////////

Tree  sigInt(int i) 								{ return tree(i); 					}
bool  isSigInt(Tree t, int* i) 						{ return isInt(t->node(), i); 		}

Tree  sigReal(float r) 							{ return tree(r); 					}
bool  isSigReal(Tree t, float* r) 					{ return isFloat(t->node(), r); 	}

Sym SIGINPUT = symbol ("sigInput");
Tree  sigInput(int i)								{ return tree(SIGINPUT, tree(i));	}
bool  isSigInput(Tree t, int* i) 					{ Tree x; return isTree(t, SIGINPUT, x) && isInt(x->node(),i); 	}

Sym SIGOUTPUT = symbol ("sigOutput");
Tree  sigOutput(int i, Tree t0)						{ return tree(SIGOUTPUT, tree(i), t0); 	}
bool  isSigOutput(Tree t, int* i, Tree& t0)			{ Tree x; return isTree(t, SIGOUTPUT, x, t0) && isInt(x->node(),i); 	}

Sym SIGDELAY1 = symbol ("sigDelay1");
Tree  sigDelay1(Tree t0)							{ return tree(SIGDELAY1, t0); 		}
bool  isSigDelay1(Tree t, Tree& t0)					{ return isTree(t, SIGDELAY1, t0); 	}

Sym SIGFIXDELAY = symbol ("sigFixDelay");
Tree  sigFixDelay(Tree t0, Tree t1)					{ return tree(SIGFIXDELAY, t0, t1); 		}
bool  isSigFixDelay(Tree t, Tree& t0, Tree& t1)		{ return isTree(t, SIGFIXDELAY, t0, t1); 	}

Sym SIGPREFIX = symbol ("sigPrefix");
Tree  sigPrefix(Tree t0, Tree t1)					{ return tree(SIGPREFIX, t0, t1); 		}
bool  isSigPrefix(Tree t, Tree& t0, Tree& t1)		{ return isTree(t, SIGPREFIX, t0, t1); 	}

Sym SIGIOTA = symbol ("sigIota");
Tree  sigIota(Tree t0)								{ return tree(SIGIOTA, t0); 		}
bool  isSigIota(Tree t, Tree& t0)					{ return isTree(t, SIGIOTA, t0); 	}


Sym SIGRDTBL = symbol ("SigRDTbl");
Tree sigRDTbl (Tree t, Tree i)						{ return tree(SIGRDTBL, t, i); 	}
bool isSigRDTbl (Tree s, Tree& t, Tree& i)			{ return isTree(s, SIGRDTBL, t, i); 	}

Sym SIGWRTBL = symbol ("SigWRTbl");
Tree sigWRTbl (Tree id, Tree t, Tree i, Tree s)					{ return tree(SIGWRTBL, id, t, i, s); 	}
bool isSigWRTbl (Tree u, Tree& id, Tree& t, Tree& i, Tree& s)	{ return isTree(u, SIGWRTBL, id, t, i, s); 	}

Sym SIGTABLE = symbol ("SigTable");
Tree sigTable (Tree id, Tree n, Tree sig)				{ return tree(SIGTABLE, id, n, sig); 		}
bool isSigTable (Tree t, Tree& id, Tree& n, Tree& sig)	{ return isTree(t, SIGTABLE, id, n, sig); 	}

// selection d'un signal parmis plusieurs

Sym SIGSELECT2 = symbol ("SigSelect2");
Sym SIGSELECT3 = symbol ("SigSelect3");

Tree sigSelect2 (Tree selector, Tree s1, Tree s2)							{ return tree(SIGSELECT2, sigIntCast(selector), s1, s2); }
bool isSigSelect2 (Tree t, Tree& selector, Tree& s1, Tree& s2)				{ return isTree(t, SIGSELECT2, selector, s1, s2); }

Tree sigSelect3 (Tree selector, Tree s1, Tree s2, Tree s3)					{ return tree(SIGSELECT3, sigIntCast(selector), s1, s2, s3); }
bool isSigSelect3 (Tree t, Tree& selector, Tree& s1, Tree& s2, Tree& s3)	{ return isTree(t, SIGSELECT3, selector, s1, s2, s3); }


// operations arithmétiques

Sym SIGBINOP = symbol ("SigBinOp");
Tree sigBinOp(int op, Tree x, Tree y) 					{ return tree(SIGBINOP, tree(op), x, y); }
bool isSigBinOp(Tree s, int* op, Tree& x, Tree& y) 		{ Tree t; return isTree(s, SIGBINOP, t, x, y) && isInt(t->node(),op); }


// Foreign Functions

Sym SIGFFUN = symbol ("SigFFun");
Tree sigFFun (Tree ff, Tree largs)						{ return tree(SIGFFUN, ff, largs); 			}
bool isSigFFun	(Tree s, Tree& ff, Tree& largs)			{ return isTree(s, SIGFFUN, ff, largs);		}


Sym SIGFCONST = symbol ("SigFConst");
Tree sigFConst 		(Tree type, Tree name, Tree file)				{ return tree(SIGFCONST, type, name, file); 			}
bool isSigFConst	(Tree s)										{ Tree t,n,f; return isTree(s, SIGFCONST, t, n, f);	}
bool isSigFConst	(Tree s, Tree& type, Tree& name, Tree& file)	{ return isTree(s, SIGFCONST,type, name, file);		}

// Generateur de signal (compilé en tant que classe C++)

Sym SIGGEN = symbol ("SigGen");
Tree sigGen (Tree s)							{ return tree(SIGGEN, s); 		}
bool isSigGen (Tree t, Tree& x)					{ return isTree(t, SIGGEN, x); 	}
bool isSigGen (Tree t)							{ return t->node()== Node(SIGGEN); 	}

// nouvelle version utilisant rec et ref

Sym SIGPROJ = symbol ("SigProj");
Tree sigProj (int i, Tree rgroup)				{ return tree(SIGPROJ, tree(i), rgroup); 	}
bool isProj (Tree t, int* i, Tree& rgroup)		{ Tree x; return isTree(t, SIGPROJ, x, rgroup) && isInt(x->node(), i); 	}


// Int and Float casting

Sym SIGINTCAST = symbol ("sigIntCast");
Sym SIGFLOATCAST = symbol ("sigFloatCast");

Tree  sigIntCast(Tree t)						{ return tree(SIGINTCAST, t); 			}
Tree  sigFloatCast(Tree t)						{ return tree(SIGFLOATCAST, t); 		}

bool  isSigIntCast(Tree t, Tree& x)				{ return isTree(t, SIGINTCAST, x); 		}
bool  isSigFloatCast(Tree t, Tree& x)			{ return isTree(t, SIGFLOATCAST, x); 	}




/*****************************************************************************
							 User Interface Elements
*****************************************************************************/

Sym SIGBUTTON = symbol ("SigButton");
Tree sigButton 	 (Tree lbl)					{ return tree(SIGBUTTON, lbl); 					}
bool isSigButton (Tree s)					{ Tree lbl; return isTree(s, SIGBUTTON, lbl);	}
bool isSigButton (Tree s, Tree& lbl)		{ return isTree(s, SIGBUTTON, lbl);				}


Sym SIGCHECKBOX = symbol ("SigCheckbox");
Tree sigCheckbox   (Tree lbl)				{ return tree(SIGCHECKBOX, lbl); 					}
bool isSigCheckbox (Tree s)					{ Tree lbl; return isTree(s, SIGCHECKBOX, lbl);	}
bool isSigCheckbox (Tree s, Tree& lbl)		{ return isTree(s, SIGCHECKBOX, lbl);				}


Sym SIGHSLIDER = symbol ("SigHSlider");
Tree sigHSlider   (Tree lbl, Tree cur, Tree min, Tree max, Tree step)
											{ return tree(SIGHSLIDER, lbl, list4(cur,min,max,step));		}
bool isSigHSlider (Tree s)					{ Tree lbl, params; return isTree(s, SIGHSLIDER, lbl, params);	}

bool isSigHSlider (Tree s, Tree& lbl, Tree& cur, Tree& min, Tree& max, Tree& step)
{
	Tree params;
	if (isTree(s, SIGHSLIDER, lbl, params)) {
		cur = nth(params, 0);
		min = nth(params, 1);
		max = nth(params, 2);
		step= nth(params, 3);
		return true;
	} else {
		return false;
	}
}


Sym SIGVSLIDER = symbol ("SigVSlider");
Tree sigVSlider   (Tree lbl, Tree cur, Tree min, Tree max, Tree step)
											{ return tree(SIGVSLIDER, lbl, list4(cur,min,max,step));		}
bool isSigVSlider (Tree s)					{ Tree lbl, params; return isTree(s, SIGVSLIDER, lbl, params);	}

bool isSigVSlider (Tree s, Tree& lbl, Tree& cur, Tree& min, Tree& max, Tree& step)
{
	Tree params;
	if (isTree(s, SIGVSLIDER, lbl, params)) {
		cur = nth(params, 0);
		min = nth(params, 1);
		max = nth(params, 2);
		step= nth(params, 3);
		return true;
	} else {
		return false;
	}
}


Sym SIGNUMENTRY = symbol ("SigNumEntry");
Tree sigNumEntry   (Tree lbl, Tree cur, Tree min, Tree max, Tree step)
											{ return tree(SIGNUMENTRY, lbl, list4(cur,min,max,step));		}
bool isSigNumEntry (Tree s)					{ Tree lbl, params; return isTree(s, SIGNUMENTRY, lbl, params);	}

bool isSigNumEntry (Tree s, Tree& lbl, Tree& cur, Tree& min, Tree& max, Tree& step)
{
	Tree params;
	if (isTree(s, SIGNUMENTRY, lbl, params)) {
		cur = nth(params, 0);
		min = nth(params, 1);
		max = nth(params, 2);
		step= nth(params, 3);
		return true;
	} else {
		return false;
	}
}

// output elements



Sym SIGHBARGRAPH = symbol ("SigHBargraph");
Tree sigHBargraph   (Tree lbl, Tree min, Tree max, Tree x)				{ return tree(SIGHBARGRAPH, lbl, min, max, x);		}
bool isSigHBargraph (Tree s)											{ Tree lbl, min, max, x; return isTree(s, SIGHBARGRAPH, lbl, min, max, x);	}
bool isSigHBargraph (Tree s, Tree& lbl, Tree& min, Tree& max, Tree& x)	{ return isTree(s, SIGHBARGRAPH, lbl, min, max, x);	}


Sym SIGVBARGRAPH = symbol ("SigVBargraph");
Tree sigVBargraph   (Tree lbl, Tree min, Tree max, Tree x)				{ return tree(SIGVBARGRAPH, lbl, min, max, x);		}
bool isSigVBargraph (Tree s)											{ Tree lbl, min, max, x; return isTree(s, SIGVBARGRAPH, lbl, min, max, x);	}
bool isSigVBargraph (Tree s, Tree& lbl, Tree& min, Tree& max, Tree& x)	{ return isTree(s, SIGVBARGRAPH, lbl, min, max, x);	}


Sym SIGATTACH = symbol ("sigAttach");
Tree  sigAttach(Tree t0, Tree t1)					{ return tree(SIGATTACH, t0, t1); 		}
bool  isSigAttach(Tree t, Tree& t0, Tree& t1)		{ return isTree(t, SIGATTACH, t0, t1); 	}



Tree addNums(Tree a, Tree b)
{
	return tree(addNode(a->node(),b->node()));
}

Tree subNums(Tree a, Tree b)
{
	return tree(subNode(a->node(),b->node()));
}

Tree mulNums(Tree a, Tree b)
{
	return tree(mulNode(a->node(),b->node()));
}

Tree divNums(Tree a, Tree b)
{
	return tree(divNode(a->node(),b->node()));
}

Tree divExtendedNums(Tree a, Tree b)
{
	return tree(divExtendedNode(a->node(),b->node()));
}

Tree minusNum(Tree a)
{
	return tree(minusNode(a->node()));
}

Tree inverseNum(Tree a)
{
	return tree(inverseNode(a->node()));
}

bool isSigAdd(Tree a, Tree& x, Tree& y)
{
	int	op;
	return isSigBinOp(a, &op, x, y) && (op == kAdd);
}

bool isSigMul(Tree a, Tree& x, Tree& y)
{
	int	op;
	return isSigBinOp(a, &op, x, y) && (op == kMul);
}

bool isSigSub(Tree a, Tree& x, Tree& y)
{
	int	op;
	return isSigBinOp(a, &op, x, y) && (op == kSub);
}

bool isSigDiv(Tree a, Tree& x, Tree& y)
{
	int	op;
	return isSigBinOp(a, &op, x, y) && (op == kDiv);
}




/*****************************************************************************
							 matrix extension
*****************************************************************************/
Sym SIGTUPLE 		= symbol ("SigTuple");
Sym SIGTUPLEACCESS 	= symbol ("SigTupleAccess");

// a tuple of signals is basically a list of signals.
// mode = 0 means normal, mode = 1 means blocked
Tree sigTuple (int mode, Tree ls)					{ return tree(SIGTUPLE, tree(mode), ls); }
bool isSigTuple (Tree s, int* mode, Tree& ls)		{ Tree m; return isTree(s, SIGTUPLE, m, ls) && isInt(m->node(), mode); }

// Access the components of a tuple.
// ts is tuple of signals, idx is a scalar signal between 0..n
Tree sigTupleAccess(Tree ts, Tree idx)				{ return tree(SIGTUPLEACCESS, ts, idx); }
bool isSigTupleAccess(Tree s, Tree& ts, Tree& idx)	{ return isTree(s, SIGTUPLEACCESS, ts, idx); }

// create a tuple of signals
Tree sigCartesianProd (Tree s1, Tree s2)
{
	Tree 	l1, l2;
	int		m1, m2;

	if (isSigTuple(s1, &m1, l1) && (m1 == 0)) {
		// nothing to do
	} else {
		l1 = list1(s1);
	}

	if (isSigTuple(s2, &m2, l2) && (m2 == 0)) {
		// nothing to do
	} else {
		l2 = list1(s2);
	}


	return sigTuple(0, concat(l1,l2));
}

