#include "squaredance.h"
#include "dancefigures.h"
#include <assert.h>

#define MAX_ROLE_CNT	64	/* Number of bits in a long */

static void danceSmoothTransitions(Dance *dance, Person *role, enum danceFigure fig) {
    FigureSegment *cs, *csPrev;
    int i;

    if ( role->old_move_cnt==role->curBlock->move_cnt )
return;		/* No new transition has appeared */
    if ( role->old_move_cnt==0 )
return;		/* No transition to smooth at start */
    cs = &role->curBlock->moves[role->old_move_cnt];
    csPrev = &role->curBlock->moves[role->old_move_cnt-1];

    if ( !csPrev->dd_cleanup || !cs->dd_setup )
return;

    /* !!!! Transitions at start and end of if??? */
    if ( fig==df_figure8 && cs->cmd==c_pause &&
	    (cs->orient==mo_forward || cs->orient==mo_fixed) && cs->millibars<600 ) {
	/* sometimes we can fold the pause at the start of a man's figure 8 into to previous movement */
	csPrev->millibars += cs->millibars;
	for ( i=role->old_move_cnt; i<role->curBlock->move_cnt-1; ++i )
	    memcpy(&role->curBlock->moves[i],&role->curBlock->moves[i+1],sizeof(FigureSegment));
	--role->curBlock->move_cnt;
	csPrev->dd_cleanup=false;
	cs->dd_setup = false;
    }
    /* !!!!! More work needed here for other cases of the if */
}

static void danceAddFS(Dance *dance,Person *role,FigureSegment *fs) {
    Block *b = role->curBlock;
    FigureSegment *cs;

    if ( b->move_cnt>=b->move_max-1 ) {
	b->moves = realloc(b->moves,(b->move_max += 20)*sizeof(FigureSegment));
	memset(b->moves+b->move_cnt,0,20*sizeof(FigureSegment));
    }
    cs = &b->moves[b->move_cnt++];
    *cs = *fs;
    cs->ifblock = blockCopy(cs->ifblock);
    cs->levitation = valueChangeCopy(cs->levitation);
    cs->outness = valueChangeCopy(cs->outness);
    cs->rightHand = rThetaChangeCopy(cs->rightHand);
    cs->swordPoint = rThetaChangeCopy(cs->swordPoint);
    cs->leftHand = rThetaChangeCopy(cs->leftHand);
}

static void danceAddPause(Dance *dance,Person *role,int millibars) {
    FigureSegment cs;

    initFigureSegment(&cs);
    cs.cmd = c_pause;
    cs.millibars = millibars;
    danceAddFS(dance,role,&cs);
}

static void danceFigure_Meanwhile(Dance *dance,DanceDescription *dd) {
    DanceDescription *kid;

    for ( kid = dd->kids; kid!=NULL; kid=kid->siblings ) {
	if ( figureTable[kid->figure].figure!=kid->figure ) {
	    fprintf( stderr, "Figure table is out of sync at %d (is %s but shouldn't be)\n", kid->figure, figureTable[kid->figure].name );
	    abort();
	}
	figureTable[kid->figure].doIt(dance,kid);
    }
}

static void danceFigure_Concat(Dance *dance,DanceDescription *dd) {
    DanceDescription *kid;
    int oldMoveCnt[MAX_ROLE_CNT];
    int r;

    for ( kid = dd->kids; kid!=NULL; kid=kid->siblings ) {
	for ( r=0; r<dance->role_cnt; ++r )
	    oldMoveCnt[r] = dance->roles[r].curBlock->move_cnt;

	if ( figureTable[kid->figure].figure!=kid->figure ) {
	    fprintf( stderr, "Figure table is out of sync at %d (is %s but shouldn't be)\n", kid->figure, figureTable[kid->figure].name );
	    abort();
	}
	figureTable[kid->figure].doIt(dance,kid);

	for ( r=0; r<dance->role_cnt; ++r ) {
	    if ( oldMoveCnt[r]!=dance->roles[r].curBlock->move_cnt ) {
		dance->roles[r].old_move_cnt = oldMoveCnt[r];
		danceSmoothTransitions(dance,&dance->roles[r],kid->figure);
		dance->roles[r].old_move_cnt = dance->roles[r].curBlock->move_cnt;
	    }
	}
    }
}

static void danceFigure_If(Dance *dance,DanceDescription *dd) {
    DanceDescription *kid;
    Block *oldBlock[MAX_ROLE_CNT];
    int r;
    Block emptyIf, emptyElse;
    FigureSegment fsIf, *csIf;

    memset(&emptyIf,0,sizeof(emptyIf));
    memset(&emptyElse,0,sizeof(emptyElse));
    initFigureSegment(&fsIf);
    fsIf.cmd = c_if;
    fsIf.ifblock = &emptyIf;
    emptyIf.condition = dd->condition;
    emptyIf.next = &emptyElse;

    for ( r=0; r<MAX_ROLE_CNT && r<dance->role_cnt; ++r ) if ( dd->personMask&(1<<r)) {
	oldBlock[r] = dance->roles[r].curBlock;
	/*oldMoveCnt[r] = dance->roles[r].old_move_cnt;*/
	danceAddFS(dance,&dance->roles[r],&fsIf);
	csIf = &oldBlock[r]->moves[oldBlock[r]->move_cnt-1];
	assert( csIf->cmd==c_if );
	dance->roles[r].curBlock = csIf->ifblock;
	dance->roles[r].old_move_cnt=0;
    }

    if ( figureTable[dd->kids->figure].figure!=dd->kids->figure ) {
	fprintf( stderr, "Figure table is out of sync at %d (is %s but shouldn't be)\n", dd->kids->figure, figureTable[dd->kids->figure].name );
	abort();
    }
    figureTable[dd->kids->figure].doIt(dance,dd->kids);
    
    for ( r=0; r<MAX_ROLE_CNT && r<dance->role_cnt; ++r ) if ( dd->personMask&(1<<r)) {
	csIf = &oldBlock[r]->moves[oldBlock[r]->move_cnt-1];
	assert( csIf->cmd==c_if );
	dance->roles[r].curBlock = csIf->ifblock->next;
	dance->roles[r].old_move_cnt=0;
    }

    for ( r=0; r<MAX_ROLE_CNT && r<dance->role_cnt; ++r ) if ( dd->personMask&(1<<r)) {
	dance->roles[r].curBlock = oldBlock[r];
	dance->roles[r].old_move_cnt = oldBlock[r]->move_cnt;
    }
}

static void danceFigure_Explicit(Dance *dance,DanceDescription *dd) {
    int r, i;

    for ( r=0; r<MAX_ROLE_CNT && r<dance->role_cnt; ++r ) if ( dd->personMask&(1<<r)) {
	for ( i=0; i<dd->move_cnt; ++i )
	    danceAddFS(dance,&dance->roles[r],&dd->moves[i]);
    }
}

static void danceFigure_Wait(Dance *dance,DanceDescription *dd) {
    int r;

    for ( r=0; r<MAX_ROLE_CNT && r<dance->role_cnt; ++r ) if ( dd->personMask&(1<<r)) {
	danceAddPause(dance,&dance->roles[r],dd->millibars);
    }
}

struct danceFigureTable figureTable[] = {
    { df_meanwhile, "Meanwhile", danceFigure_Meanwhile, -1 },
    { df_concat   , "Concat",    danceFigure_Concat, -1 },
    { df_if       , "If",        danceFigure_If, -1 },			/* !!!!! Deal with if!!!!! */
    { df_explicit , "explicit",  danceFigure_Explicit, -1 },
    { df_wait     , "Wait",      danceFigure_Wait, -1 },

    { -1, NULL, NULL, -1 }
};

void danceRebuildFromDescription(Dance *dance) {
    int i;
    int r;
    int barCnt, maxBarCnt;
    DanceDescription *dd;

    danceResetToNew(dance,0);

    for ( r=0; r<dance->role_cnt; ++r )
	dance->roles[r].curBlock = &dance->roles[r].figures;

    for ( i=0; i<dance->description_cnt; ++i ) {
	for ( r=0; r<dance->role_cnt; ++r )
	    dance->roles[r].old_move_cnt = dance->roles[r].curBlock->move_cnt;

	dd = &dance->description[i];
	if ( figureTable[dd->figure].figure!=dd->figure ) {
	    fprintf( stderr, "Figure table is out of sync at %d (is %s but shouldn't be)\n", dd->figure, figureTable[dd->figure].name );
	    abort();
	}
	figureTable[dd->figure].doIt(dance,dd);
	maxBarCnt = 0;
	for ( r=0; r<dance->role_cnt; ++r ) {
	    if ( dance->roles[r].effective_role<r_outAtTop || dance->roles[r].effective_role > r_outAtBottom_alt ) {
		barCnt = blockCountMillibars( &dance->roles[r].figures );
		if ( barCnt>maxBarCnt )
		    maxBarCnt = barCnt;
	    }
	}
	for ( r=0; r<dance->role_cnt; ++r ) {
	    if ( dance->roles[r].effective_role<r_outAtTop || dance->roles[r].effective_role > r_outAtBottom_alt ) {
		barCnt = blockCountMillibars( &dance->roles[r].figures );
		if ( barCnt<maxBarCnt )
		    danceAddPause(dance,&dance->roles[r],maxBarCnt-barCnt);
	    }
	}
	for ( r=0; r<dance->role_cnt; ++r ) {
	    if ( dance->roles[r].effective_role<r_outAtTop || dance->roles[r].effective_role > r_outAtBottom_alt )
		danceSmoothTransitions(dance,&dance->roles[r],dd->figure);
	}
    }

    for ( r=0; r<dance->role_cnt; ++r ) {
	if ( dance->roles[r].effective_role>=r_outAtTop && dance->roles[r].effective_role <= r_outAtBottom_alt ) {
	    if ( dance->roles[r].effective_role==r_outAtBottom1of2 )
		/* Copy of r_1s */;
	    else if ( dance->roles[r].effective_role==r_outAtBottom2of2 )
		/* Copy of r_2s */;
	    else if (( dance->roles[r].effective_role==r_outAtBottom || dance->roles[r].effective_role==r_outAtTop ) &&
		    dance->improper_mask!=0 )
		/* cross over */;
	    else
		danceAddPause(dance,&dance->roles[r],maxBarCnt);
	}
    }
}
