#include "squaredance.h"
#include "figures.h"

struct figureCheck {
    int styleMask;
    char *phrase1;
    char *phrase2;
    char *phrase3;
    char *butNot1;
    char *butNot2;
    int index;
} figures[] = {
/* I use "turn" in "lead down, turn, lead back" */
    { -1, "turn", "right", NULL, "turn single", NULL, fig_turn },
    { -1, "turn", "left", NULL, "turn single", NULL, fig_turn },
    { -1, "turn", "two", NULL, "turn single", NULL, fig_turn },
    { (1<<ds_square)|(1<<ds_contra)|(1<<ds_acd), "allemande", NULL, NULL, NULL, NULL, fig_turn },
    { -1, "set", NULL, NULL, NULL, NULL, fig_set },
    { -1, "turn single", NULL, NULL, NULL, NULL, fig_turnSingle },
    { -1, "turn single", "cloverleaf", NULL, NULL, NULL, fig_turnSingleCloverleaf },
    { -1, "gypsy", NULL, NULL, NULL, NULL, fig_gypsy },
    { -1, "shoulder round", NULL, NULL, NULL, NULL, fig_gypsy },
    { -1, "circle", NULL, NULL, NULL, NULL, fig_circle },
    { -1, "cast", NULL, NULL, NULL, NULL, fig_cast },
    { -1, "lead", NULL, NULL, NULL, NULL, fig_lead },
    { -1, "figure eight", NULL, NULL, NULL, NULL, fig_figureEight },
    { -1, "figure 8", NULL, NULL, NULL, NULL, fig_figureEight },
    { -1, "figure eight", "double", NULL, NULL, NULL, fig_doubleFigureEight },
    { -1, "figure 8", "double", NULL, NULL, NULL, fig_doubleFigureEight },
    { -1, "hands across", NULL, NULL, NULL, NULL, fig_handsAcross },
    { -1, "star", "right", NULL, NULL, NULL, fig_handsAcross },
    { -1, "star", "left", NULL, NULL, NULL, fig_handsAcross },
    { -1, "ladies chain", "open", NULL, NULL, NULL, fig_openLadiesChain },
    { -1, "ladies' chain", "open", NULL, NULL, NULL, fig_openLadiesChain },
    { 1<<ds_scottish, "ladies chain", NULL, NULL, NULL, NULL, fig_openLadiesChain },
    { 1<<ds_scottish, "ladies' chain", NULL, NULL, NULL, NULL, fig_openLadiesChain },
    { -1, "ladies chain", "closed", NULL, NULL, NULL, fig_squareLadiesChain },
    { -1, "ladies' chain", "closed", NULL, NULL, NULL, fig_squareLadiesChain },
    { (1<<ds_square)|(1<<ds_contra), "ladies chain", NULL, NULL, NULL, NULL, fig_squareLadiesChain },
    { (1<<ds_square)|(1<<ds_contra), "ladies' chain", NULL, NULL, NULL, NULL, fig_squareLadiesChain },
    { ~(1<<ds_scottish), "poussette", NULL, NULL, "draw", NULL, fig_poussette },
    { -1, "poussette", "draw", NULL, NULL, NULL, fig_drawPoussette },
    { -1, "chevron", NULL, NULL, "poussette", NULL, fig_chevron },
    { -1, "chevron", "poussette", NULL, NULL, NULL, fig_chevronPoussette },
    { -1, "hey", NULL, NULL, NULL, NULL, fig_hey },
    { -1, "hey", "mirror", NULL, NULL, NULL, fig_mirrorHey },
/*
    { -1, "rights and lefts", NULL, NULL, NULL, NULL, fig_hey },
    { -1, "lefts and rights", NULL, NULL, NULL, NULL, fig_hey },
    { -1, "right and left", "grand", NULL, NULL, NULL, fig_hey },
    { -1, "chain", "grand", NULL, NULL, NULL, fig_hey },
*/
    { -1, "hey", "parallel", NULL, NULL, NULL, fig_parallelHey },
    { -1, "hey", "interwoven", NULL, NULL, NULL, fig_interwovenHey },
    { (1<<ds_square), "weave", NULL, NULL, NULL, NULL, fig_hey },
    { -1, "hey", "progressive", NULL, NULL, NULL, fig_progressiveHey },
    { -1, "hey", "for three", NULL, NULL, NULL, fig_heyFor3 },
    { -1, "hey", "for four", NULL, NULL, NULL, fig_heyFor4 },
    { -1, "hey", "dolphin", NULL, NULL, NULL, fig_dolphinHey },
    { -1, "hey", "tandem", NULL, NULL, NULL, fig_tandemHey },
    { -1, "hey", "shetland", NULL, NULL, NULL, fig_tandemHey },
    { -1, "hey", "circular", NULL, NULL, NULL, fig_circularHey },
    { -1, "rights and lefts", NULL, NULL, NULL, NULL, fig_rightsAndLefts },
    { -1, "lefts and rights", NULL, NULL, NULL, NULL, fig_rightsAndLefts },
    { -1, "right and left", NULL, NULL, "four", "through", fig_rightsAndLefts },
    { -1, "chain", "grand", NULL, NULL, NULL, fig_rightsAndLefts },
    { (1<<ds_square)|(1<<ds_contra)|(1<<ds_acd), "square through", NULL, NULL, NULL, NULL, fig_rightsAndLefts },
    { -1, "hey", "schiehallion", NULL, NULL, NULL, fig_schiehallionReel },

    { -1, "reel", NULL, NULL, NULL, NULL, fig_hey },
    { -1, "reel", "mirror", NULL, NULL, NULL, fig_mirrorHey },
    { -1, "reel", "parallel", NULL, NULL, NULL, fig_parallelHey },
    { -1, "reel", "interwoven", NULL, NULL, NULL, fig_interwovenHey },
    { -1, "reel", "progressive", NULL, NULL, NULL, fig_progressiveHey },
    { -1, "reel", "for three", NULL, NULL, NULL, fig_heyFor3 },
    { -1, "reel", "for four", NULL, NULL, NULL, fig_heyFor4 },
    { -1, "reel", "dolphin", NULL, NULL, NULL, fig_dolphinHey },
    { -1, "reel", "tandem", NULL, NULL, NULL, fig_tandemHey },
    { -1, "reel", "shetland", NULL, NULL, NULL, fig_tandemHey },
    { -1, "reel", "circular", NULL, NULL, NULL, fig_circularHey },
    { -1, "reel", "schiehallion", NULL, NULL, NULL, fig_schiehallionReel },
    { -1, "reel", "courage", NULL, NULL, NULL, fig_courageReel },

    { -1, "cast and lead", NULL, NULL, NULL, NULL, fig_castAndLead },
    { -1, "cast down and lead", NULL, NULL, NULL, NULL, fig_castAndLead },
    { -1, "cast up and lead", NULL, NULL, NULL, NULL, fig_castAndLead },
    { -1, "lead and cast", NULL, NULL, NULL, NULL, fig_leadAndCast },
    { -1, "lead down and cast", NULL, NULL, NULL, NULL, fig_leadAndCast },
    { -1, "lead up and cast", NULL, NULL, NULL, NULL, fig_leadAndCast },
    { -1, "lead above", "and cast", NULL, NULL, NULL, fig_leadAndCast },
    { -1, "lead below", "and cast", NULL, NULL, NULL, fig_leadAndCast },
    { -1, "mad robin", NULL, NULL, NULL, NULL, fig_madRobin },
    { -1, "california twirl", NULL, NULL, NULL, NULL, fig_californiaTwirl },
    { -1, "box the gnat", NULL, NULL, NULL, NULL, fig_boxTheGnat },
    { -1, "swat the flea", NULL, NULL, NULL, NULL, fig_swatTheFlea },
    { -1, "grand square", NULL, NULL, NULL, NULL, fig_grandSquare },
    { -1, "set & link", NULL, NULL, NULL, NULL, fig_setAndLink },
    { -1, "set and link", NULL, NULL, NULL, NULL, fig_setAndLink },
    { -1, "set & rotate", NULL, NULL, NULL, NULL, fig_setAndRotate },
    { -1, "set and rotate", NULL, NULL, NULL, NULL, fig_setAndRotate },
    { -1, "set & turn", "corner", NULL, NULL, NULL, fig_setAndTurn },
    { -1, "set and turn", "corner", NULL, NULL, NULL, fig_setAndTurn },
    { -1, "bentley allemande", NULL, NULL, NULL, NULL, fig_bentleyAllemande },
    { -1, "allemande", "scottish", NULL, NULL, NULL, fig_scottishAllemande },
    { (1<<ds_scottish), "allemande", NULL, NULL, NULL, NULL, fig_scottishAllemande },
    { -1, "promenade", "scottish", NULL, NULL, NULL, fig_scottishPromenade },
    { -1, "promenade", "three couple", NULL, NULL, NULL, fig_scottishPromenade },
    { (1<<ds_scottish), "promenade", NULL, NULL, NULL, NULL, fig_scottishPromenade },
    { (1<<ds_scottish), "knot", NULL, NULL, NULL, NULL, fig_scottishKnot },
    { -1, "poussette", "diamond", NULL, NULL, NULL, fig_diamondPoussette },
    { -1, "poussette", "strathspey", NULL, NULL, NULL, fig_diamondPoussette },
    { ((1<<ds_contra)|(1<<ds_square)|(1<<ds_acd)), "ladies chain", NULL, NULL, NULL, NULL, fig_squareLadiesChain },
    { ((1<<ds_contra)|(1<<ds_square)|(1<<ds_acd)), "right and left through", NULL, NULL, NULL, NULL, fig_rightAndLeftThrough },
    { -1, "bend the line", NULL, NULL, NULL, NULL, fig_bendTheLine },
    { -1, "step to a wave", NULL, NULL, NULL, NULL, fig_oceanWave },
    { -1, "step to an ocean wave", NULL, NULL, NULL, NULL, fig_oceanWave },
    { -1, "pass through to an ocean wave", NULL, NULL, NULL, NULL, fig_oceanWave },
    { -1, "pass the ocean", NULL, NULL, NULL, NULL, fig_oceanWave },
    { -1, "swing through", NULL, NULL, NULL, NULL, fig_swingThrough },
    { -1, "petronella", NULL, NULL, NULL, NULL, fig_petronella },
    { -1, "star promenade", NULL, NULL, NULL, NULL, fig_starPromenade },
    { -1, "butterfly whirl", NULL, NULL, NULL, NULL, fig_butterflyWhirl },
    { -1, "long lines forward and back", NULL, NULL, NULL, NULL, fig_longLinesForwardAndBack },
    { -1, "allemande", "regency", NULL, NULL, NULL, fig_regencyAllemande },
    { -1, "kiss", NULL, NULL, NULL, NULL, fig_kiss },
    { (1<<ds_english), "salute", NULL, NULL, NULL, NULL, fig_kiss },
    { -1, "swing corners", NULL, NULL, NULL, NULL, fig_swingCorners },
    { -1, "lead out through the sides", NULL, NULL, NULL, NULL, fig_leadOutSides },
    { -1, "lead out to the sides", NULL, NULL, NULL, NULL, fig_leadOutSides },
    { -1, "lead out the sides", NULL, NULL, NULL, NULL, fig_leadOutSides },
    { -1, "lead out sides", NULL, NULL, NULL, NULL, fig_leadOutSides },
    { -1, "lead out mens side", NULL, NULL, "set", NULL, fig_leadOutSides },
    { -1, "lead out mens' side", NULL, NULL, "set", NULL, fig_leadOutSides },
    { -1, "lead out womens side", NULL, NULL, "set", NULL, fig_leadOutSides },
    { -1, "lead out womens' side", NULL, NULL, "set", NULL, fig_leadOutSides },
    { ((1<<ds_contra)|(1<<ds_square)|(1<<ds_acd)), "swing", NULL, NULL, "through", NULL, fig_swing },
    { (1<<ds_english), "swing", NULL, NULL, "corners", NULL, fig_swing },
    { -1, "waltz", NULL, NULL, NULL, NULL, fig_waltz },
    { (1<<ds_english), "side", NULL, NULL, NULL, NULL, fig_siding },
    { (1<<ds_english), "siding", NULL, NULL, NULL, NULL, fig_siding },
    { (1<<ds_english), "arm", NULL, NULL, NULL, NULL, fig_arming },
    { (1<<ds_english), "arming", NULL, NULL, NULL, NULL, fig_arming },

    { -1, "right and left four", NULL, NULL, "four", NULL, fig_rightAndLeft4 },
    { -1, "right and left 4", NULL, NULL, "four", NULL, fig_rightAndLeft4 },
    { -1, "polka", NULL, NULL, "four", NULL, fig_polka },
    { -1, "cross go", "below", NULL, NULL, NULL, fig_crossGoBelow },
    { -1, "cross go", "above", NULL, NULL, NULL, fig_crossGoBelow },
    { -1, "cross cast", NULL, NULL, NULL, NULL, fig_crossGoBelow },
    { -1, "cross and cast", NULL, NULL, NULL, NULL, fig_crossGoBelow },
    { -1, "down the outside", "take a peek", NULL, NULL, NULL, fig_downTheOutsideBack },
    { -1, "down", "take a peek", NULL, NULL, NULL, fig_downTheOutsideBack },
    { -1, "lead down turn lead up", NULL, NULL, NULL, NULL, fig_downTheMiddleBack },
    { -1, "lead down, turn alone, return", NULL, NULL, NULL, NULL, fig_downTheMiddleBack },
    { -1, "lead down turn lead back", NULL, NULL, NULL, NULL, fig_downTheMiddleBack },
    { -1, "lead down and up", NULL, NULL, NULL, NULL, fig_downTheMiddleBack },
    { -1, "lead down and back", NULL, NULL, NULL, NULL, fig_downTheMiddleBack },
    { -1, "lead down the middle and back", NULL, NULL, NULL, NULL, fig_downTheMiddleBack },
    { -1, "espagnol", NULL, NULL, NULL, NULL, fig_espagnol },
    { -1, "rondel", NULL, NULL, NULL, NULL, fig_rondel },
    { -1, "double triangle", NULL, NULL, NULL, NULL, fig_doubleTriangle },
    { -1, "hello", "goodbye", NULL, NULL, NULL, fig_helloGoodbye },
    { -1, "hello", "good-bye", NULL, NULL, NULL, fig_helloGoodbye },
    { -1, "snake pass", NULL, NULL, NULL, NULL, fig_snakePass },
    { -1, "contra corners", NULL, NULL, NULL, NULL, fig_contraCorners },
    { -1, "turn corners", NULL, NULL, "set", NULL, fig_contraCorners },
    { -1, "turn contrary corners", NULL, NULL, "set", NULL, fig_contraCorners },
    { -1, "corner partner corner partner", NULL, NULL, "set", NULL, fig_contraCorners },
    { -1, "back to back", NULL, NULL, NULL, NULL, fig_back2back },
    { -1, "dosado", NULL, NULL, NULL, NULL, fig_back2back },
    { -1, "do-si-do", NULL, NULL, NULL, NULL, fig_back2back },
    { -1, "do si do", NULL, NULL, NULL, NULL, fig_back2back },
    { -1, "dos-à-dos", NULL, NULL, NULL, NULL, fig_back2back },
    { -1, "dos à dos", NULL, NULL, NULL, NULL, fig_back2back },
    { -1, "face to face", NULL, NULL, NULL, NULL, fig_face2face },
    { -1, "face à face", NULL, NULL, NULL, NULL, fig_face2face },
    { -1, "face-à-face", NULL, NULL, NULL, NULL, fig_face2face },
    {0}
};

static int hasName(char *haystack, char *needle) {
    int len;
    char *pt, *end;

    if ( needle==NULL )
return( true );
    if ( haystack==NULL )
return( false );

    len = strlen( needle );
    end = haystack + strlen( haystack )-len;
    for ( pt=haystack; pt<=end; ++pt ) {
	if ( pt!=haystack && isalpha(pt[-1] ))
    continue;
	if ( pt+len<end && pt[len]=='s' && !isalpha(pt[len+1]) ) {
	    /* I want "mirror hey" to match "mirror heys" */
	} else if ( pt+len<end && isalpha(pt[len]))
    continue;
	if ( strncasecmp(pt,needle,len)==0 )
return( true );
    }
return( false );
}

static int FigureNameMatch(char *name,struct figureCheck *figure) {
return( hasName(name,figure->phrase1) &&
	hasName(name,figure->phrase2) &&
	hasName(name,figure->phrase3) &&
	(figure->butNot1==NULL ||
		!hasName(name,figure->butNot1)) &&
	(figure->butNot2==NULL ||
		!hasName(name,figure->butNot2)) );
}

static int CLNameMatch(CallList *cs,struct figureCheck *figure) {
return( FigureNameMatch(cs->callName,figure) ||
	    FigureNameMatch(cs->audioFilename,figure) );
}

void figureMask(Dance *dance,unsigned int masks[FIG_INT_CNT]) {
    int i,j;
    CallList *cs, *csEnd;
    unsigned int submasks[FIG_INT_CNT];
    int danceStyleMask = (1<<dance->style);

    for ( i=0; i<FIG_INT_CNT; ++i )
	masks[i] = 0;
    cs = dance->calls;
    csEnd = cs + dance->call_cnt;
    while ( cs<csEnd ) {
	for ( j=0; figures[j].styleMask!=0 ; ++j ) {
	    if ( (figures[j].styleMask & danceStyleMask) &&
		    CLNameMatch(cs,&figures[j])) {
		masks[figures[j].index/32] |= (1<<(figures[j].index&31));
	    }
	}
	++cs;
    }

    if ( dance->choreo_cnt!=0 ) {
	for ( j=0; j<dance->choreo_cnt; ++j ) {
	    figureMask(dance->sequence[j].figure,submasks );
	    for ( i=0; i<FIG_INT_CNT; ++i )
		masks[i] |= submasks[i];
	}
    }
    if ( dance->tags!=NULL && danceHasTag(dance,"USA"))
	masks[fig_USA/32] |= (1<<(fig_USA&31));
}

struct figureCheck starFigures[] = {
    { -1, "hands across", NULL, NULL, NULL, NULL, fig_handsAcross },
    { -1, "star", "right", NULL, NULL, NULL, fig_handsAcross },
    { -1, "star", "left", NULL, NULL, NULL, fig_handsAcross },
    { 0 }
};

struct figureCheck starBackFigures[] = {
    { -1, "hands across", NULL, NULL, NULL, NULL, fig_handsAcross },
    { -1, "star", "right", NULL, NULL, NULL, fig_handsAcross },
    { -1, "star", "left", NULL, NULL, NULL, fig_handsAcross },
    { -1, "left hands back", NULL, NULL, NULL, NULL, fig_handsAcross },
    { -1, "right hands back", NULL, NULL, NULL, NULL, fig_handsAcross },
    { 0 }
};

int danceHasStarBack(Dance *dance) {
    int j,k;
    CallList *cs, *cs2, *csEnd;
    int wasRight;
    int danceStyleMask = (1<<dance->style);

    cs = dance->calls;
    csEnd = cs + dance->call_cnt;
    while ( cs<csEnd ) {
	for ( j=0; starFigures[j].styleMask!=0 ; ++j ) {
	    if ( ((starFigures[j].styleMask & danceStyleMask)) &&
		    CLNameMatch(cs,&starFigures[j]) ) {
		wasRight = hasName(cs->callName,"right");
		for ( cs2=cs+1 ; cs2<csEnd && cs2<=cs+2; ++cs2 ) {
		    for ( k=0; starBackFigures[k].styleMask!=0 ; ++k ) {
			if ( ((starBackFigures[k].styleMask & danceStyleMask)) &&
				CLNameMatch(cs,&starBackFigures[k]) ) {
			    if ( wasRight && hasName(cs->callName,"left"))
return( true );
			    if ( !wasRight && hasName(cs->callName,"right"))
return( true );
			}
		    }
		}
	    }
	}
	++cs;
    }

    if ( dance->choreo_cnt!=0 ) {
	for ( j=0; j<dance->choreo_cnt; ++j ) {
	    if ( danceHasStarBack(dance->sequence[j].figure ) )
return( true );
	}
    }
return( false );
}
