#include "squaredance.h"
#include "views.h"		/* Need this to calculate counts per minute */
#include "figures.h"

void danceOutputHeader(FILE *out, enum danceStyle style, enum subStyle substyle ) {
    fprintf( out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
    fprintf( out, "<dances" );
    if ( style!=ds_unspecified ) {
	fprintf( out, " style=\"%s\"", styleNames[style] );
	if ( style==ds_square && substyle!=ss_unspecified )
	    fprintf( out, " sub-style=\"%s\"", substyleNames[substyle] );
    }
    fprintf( out, ">\n" );
}

void danceOutputTrailer(FILE *out) {
    fprintf( out, "</dances>\n" );
}

static char *formatFloat(char *buffer, double val ) {
    if ( round(val)==val )
	sprintf( buffer, "%d", (int) val );
    else if ( 10*val-round(10*val)<.0001 && 10*val-round(10*val)>-.0001 )
	sprintf( buffer, "%.1f", val );
    else
	sprintf( buffer, "%.2f", val );
return( buffer );
}

static char *formatMillibars(char *buffer, int mb ) {
    if ( 1000*round(mb/1000.0)==mb )
	sprintf( buffer, "%d", mb/1000 );
    else if ( mb-100*round(mb/100.0)<.001 && mb-100*round(mb/100.0)>-.001 )
	sprintf( buffer, "%.1f", mb/1000.0 );
    else if ( mb-10*round(mb/10.0)<.001 && mb-10*round(mb/10.0)>-.001 )
	sprintf( buffer, "%.2f", mb/1000.0 );
    else
	sprintf( buffer, "%.3f", mb/1000.0 );
return( buffer );
}


void entityOutput(FILE *out, char *str, int donl ) {
    while ( *str ) {
	if ( *str=='&' )
	    fputs("&amp;",out);
	else if ( *str=='"' )
	    fputs("&quot;",out);
	else if ( *str=='<' )
	    fputs("&lt;",out);
	else if ( *str=='>' )
	    fputs("&gt;",out);
	else if ( *str=='\n' && donl==1 )
	    fputs("&#10;",out);
	else if ( *str=='\n' && donl==2 )
	    fputs("<br/>",out);
	else
	    fputc(*str,out);
	++str;
    }
}

static void danceOutputPartMask(FILE *out, unsigned int part_mask ) {
    int part, first;

    if ( part_mask==(unsigned int) -1 )
return;
    if ( part_mask==0 )		/* Happened once by mistake */
return;
    fprintf( out, " part=\"" );
    for ( part=0, first=1; part<32; ++part ) {
	if ( part_mask&(1<<part) ) {
	    if ( !first )
		fputc(',',out);
	    fprintf( out, "%d", part+1 );
	    first = 0;
	}
    }
    fputc('\"',out);
}

static void danceOutputACallList(FILE *out,CallList *cl, int part_mask, int indent ) {
    char buf[20], buf2[20];
    int i;

    for ( i=0; i<indent; ++i )
	putc(' ',out);
    fprintf( out, "<call-name name=\"" );
    entityOutput(out, cl->callName, true );
    fputc('\"',out);
    danceOutputPartMask(out, part_mask );
    if ( cl->musicName[0]!='\0' )
	fprintf( out, " music=\"%s\"", cl->musicName );
    if ( cl->audioFilename!=NULL )
	fprintf( out, " audio=\"%s\"", cl->audioFilename );
    if ( cl->longaudioFilename!=NULL )
	fprintf( out, " longaudio=\"%s\"", cl->longaudioFilename );
    if ( cl->claplist_millibars!=NULL && cl->claplist_millibars[0]!=CLAP_DONE ) {
	int i;
	if ( cl->snap_not_clap )
	    fprintf( out, " snap=\"" );
	else if ( cl->stamp_not_clap )
	    fprintf( out, " stamp=\"" );
	else
	    fprintf( out, " clap=\"" );
	for ( i=0; cl->claplist_millibars[i]!=CLAP_DONE; ++i )
	    fprintf( out, "%s%s", i>0? "," : "", formatMillibars(buf,cl->claplist_millibars[i]) );
	putc( '\"',out );
    }
    if ( cl->honour_end!=0 ) {
	fprintf( out, " honour-start=\"%s\" honour-end=\"%s\"",
		formatMillibars(buf,cl->honour_start),
		formatMillibars(buf2,cl->honour_end) );
    }
    if ( cl->offset_millisecs )
	fprintf( out, " offset-sec=\"%s\" ", formatMillibars(buf,cl->offset_millisecs));
    if ( cl->millibars!=cl->audio_millibars )
	fprintf( out, " audio-bar=\"%s\" ", formatMillibars(buf,cl->audio_millibars));
    if ( cl->millibars!=cl->actual_millibars )
	fprintf( out, " actual-bar=\"%s\" ", formatMillibars(buf,cl->actual_millibars));
    fprintf( out, " bar=\"%s\" />\n", formatMillibars(buf,cl->millibars));
}

static void danceOutputMaskToCondition(FILE *out, unsigned int mask, int condition) {
    int first, part;
    
    if ( condition==if_always && mask==(unsigned int) -1 ) {
	fprintf( stderr, "Attempt to output something that is always true. Shouldn't happen.\n" );
    } else if ( condition==if_always ) {
	first = true;
	for ( part=0; part<32; ++part ) {
	    if ( (1<<part)&mask ) {
		if ( !first )
		    fprintf( out, " || " );
		first=0;
		if ( (1<<part)&FIRST_MASK )
		    fprintf( out, "first" );
		else if ( (1<<part)&LAST_MASK )
		    fprintf( out, "last" );
		else
		    fprintf( out, "part(%d)", part+1 );
	    }
	}
    } else if ( mask==(unsigned int) -1 ) {
	switch ( (enum condition) condition ) {
	  case if_always:
	    /* Can't happen */;
	  break;
	  case if_topset:
	    fprintf( out, "topset" );
	  break;
	  case if_nottopset:
	    fprintf( out, "!topset" );
	  break;
	  case if_bottomset:
	    fprintf( out, "bottomset" );
	  break;
	  case if_notbottomset:
	    fprintf( out, "!bottomset" );
	  break;
	  case if_semitopset:
	    fprintf( out, "topset(1,1)" );
	  break;
	  case if_notsemitopset:
	    fprintf( out, "!topset(1,1)" );
	  break;
	  case if_semibottomset:
	    fprintf( out, "bottomset(1,1)" );
	  break;
	  case if_notsemibottomset:
	    fprintf( out, "!bottomset(1,1)" );
	  break;
	  case if_nottopsetorsemi:
	    fprintf( out, "!topset && !topset(1,1)" );
	  break;
	  case if_notbottomsetorsemi:
	    fprintf( out, "!bottomset && !bottomset(1,1)" );
	  break;
	  case if_nottopbottomset:
	    fprintf( out, "!topset && !bottomset" );
	  break;
	}
    } else {
	fprintf( stderr, "block with both a condition and a part mask, that shouldn't happen.\n" );
    }
}

static void danceOutputCallList(FILE *out,CallList *cl, int cnt, int part_cnt ) {
    int i, j, p, pp;
    CallList *icl;
    struct clParts { 
	int mask;
	CallList *start;
	CallList *bound;
    } *ifs;

#if 0
    if ( part_cnt<=1 ) {
	for ( i=0; i<cnt; ++i, ++cl )
	    danceOutputACallList(out,cl,-1,4);
	if ( cnt!=0 ) putc( '\n', out );
return;
    }
#endif

    ifs = calloc(part_cnt+2,sizeof(struct clParts));
    for ( i=0; i<cnt; ) {
	if ( cl->part_mask!=(unsigned int) (-1) ) {
	    p=0;
	    ifs[p].mask = cl->part_mask;
	    ifs[p].start = cl;
	    for ( j=i+1, icl=cl+1; j<cnt && icl->part_mask!=(unsigned int) -1; ++j, ++icl ) {
		if ( ifs[p].mask!=icl->part_mask ) {
		    /* Has this mask been used before? If so we start a new if */
		    for ( pp=0; pp<=p; ++pp )
			if ( ifs[pp].mask&icl->part_mask )
		    break;
		    if ( pp<=p )
	    break;
		    ifs[p++].bound = icl;
		    ifs[p].mask = icl->part_mask;
		    ifs[p].start = icl;
		}
	    }
	    ifs[p++].bound = icl;
	    /* if ( p>1 ) { */
		for ( pp=0; pp<p; ++pp ) {
		    fprintf( out, "    <%s", pp==0 ? "if" : pp+1==p ? "else" : "elseif" );
		    if ( pp+1!=p || pp==0 ) {
			fprintf( out, " condition=\"" );
			danceOutputMaskToCondition(out, ifs[pp].mask, if_always);
			putc('"',out);
		    }
		    fprintf(out,">\n");
		    for ( cl=ifs[pp].start; cl<ifs[pp].bound; ++cl )
			danceOutputACallList(out,cl,-1,6);
		    fprintf( out, "    </%s>\n", pp==0 ? "if" : pp+1==p ? "else" : "elseif" );
		}
		i = j;
	    if ( p==1 ) {
		fprintf( out, "    <else>\n    </else>\n" );
	    }
#if 0
	    } else {
		while ( i<j ) {
		    danceOutputACallList(out,cl,cl->part_mask,4);
		    ++i;
		    ++cl;
		}
		fprintf( stderr, "Warning: calls with part mask not put into an if.\n" );
	    }
#endif
	} else {
	    danceOutputACallList(out,cl,cl->part_mask,4);
	    ++i;
	    ++cl;
	}
    }
    free(ifs);
    if ( cnt!=0 ) putc( '\n', out );
}

static char *handClaspName(char *buf,int person, int millibars, FigureSegment *cs ) {
    int off;
    char *bpt = buf;
    int i;

    for ( i=0; handMaskMap[i].name!=NULL; ++i ) {
	if ( person&handMaskMap[i].mask ) {
	    strcpy(bpt,handMaskMap[i].name);
	    bpt += strlen( handMaskMap[i].name );
	    *bpt++ = ':';
	}
    }
    *bpt = '\0';
    person &= HAND_PERSON_MASK;

    if ( (person&HAND_INDIVIDUAL_MASK)==HAND_STAR )
	sprintf( bpt, "Star:%.0f,%.0f", cs->star_center.x, cs->star_center.y );
    else {
	if ( (person&HAND_MINORSET_MASK)==0 )
	    sprintf( bpt, "%d", person&HAND_INDIVIDUAL_MASK );
	else {
	    if ( person & HAND_MINORSET_UP )
		strcpy(bpt,"Up");
	    else
		strcpy(bpt,"Down" );
	    off = (person&HAND_MINORSET_OFFSET)>>HAND_MINORSET_SHIFT;
	    if ( off!=1 )
		sprintf(bpt+strlen(bpt),"%d", off );
	    sprintf(bpt+strlen(bpt), ":%d", person&HAND_INDIVIDUAL_MASK);
	}
    }
    bpt += strlen(bpt);
    *bpt++ = '|';
    if ( millibars==cs->millibars )
	strcpy(bpt,"end");
    else
	formatMillibars(bpt,millibars);
return( buf );
}

static char *valueChangeName(char *buf,int size, ValChange *changes, FigureSegment *cs ) {
    char *bpt = buf;
    int i;

    if ( changes==NULL || changes[0].millibars==-1 ) {
	*buf='\0';
return( buf );
    }
    for ( i=0; changes[i].millibars!=-1 && bpt<buf+size-10; ++i ) {
	sprintf( bpt, "%d", changes[i].value );
	bpt += strlen( bpt );
	if ( changes[i].millibars!=cs->millibars ) {
	    *bpt++ = '|';
	    formatMillibars(bpt,changes[i].millibars);
	} else if ( changes[i+1].millibars!=-1 )
	    sprintf( bpt, "|end" );
	bpt += strlen( bpt );
	if ( changes[i+1].millibars!=-1 )
	    *bpt++ = ';';
    }
    if ( changes[i].millibars!=-1 )
	fprintf( stderr, "%s:%d Buffer overflow in valueChangeName.\n", __FILE__, __LINE__ );
    *bpt = '\0';
return( buf );
}

static char *rThetaChangeName(char *buf,RThetaChange *changes, FigureSegment *cs ) {
    char *bpt = buf;
    int i;

    if ( changes==NULL || changes[0].millibars==-1 ) {
	*buf='\0';
return( buf );
    }
    for ( i=0; changes[i].millibars!=-1 ; ++i ) {
	sprintf( bpt, "%.2f,%.1f", changes[i].pos.r, changes[i].pos.theta );
	bpt += strlen( bpt );
	if ( changes[i].millibars!=cs->millibars ) {
	    *bpt++ = '|';
	    formatMillibars(bpt,changes[i].millibars);
	} else if ( changes[i+1].millibars!=-1 )
	    sprintf( bpt, "|end" );
	bpt += strlen( bpt );
	if ( changes[i+1].millibars!=-1 )
	    *bpt++ = ';';
    }
    *bpt = '\0';
return( buf );
}

static void danceOutputCondition(FILE *out,Condition *c) {
    switch ( c->operator ) {
      case op_not:
        if ( c->op1->operator==op_and || c->op1->operator==op_or ) {
            fprintf(out,"!(");
            danceOutputCondition(out,c->op1);
            putc(')',out);
        } else {
            putc('!',out);
            danceOutputCondition(out,c->op1);
        }
      break;
      case op_and: case op_or:
        if (( c->operator==op_and && c->op1->operator==op_or ) ||
        	(c->operator==op_or && c->op1->operator==op_and )) {
            putc('(',out);
            danceOutputCondition(out,c->op1);
            putc(')',out);
        } else
            danceOutputCondition(out,c->op1);
        fprintf( out, c->operator==op_and ? " and " : " or " );
        if (( c->operator==op_and && c->op2->operator==op_or ) ||
        	(c->operator==op_or && c->op2->operator==op_and )) {
            putc('(',out);
            danceOutputCondition(out,c->op2);
            putc(')',out);
        } else
            danceOutputCondition(out,c->op2);
      break;
      case op_topset:
        if ( c->argument2!=0 )
	    fprintf( out, "topset(%d,%d)", c->argument1, c->argument2 );
	else if ( c->argument1!=1 )
	    fprintf( out, "topset(%d)", c->argument1 );
	else
	    fprintf( out, "topset" );
      break;
      case op_bottomset:
        if ( c->argument2!=0 )
	    fprintf( out, "bottomset(%d,%d)", c->argument1, c->argument2 );
	else if ( c->argument1!=1 )
	    fprintf( out, "bottomset(%d)", c->argument1 );
	else
	    fprintf( out, "bottomset" );
      break;
      case op_majorpos:
	 fprintf( out, "majorpos(%d)", c->argument1 );
      break;
      case op_part:
	fprintf( out, "part(%d)", c->argument1 );
      break;
      case op_proper:
	fprintf( out, "proper" );
      break;
      case op_improper:
	fprintf( out, "improper" );
      break;
      case op_last:
	fprintf( out, "last" );
      break;
      case op_first:
	fprintf( out, "first" );
      break;
    }
}

struct callMillibars {
    CallList *next;
    int cstart;
    int figure_mbars;
    int show_mbars_in_output;
};

static void danceOutputBlock(FILE *out, Block *b, struct callMillibars *mb, int indent, Person *p );

static void danceOutputIfBlocks(FILE *out, FigureSegment *cs, struct callMillibars *mb, int indent, Person *p ) {
    Block *b;
    struct callMillibars restart;
    int i;
    char *tag;
    
    restart = *mb;
    for ( b=cs->ifblock; b!=NULL ; b=b->next ) {
    	*mb = restart;
    	if ( b==cs->ifblock )
    	    tag = "if";
    	else if ( b->condition != NULL )
    	    tag = "elseif";
    	else
    	    tag = "else";
	for ( i=0; i<indent; ++i )
	    putc(' ',out);
	fprintf( out, "<%s", tag );
	if ( b->condition!=NULL ) {
	    fprintf( out, " condition=\"" );
	    danceOutputCondition(out,b->condition);
	    putc( '"', out );
	}
	fprintf( out,">\n" );
	danceOutputBlock(out,b,mb,indent+2,p);
	for ( i=0; i<indent; ++i )
	    putc(' ',out);
	fprintf( out, "</%s>\n", tag );
    }
}
	
static void danceOutputFigureSegment(FILE *out, FigureSegment *cs, struct callMillibars *mb, int indent, Person *p ) {
    int i;
    char buf1[400], buf2[20], buf3[20];

	if ( cs==p->figures.moves+1 || cs->cmd==c_resetDance || cs->cmd==c_end )
	    fprintf( out, "\n" );
	else if ( mb->next!=NULL && mb->next->callName!=NULL && mb->cstart+mb->next->millibars<=mb->figure_mbars ) {
	    /* put a blank line between every call */
	    fprintf( out, "\n" );
	    /* In case we have a pause which skips multiple calls */
	    while ( mb->next->callName!=NULL && mb->cstart+mb->next->millibars<=mb->figure_mbars ) {
		mb->cstart += mb->next->millibars;
		++mb->next;
		while ( mb->next->callName!=NULL && !(1&mb->next->part_mask))
		    ++mb->next;
	    }
	}
	if ( cs->cmd==c_if ) {
	    danceOutputIfBlocks(out,cs,mb,indent,p);
return;
	}
	for ( i=0; i<indent; ++i )
	    putc(' ',out);
	fprintf( out, "<%s", commandNames[cs->cmd] );
	if ( cs->cmd==c_lineto || cs->cmd==c_curveto || cs->cmd==c_pause ) {
	    fprintf( out, " bar=\"%s\"", formatMillibars(buf1, cs->millibars ));
	    if ( cs->cmd!=c_pause || (cs->orient!=mo_forward && cs->orient!=mo_fixed ))
		fprintf( out, " orientation=\"%s\"", orientationNames[cs->orient] );
	    if ( cs->position_absolute )
		fprintf( out, " absolute-position=\"1\"" );
	    if ( (cs->orient==mo_rotateto || cs->orient==mo_cw_rotateto || cs->orient==mo_ccw_rotateto )
		    && cs->degrees==DEGREES_END )
		fprintf( out, " degrees=\"end\"" );
	    else if ( cs->orient==mo_rotating || cs->orient==mo_rotateto || cs->orient==mo_cw_rotateto || cs->orient==mo_ccw_rotateto )
		fprintf( out, " degrees=\"%s\"", formatFloat(buf3,cs->degrees));
	    else if ( cs->orient==mo_faceDancer )
		fprintf( out, " dancer=\"%d\"", cs->faceDancer );
	}
	if ( cs->cmd == c_moveto ) {
	    fprintf( out, " facing=\"%s\"",
		    formatFloat(buf3,cs->degrees));
	} else if ( cs->cmd==c_curveto ) {
	    fprintf( out, " cp1=\"%s,%s\"",
		    formatFloat(buf1,cs->cp1.x),
		    formatFloat(buf2,cs->cp1.y));
	    fprintf( out, " cp2=\"%s,%s\"",
		    formatFloat(buf1,cs->cp2.x),
		    formatFloat(buf2,cs->cp2.y));
	}
	if ( cs->cmd==c_moveto || cs->cmd==c_lineto || cs->cmd==c_curveto )
	    fprintf( out, " pos=\"%s,%s\"",
		    formatFloat(buf1,cs->endPoint.x),
		    formatFloat(buf2,cs->endPoint.y));
	if ( cs->congruent!=0 )
	    fprintf( out, " congruent=\"0x%lx\"", cs->congruent );
	if ( cs->drop_right_millibars==cs->millibars )
	    fprintf( out, " drop-right-bars=\"end\"" );
	else if ( cs->drop_right_millibars!=HAND_MILLIBARS_UNSET )
	    fprintf( out, " drop-right-bars=\"%s\"", formatMillibars(buf1,cs->drop_right_millibars) );
	if ( cs->drop_left_millibars==cs->millibars )
	    fprintf( out, " drop-left-bars=\"end\"" );
	else if ( cs->drop_left_millibars!=HAND_MILLIBARS_UNSET )
	    fprintf( out, " drop-left-bars=\"%s\"", formatMillibars(buf1,cs->drop_left_millibars) );

	if ( cs->join_right_hand!=0 )
	    fprintf( out, " join-right=\"%s\"", handClaspName(buf1,cs->join_right_hand, cs->join_right_millibars, cs ));
	if ( cs->join_left_hand!=0 )
	    fprintf( out, " join-left=\"%s\"", handClaspName(buf1,cs->join_left_hand, cs->join_left_millibars, cs ));
	if ( cs->levitation!=NULL && cs->levitation[0].millibars!=-1 )
	    fprintf( out, " levitation=\"%s\"", valueChangeName(buf1,sizeof(buf1),cs->levitation, cs ));
	if ( cs->outness!=NULL && cs->outness[0].millibars!=-1 )
	    fprintf( out, " outness=\"%s\"", valueChangeName(buf1,sizeof(buf1),cs->outness, cs ));

	if ( cs->rightHand!=NULL && cs->rightHand[0].millibars!=-1 )
	    fprintf( out, " right-hand=\"%s\"", rThetaChangeName(buf1,cs->rightHand, cs ));
	if ( cs->leftHand!=NULL && cs->leftHand[0].millibars!=-1 )
	    fprintf( out, " left-hand=\"%s\"", rThetaChangeName(buf1,cs->leftHand, cs ));
	if ( cs->swordPoint!=NULL && cs->swordPoint[0].millibars!=-1 )
	    fprintf( out, " swordpoint=\"%s\"", rThetaChangeName(buf1,cs->swordPoint, cs ));
	if ( cs->join_left_to_point!=-1 ) {
	    fprintf( out, " join-left-to-point=\"%d|", cs->join_left_to_point );
	    if ( cs->join_left_millibars==cs->millibars )
		fprintf( out, "end\"" );
	    else
		fprintf( out, "%s\"", formatMillibars(buf1,cs->join_left_millibars) );
	}

	if ( cs->start_honour<cs->end_honour )
	    fprintf( out, " start-honour=\"%s\" end-honour=\"%s\"",
		    formatMillibars(buf1, cs->start_honour ),
		    cs->end_honour==cs->millibars? "end" : formatMillibars(buf2, cs->end_honour ) );
	danceOutputPartMask(out, cs->part_mask );
	if ( cs->condition!=if_always )
	    fprintf( out, " condition=\"%s\"", conditionNames[cs->condition]);
	if ( !mb->show_mbars_in_output ) 
	    fprintf( out, "/>\n" );
	else
	    fprintf( out, "/>\t<!-- %s -->\n", formatMillibars(buf1,mb->figure_mbars ));
	    
	mb->figure_mbars += cs->millibars;
}

static void danceOutputBlock(FILE *out, Block *b, struct callMillibars *mb, int indent, Person *d ) {
    FigureSegment *cs, *ics, *cs_end;
    int p,pp,i;
    int any;
    struct fsParts { 
	int mask;
	int condition;
	FigureSegment *start;
	FigureSegment *bound;
    } ifs[32];
    /* we support at most 32 parts, and at most 3 conditions (topset, semitopset, nottopsetorsemi */
    struct callMillibars restart;

    cs_end = b->moves + b->move_cnt;
    any = 0;
    for ( cs = b->moves; cs<cs_end ; ++cs ) {
	if ( cs->part_mask!=(unsigned int) -1 || cs->condition!=if_always ) {
	    any = 1;
    break;
	}
    }
    if ( !any ) {
	for ( cs = b->moves; cs<cs_end ; ++cs ) {
	    danceOutputFigureSegment(out,cs,mb,indent,d);
	    if ( cs->cmd==c_end )		/* Need to output the end command */
	break;
	}
return;
    }

    for ( cs = b->moves; cs<cs_end ; ) {
	if ( (cs->part_mask!=(unsigned int) (-1) && cs->condition==if_always) ||
	      (cs->part_mask==(unsigned int) -1 && cs->condition!=if_always )) {
	    p=0;
	    ifs[p].mask = cs->part_mask;
	    ifs[p].condition = cs->condition;
	    ifs[p].start = cs;
	    for ( ics=cs+1; ics<cs_end && (ics->part_mask!=(unsigned int) -1 || ics->condition!=if_always); ++ics ) {
		if ( ifs[0].mask!=(unsigned int)-1 && ics->part_mask==(unsigned int) -1 )
	    break;	/* No ifs with mixed parts and conditions */
		if ( ifs[0].condition!=if_always && ics->condition==if_always )
	    break;	/* same thing, other way round */

		if ( ifs[p].mask!=ics->part_mask || ifs[p].condition!=ics->condition ) {
		    /* Has this mask or condition been used before? If so we start a new if */
		    if ( ifs[0].mask!=(unsigned int) -1) for ( pp=0; pp<=p; ++pp ) {
			if ( ifs[pp].mask&ics->part_mask )
		    break;
		    } else if ( ifs[0].condition!=if_always ) for ( pp=0; pp<=p; ++pp ) {
			if ( ifs[pp].condition==ics->condition )
		    break;
		    }
		    if ( pp<=p )
	    break;
		    ifs[p++].bound = ics;
		    ifs[p].mask = ics->part_mask;
		    ifs[p].condition = ics->condition;
		    ifs[p].start = ics;
		}
	    }
	    ifs[p++].bound = ics;
	    if ( p>1 ) {
		restart = *mb;
		for ( pp=0; pp<p; ++pp ) {
		    *mb = restart;
		    for ( i=0; i<indent ; ++i )
			putc(' ',out);
		    fprintf( out, "<%s", pp==0 ? "if" : pp+1==p ? "else" : "elseif" );
		    if ( pp+1!=p ) {
			fprintf( out, " condition=\"" );
			danceOutputMaskToCondition(out, ifs[pp].mask, ifs[pp].condition);
			putc('"',out);
		    }
		    fprintf(out,">\n");
		    for ( cs=ifs[pp].start; cs<ifs[pp].bound; ++cs ) {
			cs->part_mask = (unsigned int) -1; cs->condition = if_always;
			danceOutputFigureSegment(out,cs,mb,indent+2,d);
			cs->part_mask = ifs[pp].mask; cs->condition = ifs[pp].condition;
		    }
		    for ( i=0; i<indent ; ++i )
			putc(' ',out);
		    fprintf( out, "</%s>\n", pp==0 ? "if" : pp+1==p ? "else" : "elseif" );
		}
	    } else {
		while ( cs<ics ) {
		    danceOutputFigureSegment(out,cs,mb,indent,d);
		    ++cs;
		}
		fprintf( stderr, "Warning: figure segments with part mask/condition not put into an if (within block starting at %ld).\n", b->lineno );
	    }
	} else {
	    danceOutputFigureSegment(out,cs,mb,indent,d);
	    ++cs;
	}
    }
}
    
static void danceOutputPerson(FILE *out, int is_person, Person *person, CallList *calls, int show_mbars_in_output) {
    struct callMillibars mb;

    if ( is_person ) {
	fprintf( out, "    <person couple=\"%d\"", person->couple_number );
    } else {
	fprintf( out, "    <role role=\"%s\"", roleNames[person->effective_role] );
	if ( person->display_role!=r_last_unused )
	    fprintf( out, " display-role=\"%s\"", roleNames[person->display_role] );
    }
    fprintf( out, " sex=\"%c\">\n", sexChars[person->sex] );
 if ( person->now_sicilian_theta!=0 )	/* !!!! Debug !!!! */
  fprintf( out, "<!-- theta=%.4f -->\n", person->now_sicilian_theta );
    memset(&mb,0,sizeof(mb));
    if ( calls!=NULL )
	while ( calls->callName!=NULL && !(1&calls->part_mask))
	    ++calls;
    mb.next  = calls;
    mb.show_mbars_in_output = show_mbars_in_output;
    danceOutputBlock(out,&person->figures,&mb,6,person);
    fprintf( out, is_person ? "    </person>\n" : "    </role>\n" );
}

static void danceOutputMusicSegments(FILE *out,char *label,MusicSegment *segment,int repeats, int cnt) {
    char buf1[40];

    if ( repeats==0 && cnt==0 )
	fprintf( out, "      <%s>\n", label );
    else if ( repeats==0 )
	fprintf( out, "      <%s count=\"%d\">\n", label, cnt );
    else
	fprintf( out, "      <%s repeats=\"%d\">\n", label, repeats );
    for ( ; segment!=NULL; segment=segment->next ) {
	if ( segment->type==it_copy )
	    fprintf( out, "\t<copy " );
	else if ( segment->type==it_fit )
	    fprintf( out, "\t<fit " );
	else if ( segment->type==it_fit2set )
	    fprintf( out, "\t<fit2set " );
	else
	    fprintf( out, "\t<fit2lastset " );
	if ( segment->name!=NULL )
	    fprintf( out, "name=\"%s\" ", segment->name );
	fprintf( out, "start=\"%d\" end=\"%d\" ", segment->start, segment->end );
	if ( segment->duration_millibars!=0 )
	    fprintf( out, "duration-bar=\"%s\" ", formatMillibars(buf1, segment->duration_millibars ));
	if ( segment->fadein_duration!=0 )
	    fprintf( out, "fade-in=\"%d,%.2f\" ", segment->fadein_duration, segment->fadein_initial_level );
	if ( segment->autogen )
	    fprintf( out, "/>\t<!-- Automatically generated -->\n" );
	else
	    fprintf( out, "/>\n" );
    }
    fprintf( out, "      </%s>\n", label );
}

void danceOutputMusicDescription(FILE *out, MusicDescription *md) {

    if ( md->filename!=NULL ) {
	char buf2[20], buf3[20];
	fprintf( out, "    <music file=\"%s\" amplify=\"%s\" ",
	    md->filename,
	    formatFloat(buf3,md->amplify));
	if ( md->start_millibars!=0 )
	    fprintf( out, "delay-start-bar=\"%s\" ", formatMillibars(buf2, md->start_millibars ));

	if ( md->name!=NULL )
	    fprintf( out, "\n      name=\"%s\"", md->name );
	if ( md->danceName!=NULL ) {
	    fprintf( out, "\n      dance-name=\"" );
	    entityOutput(out, md->danceName, false );
	    fprintf( out, "\"" );
	}
	if ( md->mdId!=NULL )
	    fprintf( out, "\n      music-description-id=\"%s\"", md->mdId );
	if ( md->composer!=NULL ) {
	    fprintf( out, "\n      composer=\"" );
	    entityOutput(out,md->composer,false);
	    fprintf( out, "\"" );
	}
	if ( md->performers!=NULL ) {
	    fprintf( out, "\n      performers=\"" );
	    entityOutput(out,md->performers,false);
	    fprintf( out, "\"" );
	}
	if ( md->publication!=NULL ) {
	    fprintf( out, "\n      publication=\"" );
	    entityOutput(out,md->publication,false);
	    fprintf( out, "\"" );
	}
	if ( md->year_written>0 )
	    fprintf( out, " year-written=\"%d\"", md->year_written );
	else if ( md->year_written<0 )
	    fprintf( out, " year-written=\"~%d\"", -md->year_written );
	if ( md->year_performed>0 )
	    fprintf( out, " year-performed=\"%d\"", md->year_performed );
	else if ( md->year_performed<0 )
	    fprintf( out, " year-performed=\"~%d\"", -md->year_performed );
	if ( md->titleAudioFilename!=NULL )
	    fprintf( out, "\n      title-audio=\"%s\"", md->titleAudioFilename );
	if ( md->show_minorset!=MINORSET_UNSPECIFIED )
	    fprintf( out, "\n      show-minor-set=\"%s\"", md->show_minorset ? "on" : "off" );
	if ( md->desired_repetitions!=0 )
	    fprintf( out, "\n      minor-set-count=\"%d\"", md->desired_repetitions );
	if ( md->cdImage!=NULL )
	    fprintf( out, "\n      cd-image=\"%s\"", md->cdImage );
	if ( md->cdNotes!=NULL )
	    fprintf( out, "\n      cd-notes=\"%s\"", md->cdNotes );
	if ( md->permission!=NULL )
	    fprintf( out, "\n      permission=\"%s\"", md->permission );

	if ( md->style!=-1 ) {
	    fprintf( out, "\n      style=\"%s\"", styleNames[md->style] );
	    if ( md->music_type!=-1 )
		fprintf( out, " type=\"%s\"", musicTypes[md->music_type] );
	    if ( md->key!=NULL )
		fprintf( out, " key=\"%s\"", md->key );
	    fprintf( out, " counts-per-bar=\"%d\"", md->counts_per_bar );
	}

	fprintf( out, ">\n" );

	if ( md->html_notes ) {	/* Entities should already be in entities when needed */
	    fprintf( out, "    <html-notes>" );
	    fprintf( out, "%s", md->html_notes );
	    fprintf( out, "</html-notes>\n\n" );
	}

	if ( md->honour!=NULL )
	    danceOutputMusicSegments(out,"honour",md->honour,0,0);
	if ( md->intro!=NULL )
	    danceOutputMusicSegments(out,"intro",md->intro,0,0);
	if ( md->minorset!=NULL )
	    danceOutputMusicSegments(out,"minorset",md->minorset,0,0);
	if ( md->middlesets!=NULL )
	    danceOutputMusicSegments(out,"middlesets",md->middlesets,0,md->middleset_count);
	if ( md->lastminorset!=NULL )
	    danceOutputMusicSegments(out,"lastminorset",md->lastminorset,0,0);
	if ( md->body!=NULL )
	    danceOutputMusicSegments(out,"body",md->body,md->body_repeats,0);
	if ( md->ending!=NULL )
	    danceOutputMusicSegments(out,"ending",md->ending,0,0);
	if ( md->set_count!=0 ) {
	    int i;
	    fprintf( out, "      <set-bounds set-bars=\"%d\" intro-start=\"%ld\" intro-end=\"%ld\" final-end=\"%ld\"\n",
		    (int) (md->minorset_millibars/MILLIBARS_MUL),
		    md->intro_start, md->intro_end,
		    md->final_end );
	    fprintf( out, "\t  set-ends=\"" );
	    for ( i=0; i<md->set_count; ++i )
		fprintf( out, "%s%ld", i==0?"":", ", md->set_ends[i] );
	    fprintf( out, "\"/>\n" );
	}
	fprintf( out, "    </music>\n\n" );
    }
}

static void danceOutputComparisonContents(FILE *out, Comparison *comp, int show_mbars_in_output) {
    int i;

    fprintf( out, "  <comp id=\"%s\"", comp->dance.name );
    if ( comp->dance.sortName!=NULL ) {
	fprintf( out, " sort-name=\"" );
	entityOutput(out,comp->dance.sortName,false );
	fprintf( out, "\"" );
    }
    if ( comp->dance.commonName!=NULL ) {
	fprintf( out, " common-name=\"" );
	entityOutput(out,comp->dance.commonName,false );
	fprintf( out, "\"" );
    }
    if ( comp->display_id!=NULL )
	fprintf( out, "\n      display-id=\"%s\"", comp->display_id );
    if ( comp->dance.author!=NULL )
	fprintf( out, "\n      author=\"%s\"", comp->dance.author );
    if ( comp->dance.year_written>0 )
	fprintf( out, " year-written=\"%d\"", comp->dance.year_written );
    else if ( comp->dance.year_written<0 )
	fprintf( out, " year-written=\"~%d\"", -comp->dance.year_written );
    if ( comp->dance.publication!=NULL )
	fprintf( out, "\n      publication=\"%s\"", comp->dance.publication );

    if ( comp->dance.animator!=NULL )
	fprintf( out, "\n      animator=\"%s\"", comp->dance.animator );
    if ( comp->dance.animation_date!=NULL )
	fprintf( out, " animation-date=\"%s\"", comp->dance.animation_date );

    if ( comp->dance.titleAudioFilename!=NULL )
	fprintf( out, "\n      title-audio=\"%s\"", comp->dance.titleAudioFilename );
    if ( comp->dance.url!=NULL )
	fprintf( out, "\n      url=\"%s\"", comp->dance.url );

    for ( i=0; i<2; ++i ) {
	fprintf( out, "\n      dance%d=\"%s\"", i+1, comp->dances[i].filename );
	if ( comp->dances[i].args!=NULL && *comp->dances[i].args!='\0' )
	    fprintf( out, "\n      args%d=\"%s\"", i+1, comp->dances[i].args );
	if ( comp->dances[i].sicilian<2 )
	    fprintf( out, "\n      sicilian%d=\"%s\"", i+1, comp->dances[i].sicilian?"on":"off" );
    }
    fprintf( out, "\n      stacked=\"%s\"", comp->stacked_vertically ? "vertically" : "horizontally" );
    if ( comp->sourceImage!=NULL )
	fprintf( out, "\n      sourceImage=\"%s\"", comp->sourceImage );
    if ( comp->imageCopyright!=NULL ) {
	fprintf( out, "\n      imageCopyright=\"" );
	entityOutput(out, comp->imageCopyright, true );
	fprintf( out, "\"" );
    }

    fprintf( out, "\n   >\n" );

    if ( comp->dance.html_notes ) {	/* Entities should already be in entities when needed */
	fprintf( out, "\n    <html-notes>" );
	fprintf( out, "%s",comp->dance.html_notes );
	fprintf( out, "</html-notes>\n\n" );
    }

    if ( comp->dance.music.html_notes ) {	/* Entities should already be in entities when needed */
	fprintf( out, "    <music-html-notes>" );
	fprintf( out, "%s",comp->dance.music.html_notes );
	fprintf( out, "</music-html-notes>\n\n" );
    }
    fprintf( out, "    </comp>\n\n" );
}

#define DOFC_DEBUG	0x1
#define DOFC_NOROLES	0x2		/* Don't include roles nor the figures that make them up (so just metadata and calls, but do include a role-cnt field) */
#define DOFC_EXTRA	0x4		/* Remove long-s, add missing sort-name, include public domain flag, counts-per-minute, minorset-bars, up-a-double-id, figures mask */
#define DOFC_ADDCALLS	0x8		/* Generate HTML to describe the calls */
#define DOFC_ADDMBARS	0x10		/* Add comments to the output showing the number of bars from start of dance at the beginning of each figure segment */

static void danceOutputFigureContents(FILE *out, Figure *figure, int flags, char *filename) {
    int i;
    char buf1[400];
    int debug = (flags&DOFC_DEBUG)?1:0;
    int show_mbars_in_output = (flags&DOFC_ADDMBARS)?1:0;

    if ( figure==NULL )
return;
    if ( figure->is_comparison ) {
	danceOutputComparisonContents(out,(Comparison *) figure,debug);
return;
    }
    fprintf( out, figure->is_figure ? "  <figure" : figure->is_component ? "  <component" : "  <dance" );

    if ( flags&DOFC_EXTRA ) {
	if ( strcmp(figure->name,"Three's Company\n<small><small><small>Twelve's a Crowd</small></small></small>")==0 ) {
	    fprintf( out, " id=\"Three\'s Company\"" );
	    fprintf( out, " alt-name=\"Twelve\'s a Crowd\"" );
	} else {
	    fprintf( out, " id=\"" );
	    entityOutput(out,removeLongs(buf1,sizeof(buf1),figure->name, false ),false);
	    fprintf( out, "\"");
	    if ( figure->altName!=NULL ) {
		fprintf( out, " alt-name=\"" );
		entityOutput(out,removeLongs(buf1,sizeof(buf1),figure->altName, false ),false);
		fprintf( out, "\"" );
	    }
	}
    } else {
	fprintf( out, " id=\"" );
	entityOutput(out,figure->name,false);
	fprintf( out, "\"");
	if ( figure->altName!=NULL )
	    fprintf( out, " alt-name=\"%s\"", figure->altName );
    }
    if ( figure->sortName!=NULL ) {
	fprintf( out, "\n sort-name=\"" );
	entityOutput(out,figure->sortName,false );
	fprintf( out, "\"" );
    } else if ( flags&DOFC_EXTRA ) {
	fprintf( out, " sort-name=\"" );
	entityOutput(out,makeSortName(buf1,sizeof(buf1),figure->name, figure->sortName ),false);
	fprintf( out, "\"" );
    }
    if ( figure->commonName!=NULL ) {
	fprintf( out, "\n common-name=\"" );
	entityOutput(out,figure->commonName,false );
	fprintf( out, "\"" );
    }
    if ( figure->callGroup!=NULL )
	fprintf( out, " call-group=\"%s\"", figure->callGroup );
    if ( figure->style!=ds_unspecified ) {
	fprintf( out, " style=\"%s\"", styleNames[figure->style] );
	if ( figure->style==ds_square && figure->substyle!=ds_unspecified )
	    fprintf( out, " sub-style=\"%s\"", substyleNames[figure->substyle] );
    }
    if ( figure->author!=NULL ) {
	fprintf( out, "\n      devisor=\"" );
	if ( flags&DOFC_EXTRA )
	    entityOutput(out,removeLongs(buf1,sizeof(buf1),figure->author, false ),true);
	else
	    entityOutput(out,figure->author,true);
	fprintf( out, "\"" );
    }
    if ( figure->year_written>0 )
	fprintf( out, " year-written=\"%d\"", figure->year_written );
    else if ( figure->year_written<0 )
	fprintf( out, " year-written=\"~%d\"", -figure->year_written );
    if ( figure->publication!=NULL ) {
	fprintf( out, "\n      publication=\"" );
	entityOutput(out,figure->publication,false);
	fprintf( out, "\"" );
    }
    if ( figure->interpreterPublication!=NULL ) {
	fprintf( out, "\n      interpreter-publication=\"" );
	entityOutput(out,figure->interpreterPublication,false);
	fprintf( out, "\"" );
    }

    if ( figure->interpreter!=NULL ) {
	fprintf( out, "\n      interpreter=\"" );
	entityOutput(out,figure->interpreter,false);
	fprintf( out, "\"" );
    }
    if ( figure->year_interpreted>0 )
	fprintf( out, " year-interpreted=\"%d\"", figure->year_interpreted );
    else if ( figure->year_interpreted<0 )
	fprintf( out, " year-interpreted=\"~%d\"", -figure->year_interpreted );
    if ( ( flags&DOFC_EXTRA ) && danceIsPublicDomain(figure))
	fprintf( out, "\n      public-domain=\"1\"" );

    if ( figure->animator!=NULL )
	fprintf( out, "\n      animator=\"%s\"", figure->animator );
    if ( figure->animation_date!=NULL )
	fprintf( out, " animation-date=\"%s\"", figure->animation_date );

    if ( figure->source!=NULL )
	fprintf( out, "\n      source=\"%s\"", figure->source );
    if ( figure->music_name!=NULL )
	fprintf( out, "\n      music=\"%s\"", figure->music_name );
    if ( figure->music_author!=NULL )
	fprintf( out, " music-author=\"%s\"", figure->music_author );
    if ( figure->license!=NULL )
	fprintf( out, "\n      license=\"%s\"", figure->license );
    if ( figure->tags!=NULL )
	fprintf( out, "\n      tags=\"%s\"", figure->tags );
    if ( figure->rscds_precis!=NULL )
	fprintf( out, "\n      rscds-precis=\"%s\"", figure->rscds_precis );
    if ( figure->titleAudioFilename!=NULL )
	fprintf( out, "\n      title-audio=\"%s\"", figure->titleAudioFilename );
    if ( figure->url!=NULL )
	fprintf( out, "\n      url=\"%s\"", figure->url );
    if ( figure->url10!=NULL )
	fprintf( out, "\n      url10=\"%s\"", figure->url10 );
    if ( figure->url11!=NULL )
	fprintf( out, "\n      url11=\"%s\"", figure->url11 );
    if ( figure->sicilian_url!=NULL )
	fprintf( out, "\n      sicilian-url=\"%s\"", figure->sicilian_url );
    if ( figure->orig_url!=NULL )
	fprintf( out, "\n      orig-url=\"%s\"", figure->orig_url );
    if ( figure->source_url!=NULL ) {
	fprintf( out, "\n      source-url=\"" );
	entityOutput(out,figure->source_url,false);
	fprintf( out, "\"" );
    }
    if ( figure->live_urls!=NULL )
	fprintf( out, "\n      live-urls=\"%s\"", figure->live_urls );
    if ( figure->live_labels!=NULL )
	fprintf( out, "\n      live-labels=\"%s\"", figure->live_labels );
    if ( figure->la_url!=NULL )
	fprintf( out, "\n      la-url=\"%s\"", figure->la_url );
    if ( figure->la_caller!=NULL )
	fprintf( out, "\n      la-caller=\"%s\"", figure->la_caller );
    if ( figure->la_musicians!=NULL ) {
	fprintf( out, "\n      la-musicians=\"" );
	entityOutput(out,figure->la_musicians,false);
	fprintf( out, "\"" );
    }
    if ( figure->la_dancers!=NULL )
	fprintf( out, "\n      la-dancers=\"%s\"", figure->la_dancers );
    if ( figure->background_file!=NULL )
	fprintf( out, "\n      background=\"%s\"", figure->background_file );
    if ( figure->title_background_file!=NULL )
	fprintf( out, "\n      title-background=\"%s\"", figure->title_background_file );
    if ( figure->title_font!=NULL )
	fprintf( out, "\n      title-font=\"%s\"", figure->title_font );
    if ( figure->use_long_s!=2 )
	fprintf( out, "\n      use-long-s=\"%d\"", figure->use_long_s );
    if ( figure->key!=NULL )
	fprintf( out, "\n      key=\"%s\"", figure->key );
    if ( figure->difficulty_level!=0 )
	fprintf( out, "\n      difficulty-level=\"%s\"", difficultyLevelNames[figure->difficulty_level] );
    if ( figure->stroke_text_on_background )
	fprintf( out, "\n      stroke-text=\"yes\"" );
    if ( figure->text_color!=-1 )
	fprintf( out, "\n      text-color=\"#%06x\"", figure->text_color );
    if ( figure->progression_mod!=1 )
	fprintf( out, "\n      progression-rotation-center=\"%.2f,%.2f%%%d\"",
		figure->progression_center.x, figure->progression_center.y, figure->progression_mod );
    else if ( figure->progression_offset!=0 )
	fprintf( out, "\n      progression-rotation-center=\"%.2f,%.2fr%d\"",
		figure->progression_center.x, figure->progression_center.y, figure->progression_offset );
    if ( figure->ignore_sex )
	fprintf( out, "\n      ignore-sex=\"true\"" );
    if ( figure->bandPos!=band_unspecified )
	fprintf( out, "\n      band-pos=\"%s\"", bandNames[figure->bandPos] );

    if ( figure->counts_per_bar!=2 )
	fprintf( out, "\n      counts-per-bar=\"%d\"", figure->counts_per_bar );
    if ( figure->counts_per_min!=0 )
	fprintf( out, "\n      counts-per-min=\"%d\"", figure->counts_per_min );
    if ( figure->part_cnt!=1 )
	fprintf( out, "\n      parts=\"%d\"", figure->part_cnt );
    if ( figure->part_cnt!=figure->original_parts )
	fprintf( out, "\n      original-parts=\"%d\"", figure->original_parts );
    if ( figure->desired_repetitions!=0 )
	fprintf( out, "\n      repetitions=\"%d\"", figure->desired_repetitions );
    if ( figure->desired_couple_cnt!=0 )
	fprintf( out, "\n      couple-count=\"%d\"", figure->desired_couple_cnt );
    if ( !figure->show_minorset )
	fprintf( out, "\n      show-minor-set=\"no\"" );
    if ( figure->show_figure_titles )
	fprintf( out, "\n      show-figure-titles=\"yes\"" );
    if ( figure->old_style_progression )
	fprintf( out, "\n      progression=\"%d,old-style\"", figure->progression );
    else if ( figure->progression!=1 )
	fprintf( out, "\n      progression=\"%d\"", figure->progression );
    if ( figure->do_honour!=honour_default )
	fprintf( out, "\n      honour=\"%s\"", figure->do_honour == honour_doit ? "Yes" : "No" );
    if ( figure->do_honour_end!=honour_default )
	fprintf( out, "\n      honour-at-end=\"%s\"", figure->do_honour == honour_doit ? "Yes" : "No" );
    if ( flags&DOFC_NOROLES )
	fprintf( out, "\n      role-cnt=\"%d\"", figure->role_cnt );
    
    if ( flags&DOFC_EXTRA ) {
	struct video_options options;
	DanceView dv;
	int nonprogressive;
	unsigned int figureMasks[FIG_INT_CNT];

	memset(&options,0,sizeof(options));
	memset(&dv,0,sizeof(dv));
	dv.dance = figure;
	if ( figure->is_comparison )
	    dv.dance = ((Comparison *) figure)->dances[0].d;
	dv.shush = true;
	danceAVFigureFramesPerSecBar(&dv,&options);
	fprintf( out,"\n\tcpm=\"%d\"", (int) round(60*figure->counts_per_bar*dv.frames_per_sec/(double)dv.frames_per_bar) );

	if ( (!danceHasReset(figure) && figure->choreo_cnt==0) || figure->formation==sp_facingCouples ||
		/*figure->formation==sp_facingCouplesProper ||*/
		figure->formation==sp_2Couple ||
		figure->formation==sp_facingCouplesBecket ) {
	    nonprogressive = 1;
	} else if ( figure->choreo_cnt==0 && !danceHasProgression(figure)) {
	    nonprogressive = 1;
	} else {
	    nonprogressive = 0;
	}
	if ( nonprogressive && figure->part_cnt!=0 )
	    fprintf( out," dance-bars=\"%d\"", figure->part_cnt*danceMinorSetActualMillibarCount( figure )/1000 );
	else
	    fprintf( out," dance-bars=\"%d\"", danceMinorSetActualMillibarCount( figure )/1000 );

	if ( filename!=NULL ) {
	    char *start, *end;

	    start = strrchr(filename,'/');
	    if ( start==NULL ) start=filename; else ++start;
	    end = strrchr(start,'.');
	    if ( end==NULL ) end=start+strlen(start);
	    fprintf( out,"\n\tup-a-double-id=\"%.*s\"", (int) (end-start), start );
	}

	if ( figure->music_description_ids!=NULL )
	    fprintf( out,"\n\tmusic-description-ids=\"%s\"", figure->music_description_ids );
	else if ( figure->include_md!=NULL )
	    fprintf( out,"\n\tmusic-description-ids=\"%s\"", makeMusicDescriptionID( buf1, sizeof(buf1), figure->include_md ));

	figureMask(figure,figureMasks);
	fprintf( out, "\n\tfigure-mask=\"0x" );
	for ( i=FIG_INT_CNT-1; i>=0 ; --i ) {
	    // Java refuses to parse 0x80000000, claiming an overflow. So I have to use negative numbers which is weird
	    if ( ((int) figureMasks[i])<0 )
		fprintf( out, "-%08x", -figureMasks[i]);
	    else
		fprintf( out, "+%08x", figureMasks[i]);
	    if ( i!=0 ) fputc(',',out);
	}
	fprintf( out, "\"" );
    }
    if ( figure->original_formation != figure->formation )
	fprintf( out, "\n      original-formation=\"%s\"",
	    startFormationNames[figure->original_formation] );
    if ( figure->original_improper != figure->improper_mask ) {
	int k, any=0;
	fprintf( out, "\n      original-improper=\"" );
	if ( figure->original_improper==0 )
	    fprintf( out, "0" );
	else for ( k=0; k<32; ++k ) {
	    if ( figure->original_improper&(1<<k) ) {
		if ( any )
		    putc(',',out);
		fprintf( out, "%s", roleNames[k] );
		any = true;
	    }
	}
	putc('\"',out);
    }
    if ( figure->original_progression != figure->progression )
	fprintf( out, "\n      original-progression=\"%d\"",
	    figure->original_progression );
    if ( figure->improper_mask!=0 ) {
	int k, any=0;
	fprintf( out, "\n      improper=\"" );
	for ( k=0; k<32; ++k ) {
	    if ( figure->improper_mask&(1<<k) ) {
		if ( any )
		    putc(',',out);
		fprintf( out, "%s", roleNames[k] );
		any = true;
	    }
	}
	putc('\"',out);
    }
    if ( figure->becket_style!=bs_none )
	fprintf( out, "\n      becket=\"%s\"", becketStyle[figure->becket_style] );
    if ( figure->minorset_spacing!=0 )
	fprintf( out, "\n      minorset-spacing=\"%d\"", figure->minorset_spacing );
    if ( figure->grid_pos!=NULL && figure->grid_pos[0]!=NULL ) {
	int i;
	char buf1[40], buf2[40];
	fprintf( out, "\n      grid-pos=\"%s,%s",
		formatFloat(buf1,figure->grid_pos[0]->x),
		formatFloat(buf2,figure->grid_pos[0]->y));
	for ( i=1; figure->grid_pos[i]!=NULL; ++i )
	    fprintf( out, ";%s,%s",
		    formatFloat(buf1,figure->grid_pos[i]->x),
		    formatFloat(buf2,figure->grid_pos[i]->y));
	fprintf( out, "\"" );
    }
    fprintf( out, "\n      units=\"%d\" starting-position=\"%s\">\n\n",
	    figure->units, startFormationNames[figure->formation] );
#if 0
    if ( figure->notes ) {	/* Entities should already be in entities when needed */
	fprintf( out, "    <notes>" );
	fprintf( out, "%s", figure->notes );
	fprintf( out, "</notes>\n" );
    }
#endif
    if ( figure->html_notes ) {	/* Entities should already be in entities when needed */
	fprintf( out, "    <html-notes>" );
	fprintf( out, "%s", figure->html_notes );
	fprintf( out, "</html-notes>\n\n" );
    }
    if ( (flags & DOFC_ADDCALLS) && (danceIsPublicDomain(figure) || danceHasPublicDomainLicense(figure)) ) {
	if ( figure->call_cnt!=0 || ( figure->choreo_cnt!=0 && figure->sequence[0].figure->call_cnt!=0 )) {
	    fprintf( out,"    <html-calls>\n" );
	    dumpCalls(out,figure,buf1,sizeof(buf1));
	    fprintf( out,"    </html-calls>\n\n" );
	}
    }
/*
    if ( figure->part_cnt!=1 )
	fprintf( out, "    <parts>%d</parts>\n", figure->part_cnt );
*/

    danceOutputCallList(out,figure->calls, figure->call_cnt, figure->part_cnt );

    if ( figure->include_md!=NULL )
	fprintf( out, "    <music-include filename=\"%s\"/>\n", figure->include_md );
    else
	danceOutputMusicDescription(out,&figure->music);

    if ( figure->minorset1_bar!=-1 && !(flags&DOFC_NOROLES) ) {
	fprintf( out, "\n    <minorset1 bar=\"%d\" dont-show-couples-above-me=\"%d\" ",
		figure->minorset1_bar, figure->minorset1_dont_show_couples_above_me );
	if ( figure->minorset1_end_bar!=-1 )
	    fprintf( out, "end-bar=\"%d\" ", figure->minorset1_end_bar );
	if ( figure->minorset1_same_bb )
	    fprintf( out, "same_bb=\"true\" " );
	fprintf( out, "/>\n\n" );
    }

    if ( figure->whirl_bar!=figure->whirl_end_bar ) {
	fprintf( out, "\n    <whirl bar=\"%d\" end-bar=\"%d\"/>\n",
		figure->whirl_bar, figure->whirl_end_bar );
    }

    if ( figure->bbs!=NULL ) {
	BBList *bbs = figure->bbs;

	if ( bbs->automatic )
	    fprintf( out, "\n    <bbs initial=\"auto\">\n" );
	else
	    fprintf( out, "\n    <bbs initial=\"%d,%d,%d,%d\">\n", bbs->bb.minx, bbs->bb.miny, bbs->bb.maxx, bbs->bb.maxy );
	for ( bbs = bbs->next ; bbs!=NULL ; bbs=bbs->next ) {
	    if ( bbs->automatic )
		fprintf( out, "      <auto" );
	    else if ( bbs->fixed )
		fprintf( out, "      <fixed" );
	    else
		fprintf( out, "      <bb minx=\"%d\" miny=\"%d\" maxx=\"%d\" maxx=\"%d\"",
			bbs->bb.minx, bbs->bb.miny, bbs->bb.maxx, bbs->bb.maxy );
	    if ( bbs->extra_frames!=0 )
		fprintf( out, " extra-frames=\"%d\"", bbs->extra_frames );
	    if ( bbs->millibars!=0 )
		fprintf( out, " bars=\"%s\"", formatMillibars(buf1,bbs->millibars ));
	    fprintf( out, " />\n" );
	}
	fprintf( out, "    </bbs>\n" );
    }

    if ( flags & DOFC_NOROLES )
	/* Do nothing */;
    else if ( figure->is_figure ) {
	for ( i=0; i<figure->people; ++i )
	    danceOutputPerson(out,true,&figure->dancers[i], figure->calls,show_mbars_in_output);
    } else {
	if ( debug )
	    for ( i=0; i<figure->people; ++i )
		danceOutputPerson(out,true,&figure->dancers[i], figure->calls,show_mbars_in_output);
	
	for ( i=0; i<figure->role_cnt; ++i )
	    danceOutputPerson(out,false,&figure->roles[i], figure->calls,show_mbars_in_output);
    }

    if ( figure->comp_cnt!=0 ) {
	for ( i=0; i<figure->comp_cnt; ++i )
	    danceOutputFigureContents(out,figure->components[i],flags,NULL);
    }
    for ( i=0; i<figure->choreo_cnt; ++i ) {
	fprintf( out, "    <choreography figure=\"%s\" ", figure->sequence[i].figure->name );
	if ( figure->sequence[i].active!=0 )
	    fprintf( out, "active=\"%d\" ", figure->sequence[i].active );
	if ( figure->sequence[i].part!=0 )
	    fprintf( out, "part=\"%d\" ", figure->sequence[i].part );
	if ( figure->sequence[i].no_one_out_at_top!=0 )
	    fprintf( out, "no-one-out-at-top=\"true\" " );
	if ( figure->sequence[i].first_set!=0 )
	    fprintf( out, "first-set=\"true\" " );
	if ( figure->sequence[i].clockwise!=0 )
	    fprintf( out, "clockwise=\"true\" " );
	if ( figure->sequence[i].time_scale!=0 && figure->sequence[i].time_scale!=1 )
	    fprintf( out, "time-scale=\"%.2f\" ", figure->sequence[i].time_scale );
	fprintf( out, "/>\n" );
    }

    if ( flags & DOFC_NOROLES )
	/* Do Nothing */;
    else if ( figure->face_cnt!=0 ) {
	for ( i=0; i<figure->face_cnt; ++i ) {
	    struct faceImage *fi = &figure->faceImages[i];
	    if ( fi->facename!=NULL ) {
		fprintf( out, "    <face " );
		if ( fi->person!=-1 )
		    fprintf( out, "person=\"%d\" ", fi->person );
		if ( fi->sex!=s_unknown )
		    fprintf( out, "sex=\"%c\" ", sexChars[fi->sex] );
		fprintf( out, "filename=\"%s\"", fi->facename );
		if ( fi->facescale!=1 )
		    fprintf( out, " scale=\"%.1f\"", fi->facescale);
		if ( fi->hide_normal )
		    fprintf( out, " hide-normal=\"1\"" );
		fprintf( out, " />\n" );
	    }
	}
    }
    fprintf( out, figure->is_figure ? "  </figure>\n" :
		  figure->is_component ? "   </component>\n" :
		  "  </dance>\n" );
}

int danceOutputFigure(const char *filename, Dance *dance) {
    FILE *out = fopen(filename,"w");
    if ( out==NULL )
return( 0 );

    danceOutputHeader(out,dance->style,dance->substyle);
    danceOutputFigureContents(out,dance,false,NULL);
    danceOutputTrailer(out);
    fclose(out);
return( 1 );
}

int danceOutputDebugFigure(const char *filename, Dance *dance) {
    FILE *out = fopen(filename,"w");
    if ( out==NULL )
return( 0 );

    danceOutputHeader(out,dance->style,dance->substyle);
    danceOutputFigureContents(out,dance,DOFC_DEBUG,NULL);
    danceOutputTrailer(out);
    fclose(out);
return( 1 );
}

int danceOutputLibrary(const char *filename, FigureLib *lib) {
    Figure **call;
    FILE *out = fopen(filename,"w");
    if ( out==NULL )
return( 0 );

    danceOutputHeader(out,lib->style,lib->substyle);
    for ( call=lib->figures; call!=NULL && *call!=NULL; ++call )
	danceOutputFigureContents(out,*call,false,NULL);
    danceOutputTrailer(out);
    fclose(out);
return( 1 );
}

int danceOutputLibraryInfo(const char *filename, FigureLib *lib) {
    Figure **call;
    FILE *out = fopen(filename,"w");
    if ( out==NULL )
return( 0 );

    danceOutputHeader(out,lib->style,lib->substyle);
    for ( call=lib->figures; call!=NULL && *call!=NULL; ++call )
	danceOutputFigureContents(out,*call,DOFC_ADDMBARS,NULL);
    danceOutputTrailer(out);
    fclose(out);
return( 1 );
}

void danceOutputAddDanceToMusicDB(FILE *out,Dance *dance, char *filename ) {
    danceOutputFigureContents(out,dance,DOFC_NOROLES|DOFC_EXTRA|DOFC_ADDCALLS, filename);
}

void danceOutputAddDanceToDB(FILE *out,Dance *dance, char *filename ) {
    danceOutputFigureContents(out,dance,DOFC_NOROLES|DOFC_EXTRA, filename);
}
