/* ------------------------------------------------------------------
                        MML compiler [M2]
               programmed by S.Yamamoto (SHINNOSUKE)
                   m2itlmac.c  --  内部マクロ
------------------------------------------------------------------ */

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<string.h>
#include	<limits.h>
#include	"m2.h"

#define	SUCCSESS	0
#define	FAILURE	(-1)
#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)
#define	YES	1
#define	NO	0

#define	ABS(x)	((x)>0?(x):-(x))
#define	CHECKSUM(x)	(((x)%128!=0)?128-(x)%128:0)

static	char	*itlMac[] = {
	"SLIDE",
	"WAVE",
	"CSLIDE",
	"CWAVE",
	"SCVLTR",
	"SCVDOT",
	"\0"
};

static	int	itlPNum[] = { 5 ,6 ,5 ,6 ,0 ,16 };


/* 内部マクロ */
/*
   _SLIDE(function mode,start pitch,end pitch,wait,number)
   _WAVE(sinpuku,tyuuou,speed,wait,number)
   _CSLIDE(control change number,...)
   _CWAVE(control change number,...)
   _SCVLTR("strings")
   _SCVDOT(?(0b00000000),?(0b00000000),...
           ?(0b00000000),?(0b00000000)))
*/

int	getIMPara( char **rp ,int mode ,int min ,int max ,char *msg )
{
	int	ret;
	int	dummy;

	ret = readPara( min ,max ,max+1 ,msg );
	dummy = ( mode == 0 ) ? ',' :')';
	if( readData() != dummy )
		errMsg( MSG_misopd ,msg );
	*rp += 1;
	return( ret );
}

/* アートキャンバス */

void	acvDispLtr( unsigned short t ,char **rp ,char *msg )
{
	char	buf[41];

	int	ex = 7;
	int	sum = 0x10;
	int	dat;

	if( readData() != '"' )
		errMsg( MSG_misopd ,msg );
	*rp += 1;

	buf[0] = 0x41;
	buf[1] = 0x10;
	buf[2] = 0x45;
	buf[3] = 0x12;
	buf[4] = 0x10;
	buf[5] = 0x00;
	buf[6] = 0x00;

	for(;;) {
		dat = **rp;
		if( dat == '\0' ) {
			skipLine( rp );
			continue;
		}
		*rp += 1;
		if( dat == '"' )
			break;
		if( dat < 32 || dat > 127)
			dat = 32;
		if( ex >= 32+7)
			errMsg( MSG_misopd ,msg );
		buf[ex] = dat;
		sum += dat;
		ex++;
	}
	*rp += 1;
	buf[ex] = CHECKSUM( sum );
	exclBank( t ,0xf0 ,ex+1 ,&buf[0] );
	return;
}

void	acvDispDot( unsigned short t ,char **rp ,char *msg )
{
	char	*buf;
	char	bufDummy[80];

	int	dot[16][16];
	int	i;
	int	j;
	int	k;
	int	dummy;
	int	dat;
	int	m;
	int	sum = 0x10+0x01+0x00;

	static	int	head[] =
		{ 0x41 ,0x10 ,0x45 ,0x12 ,0x10 ,0x01 ,0x00 };

	buf = &bufDummy[0];
	for( i=0;i<7;i++ )
		buf[i] = head[i];
	buf += 7;

	for( i=0;i<16;i++ ) {
		for( j=0;j<2;j++ ) {
			dummy = getIMPara( rp,((i==15&&j==1)?1:0),0,255,msg );
			for( k=0;k<8;k++ )
				dot[j*8+k][i] = (dummy & power(2,7-k)) >> (7-k);
		}
	}
	for( j=0;j<4;j++ ) {
		for( i=0;i<16;i++ ) {
			dat = 0;
			for( k=0;k<5;k++ ) {
				m = ( j == 3 && k != 0 )? 0 :dot[j*5+k][i];
				dat = ( dat << 1 ) + m;
			}
			sum = sum + dat;
			*buf = dat;
			buf++;
		}
	}
	sum = sum % 128;
	sum = ( sum != 0 )? 128-sum :sum;
	*buf = sum;
	exclBank( t ,0xf0 ,72 ,&bufDummy[0] );
	return;
}

/* 内部マクロメイン */
void	itlMacro( char **rp ,unsigned short *tc ,int *exclCt ,int part )
{
	char	*msg = "internal macro";

	int	imc;
	int	clChg;
	int	sldFnc;
	int	bend1;
	int	bend2;
	int	wLen;
	int	wNum;
	int	spd;
	int	para;
	int	i;
	int	max;
	int	min;
	int	dummy;

	unsigned	short	timeCt = *tc;

	imc = comSearch( itlMac ,*rp );
	if( imc == ERR )
		errMsg( MSG_freerr ,"Undefined this internal macro" );
	*rp += strlen( itlMac[imc] );

	if( readData() != '(' )
		errMsg( MSG_misopd ,msg );
	*rp += 1;

	if( imc == 5 ) {
		acvDispDot( *tc ,rp ,msg );
		goto	Point;
	}
	if( imc == 4 ) {
		acvDispLtr( *tc ,rp ,msg );
		goto	Point;
	}
	if( imc == 2 || imc == 3 )
		clChg = getIMPara( rp ,0 ,0 ,127 ,msg );
	if( imc == 0 || imc == 2 )
		sldFnc = getIMPara( rp ,0 ,1 ,2 ,msg );
	if( imc == 0 || imc == 1 ) {
		max = 8191;
		min = -8192;
	} else {
		max = 127;
		min = 0;
	}
	if( imc >= 0 || imc <= 3 ) {
		bend1 = getIMPara( rp ,0 ,min ,max ,msg );
		bend2 = getIMPara( rp ,0 ,min ,max ,msg );
	}
	if( imc == 1 || imc == 3 )
		spd = getIMPara( rp ,0 ,-180 ,180 ,msg );
	if( imc >= 0 && imc <= 3 ) {
		wLen = getIMPara( rp ,0 ,1 ,192 ,msg );
		wNum = getIMPara( rp ,1 ,1 ,256 ,msg );
	}

	switch( imc ) {
		case 0:
		case 2:	/* ----- _SLIDE/_CSLIDE */
			dummy = bend2 - bend1;
			dummy = dummy / ABS( dummy ) + dummy;
			para = bend1;
			for( i=0;i<=wNum;i++ ) {
				if( i == wNum )
					para = bend2;
				if( imc == 0 ) {
					para += 8192;
					evntPut( timeCt,0xe0+part,para & 0x7f,(para & 0x3f80) >> 7 );
				} else
					evntPut( timeCt,0xb0+part,clChg,para );
				if( i == wNum )
					break;
				timeCt += 192 / wLen;
				para = bend1+(dummy/power( wNum ,sldFnc ))*power( i+1,sldFnc );
			}
			break;
		case 1:
		case 3:	/* ----- _WAVE/_CWAVE */
			for( i=0;i<wNum;i++ ) {
			para = bend2 + bend1 * sine( i * spd );
			if( para > max )
				para = max;
			if( para < min )
				para = min;
				if( imc == 1 ) {
					para += 8192;
					evntPut( timeCt,0xe0+part,para & 0x7f,(para & 0x3f80) >> 7 );
				} else
					evntPut( timeCt,0xb0+part,clChg,para );
				timeCt += 192 / wLen;
			}
			break;
	}
Point:
	*tc = timeCt;
	return;
}
