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

static char *coupleNames[] = { "1s", "2s", "3s", "4s", "1s", "2s", "3s", "4s", NULL };
static char *posNames[] = { "1st", "2nd", "3rd", "4th", "1st", "2nd", "3rd", "4th", NULL };
static char *sidesheads[] = { "heads", "sides", "heads", "sides", NULL };
static char *SidesHeads[] = { "Heads", "Sides", "Heads", "Sides", NULL };
static char *sidehead[] = { "head", "side", "head", "side", NULL };
static char *SideHead[] = { "Head", "Side", "Head", "Side", NULL };

/* In calls and audiofilenames the following special marks occur */
/* %1-%4 gives a couple name */
/*  if active==1 => %1 yields "1s", %2 yields "2s", ... */
/*  if active==2 => %1 yields "2s", %2 yields "3s", %3 yields "4s" and %4 yields "1s" */
/*  if active==3 => %1 yields "3s", %2 yields "4s", %3 yields "1s" and %4 yields "2s" */
/*  if active==4 => %1 yields "4s", %2 yields "1s", %3 yields "2s" and %4 yields "3s" */
/* $1-$4 are ordinals (for combinations like "1st man"), with a similar pattern */
/* #1,#2 produce either sides or heads if active==1 then #1 is heads */
/* #3,#4 produce either Sides or Heads if active==2 then #1 is Sides */
/* #5,#6 produce either side or head if active==1 then #1 is head */
/* #3,#4 produce either Side or Head if active==2 then #1 is Side */

static int doctorName(char *buffer, int blen, char *name, int active ) {
    char *bpt, *bend, *pt;

    if ( name==NULL ) {
	buffer[0]='\0';
return( 0 );
    }
    if ( active==0 ) {
	strncpy(buffer,name,blen);
return( 0 );
    }

    bpt = buffer;
    bend = bpt + blen-1;
    pt = name;
    while ( *pt!='\0' ) {
	if ( bpt>=bend ) {
	    fprintf( stderr, "Call '%s' is too long for buffer %d\n", name, blen );
	    *bpt = '\0';
return( -1 );
	}
	if ((( *pt=='%' || *pt=='$') &&
		(pt[1]=='1' || pt[1]=='2' || pt[1]=='3' || pt[1]=='4')) ||
		(*pt =='#' && (pt[1]>='1' && pt[1]<='8')) ) {
	    int off = pt[1]-'1';
	    char **nameset, *name;
	    if ( *pt=='%' )
		nameset = coupleNames;
	    else if ( *pt=='$' )
		nameset = posNames;
	    else if ( *pt=='#' && off<2 )
		nameset = sidesheads;
	    else if ( *pt=='#' && off<4 ) {
		nameset = SidesHeads;
		off -= 2;
	    } else if ( *pt=='#' && off<6 ) {
		nameset = sidehead;
		off -= 4;
	    } else if ( *pt=='#' && off<8 ) {
		nameset = SideHead;
		off -= 6;
	    }
	    name = nameset[(off+active-1)%4];
	    while ( *name ) {
		if ( bpt>=bend ) {
		    fprintf( stderr, "Call '%s' is too long for buffer %d\n", name, blen );
		    *bpt = '\0';
return( -1 );
		}
		*bpt++ = *name++;
	    }
	    pt += 2;
	} else {
	    *bpt++ = *pt++;
	}
    }
    *bpt = '\0';
return( 0 );
}

static void setCalls(Dance *dance) {
    int active;
    Dance *figure;
    int i,j;
    char buffer[500];

    if ( dance->sequence==NULL )
return;
    if ( dance->choreo_pos>=dance->choreo_cnt )
return;

    active = dance->sequence[dance->choreo_pos].active;
    figure = dance->sequence[dance->choreo_pos].figure;

    figureFreeCalls(dance);
    dance->call_cnt = figure->call_cnt;
    dance->calls = calloc(figure->call_cnt+1,sizeof(CallList));
    for ( i=0; i<dance->call_cnt; ++i ) {
	CallList *from = &figure->calls[i];
	CallList *to   = &dance ->calls[i];
	memcpy(to,from,sizeof(CallList));
	if ( from->claplist_millibars!=NULL ) {
	    for ( j=0; from->claplist_millibars[j]!=CLAP_DONE; ++j );
	    ++j;
	    to->claplist_millibars = malloc( j*sizeof(unsigned short));
	    memcpy(to->claplist_millibars,from->claplist_millibars,j*sizeof(unsigned short));
	}
	if ( from->callName!=NULL ) {
	    doctorName(buffer, sizeof(buffer), from->callName, active );
	    to->callName = strdup(buffer);
	}
	if ( from->audioFilename!=NULL ) {
	    doctorName(buffer, sizeof(buffer), from->audioFilename, active );
	    to->audioFilename = strdup(buffer);
	}
	if ( from->longaudioFilename!=NULL ) {
	    doctorName(buffer, sizeof(buffer), from->longaudioFilename, active );
	    to->longaudioFilename = strdup(buffer);
	}
    }
}

static void setRoles(Dance *dance) {
    int active;
    Dance *figure;
    int i,j,h;
    int hand_offset, degrees;
    double c,s;
    Position *lastPoint = NULL;

    if ( dance->sequence==NULL )
return;
    if ( dance->choreo_pos>=dance->choreo_cnt )
return;

    active = dance->sequence[dance->choreo_pos].active;
    figure = dance->sequence[dance->choreo_pos].figure;

    if ( dance->roles!=NULL ) {
	for ( i=0; i<dance->role_cnt; ++i )
	    peopleFreeContents(&dance->roles[i]);
	free( dance->roles );
	dance->roles = NULL;
	dance->role_cnt = 0;
    }

    if ( figure->roles!=NULL ) {
	dance->roles = malloc(figure->role_cnt*sizeof(Person));
	memcpy(dance->roles,figure->roles,figure->role_cnt*sizeof(Person));
	dance->role_cnt = figure->role_cnt;
	if ( active==0 || active==1 ) {
	    /* Moves of each role are natural */
	    for ( i=0; i<figure->role_cnt; ++i ) {
		Person *from = &figure->roles[i];
		Person *to   = & dance->roles[i];
		Block *b = blockCopy(&from->figures);
		memcpy(&to->figures,b,sizeof(*b));
		free(b);
	    }
	} else {
	    int centerx=372, centery=372;
	    /* These transformations only apply to squaredances */
	    assert( dance->role_cnt==8);
	    assert(figure->role_cnt==8);
	    /* There are only 4 couples */
	    assert(active>0);
	    assert(active<=4);

	    centerx = (dance->roles[0].figures.moves[0].endPoint.x + dance->roles[4].figures.moves[0].endPoint.x )/2;
	    centery = (dance->roles[0].figures.moves[0].endPoint.y + dance->roles[1].figures.moves[0].endPoint.y )/2;

	    hand_offset = (active-1)*2;
	    if ( dance->sequence[dance->choreo_pos].clockwise ) {
		degrees = (active-1)*-90;
		switch ( active ) {
		  case 2:
		    c=0.0; s=-1.0;
		  break;
		  case 3:
		    c=-1.0; s=0.0;
		  break;
		  case 4:
		    c=0.0; s=1.0;
		  break;
		}
	    } else {
		degrees = (active-1)*90;
		switch ( active ) {
		  case 2:
		    c=0.0; s=1.0;
		  break;
		  case 3:
		    c=-1.0; s=0.0;
		  break;
		  case 4:
		    c=0.0; s=-1.0;
		  break;
		}
	    }
	    /* if the actives are the 2s, then they need to grab the moves of */
	    /*  the ones, rotate them by 90 degrees and add 2 to every hand */
	    /*  clasp, while the 1s grab the moves of the 4s, the 4s get 3s and so on */
	    /* if the actives are 3s then they need to moves of the 1s rotated */
	    /*  by 180 with 4 added to every hand clasp... */
	    /* This code is very specific for square dancing */
	    /* it knows where the center of the set it. */
	    /* it knows there will be no refernces to other squares */
	    /* it knows there will be no conditions */
	    /* all those complexities of the danceParseRoleCopy are omitted */
/* BUG!!! This doesn't handle <if> blocks in the move lists. So far no square dances use them */
	    for ( i=0; i<figure->role_cnt; ++i ) {
		Person *from = &figure->roles[(i+8-hand_offset)%8];
		Person *to   = & dance->roles[i];
		to->figures.move_cnt = from->figures.move_cnt;
		to->figures.move_max = from->figures.move_max;
		to->figures.moves    = malloc(to->figures.move_max*sizeof(FigureSegment));
		memcpy(to->figures.moves,from->figures.moves,to->figures.move_max*sizeof(FigureSegment));
		lastPoint = NULL;
		for ( j=0; j<to->figures.move_cnt; ++j ) {
		    FigureSegment *cs = &to->figures.moves[j];
		    if ( (cs->cmd==c_moveto || cs->cmd==c_lineto || cs->cmd==c_curveto ) && !cs->position_absolute ) {
			double x = centerx + c*(cs->endPoint.x-centerx) - s*(cs->endPoint.y-centery);
			double y = centery + s*(cs->endPoint.x-centerx) + c*(cs->endPoint.y-centery);
			cs->endPoint.x = round(2*x)/2; cs->endPoint.y = round(2*y)/2;
			if ( cs->cmd==c_curveto ) {
			    x = centerx + c*(cs->cp1.x-centerx) - s*(cs->cp1.y-centery);
			    y = centery + s*(cs->cp1.x-centerx) + c*(cs->cp1.y-centery);
			    cs->cp1.x = round(2*x)/2; cs->cp1.y = round(2*y)/2;
			    x = centerx + c*(cs->cp2.x-centerx) - s*(cs->cp2.y-centery);
			    y = centery + s*(cs->cp2.x-centerx) + c*(cs->cp2.y-centery);
			    cs->cp2.x = round(2*x)/2; cs->cp2.y = round(2*y)/2;
			}
			if ( cs->cmd!=c_moveto )
			    splineMake(cs,lastPoint);
			lastPoint = &cs->endPoint;
		    }
		    if ( cs->cmd==c_moveto || cs->orient==mo_rotateto ||
			    cs->orient==mo_cw_rotateto || cs->orient==mo_ccw_rotateto ) {
			if ( cs->degrees!=DEGREES_END ) {
			    cs->degrees += degrees;
			    if ( cs->degrees>=360 )
				cs->degrees -= 360;
			}
		    }
		    if ( (cs->join_right_hand&HAND_INDIVIDUAL_MASK)==HAND_STAR ||
			    (cs->join_left_hand&HAND_INDIVIDUAL_MASK)==HAND_STAR ) {
			double x = centerx + c*(cs->star_center.x-centerx) - s*(cs->star_center.y-centery);
			double y = centery + s*(cs->star_center.x-centerx) + c*(cs->star_center.y-centery);
			cs->star_center.x = round(x);
			cs->star_center.y = round(y);
		    }

		    /* And now the hand clasps */
		    if ( cs->join_right_hand!=0 /* && (cs->join_right_hand&HAND_MINORSET_OFFSET)==0 */ ) {
			h = cs->join_right_hand & HAND_INDIVIDUAL_MASK;
			if ( h!=HAND_STAR )
			    cs->join_right_hand = ( (h+hand_offset) % 8 ) |
				    (cs->join_right_hand & ~HAND_INDIVIDUAL_MASK);
		    }
		    if ( cs->join_left_hand!=0 /* && (cs->join_left_hand&HAND_MINORSET_OFFSET)==0 */ ) {
			h = cs->join_left_hand & HAND_INDIVIDUAL_MASK;
			if ( h!=HAND_STAR )
			    cs->join_left_hand = ( (h+hand_offset) % 8 ) |
				    (cs->join_left_hand & ~HAND_INDIVIDUAL_MASK);
		    }
		}
	    }
	}
	for ( i=0; i<figure->role_cnt; ++i ) {
	    Person *to   = & dance->roles[i];
	    for ( j=0; j<to->figures.move_cnt; ++j ) {
		to->figures.moves[j].levitation = valueChangeCopy(to->figures.moves[j].levitation);
		to->figures.moves[j].outness    = valueChangeCopy(to->figures.moves[j].outness   );
	    }
	}
    }
}

int danceAdvanceChoreography(Dance *dance) {
    int part;
    int ret;
    Dance *figure;

    if ( dance->choreo_cnt==0 || dance->sequence==NULL )
return( -1 );		/* Should not have been called */

    ret = 1;
    if ( dance->choreo_pos >= dance->choreo_cnt )
	ret = 0;	/* Finished */
    else {
	figure = dance->sequence[dance->choreo_pos].figure;
	if ( dance->formation!=figure->formation )
	    danceFixupFormation(dance,figure->formation,figure);
	dance->formation   = figure->formation;
	dance->progression = figure->progression;
	dance->old_style_progression = figure->old_style_progression;
	dance->part_cnt = figure->part_cnt;
	if ( dance->sequence[dance->choreo_pos].first_set )
	    dance->first_minor_set = true;
	if ( dance->sequence[dance->choreo_pos].no_one_out_at_top ) {
	    if ( figure->formation==sp_dupleMinor || figure->formation==sp_tripleMinor ) {
		danceForceNoOneOutAtTop(dance);
	    } else
		fprintf( stderr, "Use of 'no_one_out_at_top' when not in duple/triple minor, Ignored. (%s)\n", figure->name );
	}
    }
    part = dance->sequence[dance->choreo_pos].part;
    if ( part>0 ) {
	dance->current_part = part-1;
	if ( dance->current_part>dance->part_cnt )
	    dance->current_part = 0;
    }
    if ( dance->sequence[dance->choreo_pos].current_ms!=-1 )
	dance->current_minorset = dance->sequence[dance->choreo_pos].current_ms;

    setCalls(dance);
    setRoles(dance);
    if ( dance->sequence[dance->choreo_pos].time_scale!=0 && dance->sequence[dance->choreo_pos].time_scale!=1 ) {
	int i,j;
	float ts = dance->sequence[dance->choreo_pos].time_scale;
	struct transformation trans;

	for ( i=0; i<dance->call_cnt; ++i ) {
	    dance->calls[i].millibars = ts*dance->calls[i].millibars;
	    if ( dance->calls[i].claplist_millibars!=NULL )
		for ( j=0; dance->calls[i].claplist_millibars[j]!=CLAP_DONE; ++j )
		    dance->calls[i].claplist_millibars[j] *= ts;
	}
	memset(&trans,0,sizeof(trans));
	trans.timescale = ts;
	trans.hand_mod  = 400;
	trans.xscale = trans.yscale = 1;
	memset(trans.handmap,-1,sizeof(trans.handmap));
	for ( i=0; i<dance->role_cnt; ++i )
	    danceBlockTransform(&dance->roles[i].figures,&trans);
    }
    ++(dance->choreo_pos);
return( ret );
}

int danceAllComponentsSameBars(Dance *dance) {
    int i;
    int danceSetMillibars;

    if ( dance->comp_cnt==0 )
return( true );

    danceSetMillibars = danceMinorSetMillibarCount(dance->components[0]);
    for ( i=1; i<dance->comp_cnt; ++i ) {
	if ( danceSetMillibars != danceMinorSetMillibarCount(dance->components[i]) )
return( false );
    }
return( true );
}
