Subversion Repositories svn1-original

Rev

Rev 359 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*************************************************************************
*           Copyright (C) 1995 Embedded Solutions
*                       All rights reserved
*
* file:     src\report.c
*
* purpose:  PREFIX -
*
* functions
*       report                  - Report menu
*       pri_team                - Print in team order
*       pri_leg                 - Print in given leg finishing time
*       p_place                 - Print place information
*       p_team                  - Print team information
*       pri_eleg                - Print on elapsed times for given leg
*       pe_place                - Print place and elapsed time information
*       pe_team                 - Print team and elapsed time information
*       pri_final               - Print final results
*       pri_interim             - Print interim results
*       print_class_header      - Print a class header
*       print_class_stats       - Generate the class stats
*       print_legend            - Print the legend
*       px_place                - Return place data with NE and V indication
*       ck_data                 - Check data for bad times
*       srt_place               - Update placing information
*       do_big_sort             - Main sort routine for final data
*       sort                    - Sort in memory buffer
*       sort_comp               - qsort comparison function
*       load                    - load report data into memory
*       gen_stats               - Generate all the stats
*
* programmer: David Purdie
*
* revision  date        by      reason
*    e388   11-Oct-88           Option in the final printout to only
*                               produce the main result sheet. The leg
*                               placing printout is not produced.
*
*                               Changes to the display format of unknown
*                               times of disqualified teams. Only affects
*                               the leg-printouts.
*                               Disqualified teams show as -- -- --
*                               Otherwise they show as     ** ** **
*                               or a valid time.
*
*   e339    31-Oct-88   DDP     Added the "Interim Report" facility
*   00.0    27/01/95    DDP     Tidies up the program and formatted the file
*   00.1    06-sep-02   DDP     Added support for HTML report generation
*
**************************************************************************/

#include    <stdio.h>
#include    "consts.h"
#include    "structs.h"
#include    "proto.h"
#include    "mainwindow.h"

void pri_awards_html(void);
void pri_summary_html(void);
void pri_awards(void);
void pri_master_index(void);
char *placing ( int place );
void pri_name_index(void);
void pri_name_index_body( void );
void pri_all_reports (void );
void pri_leg_body(int leg);
void pri_eleg_body(int leg);
void pri_csv_data ( void );


#define MAX_PLACE 11
const char * place_text[] =
{
    "Zero'th",
    "First",
    "Second",
    "Third",
    "Fourth",
    "Fifth",
    "Sixth",
    "Seventh",
    "Eighth",
    "Ninth",
    "Tenth",
    "Eleventh"
};

int         sort_leg;
int         sort_mode;
bool        sort_withEquestrian;
bool        sort_afterEquestrianLeg;
report_type report_html = text;
bool        report_all = FALSE;

/* Parameters used by the sort routine to govern its actions */

#define S_L     1                                /* Elasped times */
#define S_LE    2                                /* Leg end time */
#define S_LC    3                                /* Elapsed times per class */
#define S_LEC   4                                /* Leg end time per class */
#define S_FIN   5                                /* Sort on finish time at given leg */
#define S_TEAM  6                                /* Sort on team order */
#define S_CLASS 7                                /* Sort on class/team order */
#define S_LC_NE 8                                /* Elapsed times per class, with NE sorted by real class */
#define S_IFIN  9                                /* Sort on elapsed time at given leg */


/*
**  Various checking modes
*/
#define C_ELAPSED   1                            /* Check elapsed times */
#define C_END       2                            /* Check end times */
#define C_DISQUAL   3                            /* Check disqualified teams */

/*
**  Data
*/
ty_s_data  *sort_data = 0;                       /* pointer to memory */
ty_s_aux   *sort_aux = 0;                        /* pointer to aux sort info */
ty_s_namedata *sort_name_data = 0;               /* pointer to name info */
unsigned    int sort_num;                        /* Number in the array */
unsigned    int sort_num_data;                   /* Number in the array */
ty_stats        stats;                           /* Holds statistics */

/*========================================================================
 *
 *  Print in team order
 *
 *  Purpose:
 *      This function is called to Print in team order
 *      This function may also be used to create an HTML suite of files
 *      within the result set
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_team(void)
{
    int         i, k;
    team_type   team_buf;

    if( !open_printer( "", "name", 132, report_html, "Team Names" ) )
        return;

    /*
     * Print out the data 
     * Print out the header
     */

    print( "\n" );

    print_underline( TRUE );
    print( "%-*s %-*s %-*s", MAX_TM_NAME + 5, "Entry number and name",
               LEN_CLASS_NAME, "Category",
               config.num_countries == 0 ? 1 : LEN_CNTRY_NAME,
               config.num_countries == 0 ? "" : "Country" );
    for( k = 0; k < MAX_MEMB; k++ )
    {
        print( " %-*s", MAX_PERSON_NAME, config.leg_name[k] ? config.leg_name[k] : "Competitor"  );
    }
    print_underline( FALSE ) ;
    print( "\n" );

    for( i = config.min_team; i <= config.max_team; i++ )
    {
        if( valid_field( i ) && g_record( i, &team_buf ) )
        {
            /*
            **  If printing an HTML report then we need to mark
            **  the entry with a reference so that we can link to it
            */
            if ( report_html == html )
            {
                print( "<A NAME=\"Team_%04d\">",team_buf.numb );
                print( "</A>" );
            }

            /*
            **  Basic information
            **      - Team number - with Xref back to full result
            **      - Full team name
            **      - Full categoray name - with Xref to category results
            **      - Country name
            */
            if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "finish" ,"html")), team_buf.numb );
            print( "%4d",       team_buf.numb );
            if ( report_html == html ) print( "</A>" );

            print( " %-*s ",     MAX_TM_NAME, team_buf.name );

            if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">",url_encode(p_filename(filebase, config.team_class[team_buf.teamclass - 1].abr ,"html")), team_buf.numb );
            print( "%-*s",     LEN_CLASS_NAME, team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].full_name );
            if ( report_html == html ) print( "</A>" );

            print( " %-*s",     config.num_countries == 0 ? 1 : LEN_CNTRY_NAME,
                                config.num_countries == 0
                                || team_buf.country ==
                                0 ? "" : config.country_name[team_buf.country - 1].full_name );

            for( k = 0; k < MAX_MEMB; k++ )
                print( " %-*s", MAX_PERSON_NAME, team_buf.members[k].name );

            print( "\n" );
        }
    }
    close_printer();
}

/*========================================================================
 *
 *  Print in name order
 *
 *  Purpose:
 *      This function is called to print a list of all known competitors
 *      This function may also be used to create an HTML suite of files
 *      within the result set
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/
void pri_name_index_body( void )
{
    ty_s_namedata *ptr;
    team_type   team_buf;
    unsigned    num;
    int i;
    unsigned int k;
    int num_names;

    /*
    **  Determine the number of names to allow for
    *   Based on the total number of teams
    */
    num = config.max_team - config.min_team + 1 ;
    num *= MAX_MEMB;

    sort_name_data = ( ty_s_namedata * ) calloc ( num , sizeof( ty_s_namedata ) );

    if( sort_name_data == 0 )
    {
        MainWindow::showMessage("Error in allocating memory");
        return;
    }

    /*
    **  Read all teams an extract name information
    */
    ptr = sort_name_data;
    for( i = config.min_team; i <= config.max_team; i++ )
    {
        if( valid_field( i ) && g_record( i, &team_buf ) )
        {
            num_names = 0;
            for( k = 0; k < MAX_MEMB; k++ )
            {
                if ( team_buf.members[k].name[0] )
                {
                    ptr->team = i;
                    ptr->leg = k;
                    ptr->teamclass = team_buf.teamclass;
                    strncpy( ptr->name,team_buf.members[k].name, sizeof(team_buf.members[k].name));
                    ptr++;
                    num_names++;
                }
            }

            if ( num_names == 0 )
            {
                ptr->team = i;
                ptr->leg = 0;
                ptr->teamclass = team_buf.teamclass;
                strncpy( ptr->name,team_buf.name, sizeof(team_buf.members[k].name));
                ptr++;
            }

        }
    }
    sort_num_data = ptr - sort_name_data;

    /*
    **  Now stort the entries by name:team:leg
    */
    qsort( ( char * ) sort_name_data, sort_num_data, sizeof( ty_s_namedata ), sort_comp_cname );

    /*
    **  Now generate the report
    */

    if( !open_printer( "", "competitor", 80, report_html, "Competitor Names" ) )
        return;

    /*
     * Print out the data 
     * Print out the header
     */
    print( "\n" );

    print_underline( TRUE );
    print( "%-*s %-*s %-*s %-*s", MAX_TM_NAME + 5, "Competitor name",
               6, "Leg",
               5, "Team",
               LEN_CLASS_NAME, "Category"
               );
    print_underline( FALSE ) ;
    print( "\n" );

    ptr = sort_name_data;
    for( k = 1; k <= sort_num_data; k++, ptr++ )
    {
        print( "%-*s", MAX_TM_NAME + 5, ptr->name );
        print( " " );
        
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "finish" ,"html")), ptr->team );
        print( "%-*d", 6, ptr->leg + 1 );
        if ( report_html == html ) print( "</A>" );
        print( " " );

        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), ptr->team );
        print( "%-*d", 5, ptr->team );
        if ( report_html == html ) print( "</A>" );
        print( " " );

        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">",(p_filename(filebase, config.team_class[ptr->teamclass - 1].abr ,"html")), ptr->team );
        print( "%-*s", LEN_CLASS_NAME, ptr->teamclass == 0 ? "" : config.team_class[ptr->teamclass - 1].abr );
        if ( report_html == html ) print( "</A>" );
        print( " " );

        if ( ptr->multi ) print( "* ");
        print( "\n" );
    }

    print_legend( -1, 0 );
    close_printer();
    
}

/*========================================================================
 *
 *  Print in given leg finishing time
 *
 *  Purpose:
 *      This function is called to Print in given leg finishing time
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_leg_body(int leg)    
{
    ty_s_data  *ptr;
    int         i, k;

    /*
     * Sort the data in finishing order 
     */

    ck_data( leg, C_END );                      /* Check data for this leg */
    sort_team_data( leg, S_FIN, true );         /* Sort the data */


    /*
     * Now print the data on the printer 
     */
    if( !open_printer( "",
                        tprintf("lg%1.1d", leg ),
                        80,
                        report_html,
                        leg ? tprintf ("Finish order for Leg %d.", leg)
                            : "Final team finish order"
                     ) )
    {
        return;
    }

    /*
     * Print out the data 
     */
    print( "PRELIMINARY RESULTS ONLY\n\n" );

    print_underline( TRUE );
    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
    print( "|" );
    print( "%4s %4s %-8s  ", "Team", "Plce", "Time" );
    print( "%4s %4s %-8s\n", "Team", "Plce", "Time" );
    print_underline( FALSE );

    for( ptr = sort_data, k = 0, i = config.min_team; i <= config.max_team; )
    {
        p_place( ptr++, leg, k++ );
        p_place( ptr++, leg, k++ );
        print( "|" );
        while( i <= config.max_team && !valid_field( i ) )
            i++;
        p_team( i++, leg );
        while( i <= config.max_team && !valid_field( i ) )
            i++;
        p_team( i++, leg );
        print( "\n" );
    }

    print_underline( TRUE );
    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
//    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
    print( "|" );
//    print( "%4s %4s %-8s  ", "Team", "Plce", "Time" );
    print( "%4s %4s %-8s\n", "Team", "Plce", "Time" );
    print_underline( FALSE );

    for( ptr = sort_data, k = 0, i = config.min_team; i <= config.max_team; )
    {
        p_place( ptr++, leg, k++ );
//        p_place( ptr++, leg, k++ );
        print( "|" );
        while( i <= config.max_team && !valid_field( i ) )
            i++;
        p_team( i++, leg );
//        while( i <= config.max_team && !valid_field( i ) )
//            i++;
//        p_team( i++, leg );
        print( "\n" );
    }
    

    /*
     * Insert the leg statistics 
     */

    gen_stats();                               /* Generate all stats */
    print( "\nLeg statistics\n" );
    print( "Fastest team: %4d time : %s.",
           stats.fast.team[leg][0], time_a( stats.fast.time[leg][0] ) );
    print( " Average time: %s\n", time_a( stats.average[leg][0] ) );

    close_printer();
}

/*========================================================================
 *
 *  Print place information
 *
 *  Purpose:
 *      This helper function is called to Print place information
 *      in a 20-character field
 *
 *  Parameters:
 *      ptr         Address of the place data
 *      leg         Leg to print
 *      k           Current index into sorted array. Simply
 *                  used to determine if the entry is valid
 *                  or if it should be space-filled
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void p_place( ty_s_data * ptr, int leg, unsigned k )
{
    if( k < sort_num )
    {
        print( "%4.4s %4d %8s  ",
               px_place(-1, ptr->place, false, false, ptr->flags ),
               ptr->team, time_fa( ptr->leg[leg], ptr->flags.disqualified ) );
    }
    else
    {
        print( "%20s", "" );
    }
}

/*========================================================================
 *
 *  Print team information
 *
 *  Purpose:
 *      This helper function is called to Print team information
 *      in a 20-character field
 *
 *  Parameters:
 *      i           team to print
 *      leg         Leg to print
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void p_team( int i, int leg )
{
    ty_s_data  *ptra;                            /* Pointer to sort data */
    int         found = FALSE;
    unsigned    j;

    if( valid_field( i ) )
    {
        ptra = sort_data;
        for( j = 1; j <= sort_num; j++, ptra++ )
            if( i == ptra->team )
            {
                found = TRUE;
                break;
            }
    }
    if( found )
    {
        print( "%4d %4.4s %8s  ",
               ptra->team,
               px_place( -1, ptra->place, false, false, ptra->flags ),
               time_fa( ptra->leg[leg], ptra->flags.disqualified ) );
    }
    else
    {
        print( "%20s", "" );
    }
}

/*========================================================================
 *
 *  Print on elapsed times for given leg
 *
 *  Purpose:
 *      This function is called to Print on elapsed times for given leg
 *
 *  Parameters:
 *      leg             - Leg number to print
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_eleg_body( int leg)
{
    ty_s_data  *ptr;
    int         i, k;

    /*
     * Sort the data in finishing order
     */

    ck_data( leg, C_ELAPSED );                      /* Check data for this leg */
    sort_team_data( leg, S_IFIN, true );            /* Sort the data on elapsed time */


    /*
     * Now print the data on the printer
     */
    if( !open_printer( "", tprintf( "le%1.1d", leg ),
                       80,
                       report_html,
                       leg ? tprintf( "Elapsed time order for Leg %d.", leg )
                           : tprintf( "Final elapsed team finishing order." )
                     ) )
    {
        return;
    }

    /*
     * Print out the data
     */
    print( "PRELIMINARY RESULTS ONLY\n\n" );

    print_underline( TRUE );
    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
    print( "|" );
    print( "%4s %4s %-8s  ", "Team", "Plce", "Time" );
    print( "%4s %4s %-8s\n", "Team", "Plce", "Time" );
    print_underline( FALSE );
    

    for( ptr = sort_data, k = 0, i = config.min_team; i <= config.max_team; )
    {
        pe_place( ptr++, leg, k++ );
        pe_place( ptr++, leg, k++ );
        print( "|" );
        while( i <= config.max_team && !valid_field( i ) )
            i++;
        pe_team( i++, leg );
        while( i <= config.max_team && !valid_field( i ) )
            i++;
        pe_team( i++, leg );
        print( "\n" );
    }

    print( "\n\n" );
    print_underline( TRUE );
    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
//    print( "%4s %4s %-8s  ", "Plce", "Team", "Time" );
    print( "|" );
//    print( "%4s %4s %-8s  ", "Team", "Plce", "Time" );
    print( "%4s %4s %-8s\n", "Team", "Plce", "Time" );
    print_underline( FALSE );


    for( ptr = sort_data, k = 0, i = config.min_team; i <= config.max_team; )
    {
        pe_place( ptr++, leg, k++ );
//        pe_place( ptr++, leg, k++ );
        print( "|" );
        while( i <= config.max_team && !valid_field( i ) )
            i++;
        pe_team( i++, leg );
//        while( i <= config.max_team && !valid_field( i ) )
//            i++;
//        pe_team( i++, leg );
        print( "\n" );
    }
    

    /*
     * Insert the leg statistics
     */

    gen_stats();                               /* Generate all stats */
    print( "\nLeg statistics\n" );
    print( "Fastest team: %4d time : %s.",
           stats.fast.team[leg][0], time_a( stats.fast.time[leg][0] ) );
    print( " Average time: %s\n", time_a( stats.average[leg][0] ) );

    close_printer();
}

///*========================================================================
// *
// *  Print place information
// *
// *  Purpose:
// *      This helper function is called to Print place and elapsed information
// *      in a 20-character field: Place:Team:ElapsedTime
// *
// *  Parameters:
// *      ptr         Address of the place data
// *      leg         Leg to print
// *      k           Current index into sorted array. Simply
// *                  used to determine if the entry is valid
// *                  or if it should be space-filled
// *
// *  Returns:
// *      Nothing
// *
// *========================================================================*/

void pe_place( ty_s_data * ptr, int leg, unsigned k )
{
    if( k < sort_num )
    {
        print( "%4.4s %4d %8s  ",
               px_place( -1, ptr->place, false, false, ptr->flags ),
               ptr->team,
               time_fa( ptr->lege[leg], ptr->flags.disqualified ) );
    }
    else
    {
        print( "%20s", "" );
    }
}

/*========================================================================
 *
 *  Print team information
 *
 *  Purpose:
 *      This helper function is called to Print team and elapsed time
 *      information in a 20-character field
 *
 *  Parameters:
 *      i           Team to print
 *      leg         Leg to print
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pe_team( int i, int leg )
{
    ty_s_data  *ptra;                            /* Pointer to sort data */
    int         found = FALSE;
    unsigned    j;

    if( valid_field( i ) )
    {
        ptra = sort_data;
        for( j = 1; j <= sort_num; j++, ptra++ )
            if( i == ptra->team )
            {
                found = TRUE;
                break;
            }
    }
    if( found )
    {
        print( "%4d %4.4s %8s  ",
               ptra->team,
               px_place( -1, ptra->place, false, false, ptra->flags ),
               time_fa( ptra->lege[leg], ptra->flags.disqualified ) );
    }
    else
    {
        print( "%20s", "" );
    }
}

/*========================================================================
 *
 *  Print final results in HTML
 *
 *  Purpose:
 *      This function is called to Print final results with HTML formatting
 *      All result files are created
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_final_html(void)
{
    /*
    **  Generate ALL results with HTML tags
    */
    report_html = html;
    pri_final();
    pri_final_teamOrder();

    report_html = printed;
    pri_final();
    pri_final_teamOrder();
    report_html = text;
}


/*========================================================================
 *
 *  Print final results
 *
 *  Purpose:
 *      This function is called to Print final results
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_final(void)
{
    ty_s_data  *ptr;
    team_type   team_buf;
    unsigned    i;
    int         j, last_class;
    char        *report_title;
    bool        class_done[MAX_CLASS+1];
    int         lcount;
    bool        isNeClass = false;
    int         place;


    ck_data( -1, C_ELAPSED );

    /*
    **  Sort on every thing
    **  Then generate all the stats too
    */
    do_big_sort();
    gen_stats();

    /*
     * Now print the data on the printer 
     */
    if( !open_printer( "", "finish", 132, report_html, "Finishing Order" ) )
        return;

    /*
     * Print out the data 
     */
    print_class_header( -1, TRUE );                     /* Print the header */
    sort_team_data( 0, S_L, true );                     /* Re-sort on elapsed time */
    lcount = 0;
    for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
    {
        if ( ptr->teamclass == config.nonequestrian_class )
            continue;

        g_record( ptr->team, &team_buf );

        /*
        ** If this is a NE team then dummy up some of the data that hasn't been stored in team_buf
        */
        if (ptr->flags.non_equestrian)
        {
            team_buf.leg[0].l_place = sort_aux[ptr->team].leq_place[0];
            team_buf.leg[0].lc_place = sort_aux[ptr->team].lq_place[0];;
        }

        /*
        **  If printing an HTML report then we need to mark
        **  the entry with a reference so that we can link to it
        */
        if ( report_html == html )
        {
            print( "<A NAME=\"Team_%04d\"></A>",team_buf.numb );
        }

        /*
        **  Print the basics (Finishing order)
        **      - Place within complete field
        **      - Team number - with HTML reference to team file
        **      - Class
        */
        if ( report_html == printed && lcount % 5 == 4 ) print_underline( TRUE );
        print( "%4.4s ", px_place( 0, team_buf.leg[0].l_place, false, true, ptr->flags ) );
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), team_buf.numb );
        print( "%4d",  team_buf.numb );
        if ( report_html == html ) print( "</A>" );

        print( " %-*s", 3, team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].abr );

        /*
        **  Print the per-leg data
        **      - Time
        **      - Leg place
        **      - End place
        */
        for( j = 1; j <= config.num_legs; j++ )
        {
            bool isEquestrianLeg = (j == config.equestrian_leg && team_buf.flags.non_equestrian);

            /*
            **  Ensure that non-equestrian leg data is not displayed
            */
            if ( isEquestrianLeg )
            {
                print( "  %-8s %4.4s %4.4s", "-- NE --", "NE","NE");
            }
            else
            {
                print( "  %-8s", time_a( team_buf.leg[j].elapsed ));
                if ( config.num_legs != 1 ) print( " %4.4s",      px_place( j, team_buf.leg[j].l_place, false, false, ptr->flags ));
                if ( config.num_legs != 1 ) print( " %4.4s",   px_place( j, team_buf.leg[j].le_place,true , true, ptr->flags ));
                if ( config.num_legs == 1 ) print( " %-*s ",  MAX_TM_NAME, team_buf.name );
            }
        }

       /*
        **  Print the trailer (Finishing order)
        **      - Total time
        **      - Category place - with reference to category file
        */
        if ( config.num_legs != 1 ) print( "  %-8s ", time_a( ptr->lege[0] ) );
        
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">",url_encode(p_filename(filebase, config.team_class[team_buf.teamclass - 1].abr ,"html")), team_buf.numb );
        print( "%4.4s", px_place( 0, team_buf.leg[0].lc_place, false, false, ptr->flags) );
        if ( report_html == html ) print( "</A>" );
        if ( report_html == printed && lcount %5 == 4 ) print_underline( FALSE );
        lcount++;
        print( "\n" );
    }

    print_class_stats( -1, TRUE );              /* Print statistics */
    print_legend(-1, 1 );                       /* Print the legend */
    close_printer();                            /* Close the printer */

    /*
     * Now produce a breakdown on a class by class basis 
     * Now print out the class placement information
     */

    sort_team_data( 0, S_LC, true );           /* Generate class placement data */
    last_class = -1;                           /* Invalid class to start with */
    memset ( class_done, 0, sizeof(class_done));

    place = 1;
    for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
    {
        int flags = 0;
        bool eflags;

        /*
        **  Detect a change in the "class"
        **  All data is within the one array of data
        **  Use the in-memory class as this MAY differ from that stored
        **  The non-equestrian class does this.
        */
        if( last_class != ptr->teamclass )
        {
            if( last_class >= 0 )
            {
                print_class_stats( last_class, TRUE );
                print_legend( last_class, 1 );
                close_printer();
            }

            /*
            ** Determine if this is the crazy dummy Non-equestrian class
            */
            isNeClass = (ptr->teamclass == config.nonequestrian_class );
            place = 1;

            report_title = tprintf( "Category results for : %-*s", LEN_CLASS_NAME, ptr->teamclass == 0 ? "" : config.team_class[ptr->teamclass - 1].full_name );

            if( !open_printer( "", config.team_class[ptr->teamclass - 1].abr, 132, report_html, report_title ) )
                continue;
            print_class_header( last_class = ptr->teamclass, TRUE );

            /*
            **  Mark the class as done
            */
            class_done[ptr->teamclass] = TRUE;
            lcount = 0;
        }

        /*
        **  Now read in the team record
        */
        g_record( ptr->team, &team_buf );
        flags = ptr->flags.bad_times;
        eflags = (ptr->teamclass != config.nonequestrian_class && ptr->flags.non_equestrian);

        /*
        ** Dummy up the data for the dummy nonEquestrian Class
        ** Its not stored in the team_buf for backwards compatability
        */
        if( isNeClass)
        {
            for( j = 0; j <= config.num_legs; j++ )
            {
                team_buf.leg[j].lc_place = sort_aux[ptr->team].lq_place[j];
                team_buf.leg[j].lec_place = sort_aux[ptr->team].leq_place[j];;
            }
        }
        else
        {
            if (ptr->flags.non_equestrian)
            {
                team_buf.leg[0].lc_place =  place++; //sort_aux[ptr->team].lq_place[0];
                team_buf.leg[0].lec_place = sort_aux[ptr->team].leq_place[0];
                eflags = false;
            }
        }


        /*
        **  If printing an HTML report then we need to mark
        **  the entry with a reference so that we can link to it
        */
        if ( report_html == html )
            print( "<A NAME=\"Team_%04d\"></A>",team_buf.numb );

        /*
        **  Print the basics
        **      - Place within the class
        **      - Team number - with HTML reference to team file
        **      - Class
        */

        if ( report_html == printed && lcount %5 == 4 ) print_underline( TRUE );
        print( "%4.4s ", px_place( 0, team_buf.leg[0].lc_place, false, false, ptr->flags) );
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), team_buf.numb );
        print( "%4d",  team_buf.numb );
        if ( report_html == html ) print( "</A>" );
        print( " %-*s", 3, team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].abr );

        for( j = 1; j <= config.num_legs; j++ )
        {
            bool isEquestrianLeg = (j == config.equestrian_leg && (ptr->teamclass == config.nonequestrian_class || ptr->flags.non_equestrian));

            /*
            **  Ensure that non-equestrian leg data is not displayed
            */
            if ( isEquestrianLeg )
            {
                print( "  %-8s %4.4s %4.4s", "-- NE --", "NE","NE");
            }
            else
            {
                print( "  %-8s", time_a( team_buf.leg[j].elapsed ));
                if ( config.num_legs != 1 ) print( " %4.4s", px_place( j, team_buf.leg[j].lc_place,  false, false, ptr->flags ));
                if ( config.num_legs != 1 ) print( " %4.4s", px_place( j, team_buf.leg[j].lec_place, true , true, ptr->flags ) );
                if ( config.num_legs == 1 ) print( " %-*s ", MAX_TM_NAME, team_buf.name );
            }
        }

        /*
        **  Print the trailer
        **      - Total time
        **      - Overall place - with reference to overall place file
        */
        if ( config.num_legs != 1 ) print( "  %-8s ", time_a( ptr->lege[0] ) );

        if ( report_html == html )  print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "finish" ,"html")), team_buf.numb );
        print( "%4.4s", px_place( 0, team_buf.leg[0].l_place, false, true, ptr->flags));
        if ( report_html == html )  print( "</A>" );

        if ( report_html == printed && lcount %5 == 4 ) print_underline( FALSE );
        lcount++;
        print( "\n" );
    }

    print_class_stats( last_class, TRUE );
    print_legend(last_class,1);
    close_printer();

    /*
    **  Pickup missed classes and create a report
    */
    for( j = 1; j <= config.num_class; j++ )
    {
        if ( class_done[j] || !config.team_class[j-1].abr[0] )
        {
            continue;
        }
        report_title = tprintf( "Category results for : %-*s", LEN_CLASS_NAME, config.team_class[j - 1].full_name );
        
        if( !open_printer( "", config.team_class[j - 1].abr, 132, report_html, report_title ) )
            continue;
        print_class_header( j-1, TRUE );
        print( "\nThere were no competitors in this class\n" );
        print_legend(j,1);
        close_printer();
    }


    /*
    **  If we are generating an HTML report then we need to create the file
    **  that contains all the team names - the assumption is that this data
    **  is available
    */
    if ( report_html == html || report_html == printed)
    {
        pri_team();
    }

    /*
    **  Generate the awards report.
    **  This is only available as an HTML report
    */
    if ( report_html == html || report_html == printed)
    {
        pri_awards_html();
    }

    /*
    **  Generate the master index page
    */
    if ( report_html == html )
    {
        pri_master_index();
    }

    pri_name_index_body();
}

/*========================================================================
 *
 *  Print final results in Team Order
 *
 *  Purpose:
 *      This function is called to Print final results in Team Order
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_final_teamOrder(void)
{
    ty_s_data  *ptr;
    team_type   team_buf;
    unsigned    i;
    int         j;
    int         lcount;

    ck_data( -1, C_ELAPSED );

    /*
    **  Sort on every thing
    **  Then generate all the stats too
    */
    do_big_sort();
    gen_stats();

    /*
     * Now print the data on the printer 
     */
    if( !open_printer( "", "team_order", 132, report_html, "Team Order" ) )
        return;

    /*
     * Print out the data 
     */
    print_class_header( -1, TRUE );                      /* Print the header */

    ptr = sort_data;
    sort_team_data( 0, S_TEAM, true );                   /* Re-sort on team number */
    lcount = 0;
    for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
    {
        if ( ptr->teamclass == config.nonequestrian_class )
            continue;

        g_record( ptr->team, &team_buf );

        /*
        ** If this is a NE team then dummy up some of the data that hasn't been stored in team_buf
        */
        if (ptr->flags.non_equestrian)
        {
            team_buf.leg[0].l_place = sort_aux[ptr->team].leq_place[0];
            team_buf.leg[0].lc_place = sort_aux[ptr->team].lq_place[0];;
        }

        /*
        **  If printing an HTML report then we need to mark
        **  the entry with a reference so that we can link to it
        */
        if ( report_html == html )
        {
            print( "<A NAME=\"Team_%04d\"></A>",team_buf.numb );
        }

        /*
        **  Print the basics (Finishing order)
        **      - Place within complete field
        **      - Team number - with HTML reference to team file
        **      - Class
        */
        if ( report_html == printed && lcount %5 == 4 ) print_underline( TRUE );
        print( "%4.4s ", px_place( 0, team_buf.leg[0].l_place, false, true, ptr->flags ) );
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), team_buf.numb );
        print( "%4d",  team_buf.numb );
        if ( report_html == html ) print( "</A>" );

        print( " %-*s", 3, team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].abr );

        /*
        **  Print the per-leg data
        **      - Time
        **      - Leg place
        **      - End place
        */
        for( j = 1; j <= config.num_legs; j++ )
        {
            bool isEquestrianLeg = (j == config.equestrian_leg && team_buf.flags.non_equestrian);
            /*
            **  Ensure that non-equestrian leg data is not displayed
            */
            if ( isEquestrianLeg )
            {
                print( "  %-8s %4.4s %4.4s", "-- NE --", "NE","NE");
            }
            else
            {
                print( "  %-8s",                     time_a( team_buf.leg[j].elapsed ));
                if ( config.num_legs != 1 ) print( " %4.4s", px_place(j, team_buf.leg[j].l_place, false, false, ptr->flags ));
                if ( config.num_legs != 1 ) print( " %4.4s", px_place(j, team_buf.leg[j].le_place,true,  true, ptr->flags ) );
                if ( config.num_legs == 1 ) print( " %-*s ", MAX_TM_NAME, team_buf.name );
            }
        }


       /*
        **  Print the trailer (Finishing order)
        **      - Total time
        **      - Category place - with reference to category file
        */
       if ( config.num_legs != 1 ) print( "  %-8s ", time_a( ptr->lege[0] ) );
        
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">",url_encode(p_filename(filebase, config.team_class[team_buf.teamclass - 1].abr ,"html")), team_buf.numb );
        print( "%4.4s", px_place( 0, team_buf.leg[0].lc_place, false, false, ptr->flags ) );
        if ( report_html == html ) print( "</A>" );
        if ( report_html == printed && lcount %5 == 4 ) print_underline( FALSE );
        lcount++;
        print( "\n" );
    }

    print_class_stats( -1, TRUE );              /* Print statistics */
    print_legend(-1, 1 );                       /* Print the legend */
    close_printer();                            /* Close the printer */

}

/*========================================================================
 *
 *  Place to text
 *
 *  Purpose:
 *      This function is called to convert a place to text
 *
 *  Parameters:
 *      place
 *
 *  Returns:
 *      text
 *
 *========================================================================*/

char *placing ( int place )
{
    if ( place > MAX_PLACE )
    {
        return tprintf( "Place: %d", place);
    }
    return tprintf ("%s Place", place_text[place]);
}

/*========================================================================
 *
 *  Print award results
 *
 *  Purpose:
 *      This function is called to Print award results
 *      Keep the page to 80 cols, so that it can be pronted on A4
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/
void pri_awards_html(void)
{
    report_type saved = report_html;
    /*
    **  Generate ALL results with HTML tags
    */
    report_html = html;
    pri_awards();
    report_html = printed;
    pri_awards();
    report_html = saved;
}

/*========================================================================
 *
 *  Print award results
 *
 *  Purpose:
 *      This function is called to Print award results
 *      Keep the page to 80 cols, so that it can be pronted on A4
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/
void pri_awards(void)
{
    int j;
    int i;
    int k;
    int windex;
    int winmax;
    ty_s_data  *ptr;
    team_type   team_buf;
    int         last_class;
    char    solid_line[100];
    bool header_done = false;

    /*
    **  Calculate Summary information
    **  Should cache the data
    */
    t_class_summary sdata;
    calc_class_summary( & sdata );
    

    if( !open_printer( "", "awards", 80, report_html, "Prizes and Awards" ) )
        return;

    memset ( solid_line, 0, sizeof( solid_line ));
    memset ( solid_line, '-', 80 );

    /*
    **  Generate an index for this page
    */
    print( "\n");
    if ( report_html == html )
    {
        print( "<hr>" );
        print( "<A NAME=\"%s\"></A>",url_encode("INDEX"));
    }
    print( "Award Categories - Full Event");

    for( j = 1; j <= config.num_class; j++ )
    {
        /*
        **  Header for the class
        */
        if (!config.team_class[j - 1].abr[0])
            continue;
        if (config.class_winners[j - 1] <= 0) 
            continue;

        winmax = config.class_winners[j-1];
        {
            int valid =   sdata.teamclass[j].valid_ev;
            if ( valid < winmax )
                winmax = valid;
        }

        print( "\n");
        print( "    ");
        if ( report_html == html ) print( "<A HREF=\"#%s\">",url_encode(config.team_class[j-1].full_name));
        print( "%s",  tprintf( "%-*s", LEN_CLASS_NAME ,config.team_class[j-1].full_name ));
        if ( report_html == html ) print( "</A>" );
        print( "  %3d Awards", winmax );
        if ( config.class_winners[j-1] != winmax )
            print( " from a maximum of %3d", config.class_winners[j-1] );
    }

    /*
    **  NE Award Categories
    */
    if ( config.class_ne_winners_by_class )
    {
        print( "\n");
        print( "Award Categories - Non Equestrian");

        for( j = 1; j <= config.num_class; j++ )
        {
            /*
            **  Header for the class
            */
            if (!config.team_class[j - 1].abr[0])
                continue;
            if ( config.class_ne_winners[j-1] <= 0 )
                continue;

            winmax = config.class_ne_winners[j-1];
            {
                int valid =   sdata.teamclass[j].valid_ne;
                if ( valid < winmax )
                    winmax = valid;
            }

            print( "\n");
            print( "    ");
            if ( report_html == html ) print( "<A HREF=\"#%s_NE\">",url_encode(config.team_class[j-1].full_name));
            print( "%s",  tprintf( "%-*s", LEN_CLASS_NAME ,config.team_class[j-1].full_name ));
            if ( report_html == html ) print( "</A>" );
            print( "  %3d Awards", winmax );
            if ( config.class_ne_winners[j-1] != winmax )
                print( " from a maximum of %3d", config.class_ne_winners[j-1] );
        }
    }

    /*
    **  Manual entries
    */
    print( "\n");
    print( "Miscellaneous");

        print( "\n");
        print( "    ");
        if ( report_html == html ) print( "<A HREF=\"#%s_by_cat\">",url_encode("Full Event"));
        print( "%s",  tprintf( "%-*s", LEN_CLASS_NAME ,"Full Event"));
        if ( report_html == html ) print( "</A>" );
        print (" by Category");
    
    if ( config.class_ne_winners_by_class )
    {
        print( "\n");
        print( "    ");
        if ( report_html == html ) print( "<A HREF=\"#%s_by_cat\">",url_encode(config.team_class[config.nonequestrian_class-1].full_name));
        print( "%s",  tprintf( "%-*s", LEN_CLASS_NAME ,config.team_class[config.nonequestrian_class-1].full_name ));
        if ( report_html == html ) print( "</A>" );
        print (" by Category");
    }


    print( "\n");
    print( "    ");
    if ( report_html == html ) print( "<A HREF=\"#%s\">",url_encode("Hall Of Fame"));
    print( "%s",  "Hall Of Fame" );
    if ( report_html == html ) print( "</A>" );

    if (config.awardsfilename[0])
    {
        print( "\n");
        print( "    ");
        if ( report_html == html ) print( "<A HREF=\"#%s\">",url_encode("Additional Awards"));
        print( "%s",  "Additional Awards" );
        if ( report_html == html ) print( "</A>" );
    }

    print( "\n");
    print( "    ");
    if ( report_html == html ) print( "<A HREF=\"#%s\">",url_encode("FASTEST"));
    print( "%s",  "FASTEST" );
    if ( report_html == html ) print( "</A>" );

    /*
    **  Sort the data by class
    */
    sort_team_data( 0, S_LC, true );     /* Generate class placement data */
    last_class = -1;                /* Invalid class to start with */

    /*
    **  Process each category
    */
    print( "\n");
    for( j = 1; ; j++ )
    {
        if (!config.team_class[j - 1].abr[0])
            continue;

        /*
        **  Tail for previous entry
        */
        //if ( config.class_ne_winners_by_class && j == config.nonequestrian_class )
        //    continue;

        if ( j != 1 )
            if ( report_html == html ) print( "<A HREF=\"#%s\">Awards Index</A>",url_encode("INDEX"));

        if ( j > config.num_class  )
            break;

        /*
        **  Header for the class
        */
        print( "\n");
        if ( report_html == html )
        {
            print( "<hr>" );

            if ( !header_done )
            {
                header_done = true;
                if ( report_html == html )
                {
                    print( "<A name=\"%s_by_cat\"></A>",url_encode("Full Event"));
                }
            }

            print( "<A name=\"%s\"></A>",url_encode(config.team_class[j-1].full_name));
        }
        else
        {
            print( "%s\n", solid_line);
        }
        print( "Category: ");
        if ( report_html == html ) print( "<A HREF=\"%s\">",url_encode(p_filename(filebase, config.team_class[j - 1].abr ,"html")));
        print( "%s",  config.team_class[j-1].full_name );
        if ( report_html == html ) print( "</A>" );

        if ( config.class_winners[j-1] <= 0 )
        {
            print( "\n");
            print( "No winners awarded" );
            if ( report_html == html ) print("<br>");
            continue;
        }

        /*
        **  Enties for 'n' the best teams as configured
        */
        windex = 0;                     /* Winners done */
        unsigned int i;
        for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
        {
            if ( ptr->teamclass != j )
            {
                continue;
            }

            /*
            **  Now read in the team record
            */
            if( valid_field( ptr->team ) && g_record( ptr->team, &team_buf ) )
            {
                /*
                **  Ensure we have a valid team
                **  Can't award disqualified teams
                **  Can't award NE teams unless its a NE award
                */
                if ( ptr->flags.bad_times || ptr->flags.disqualified || ptr->flags.vet_check || !ptr->flags.valid )
                    break;

                if ( ptr->teamclass != config.nonequestrian_class  && ptr->flags.non_equestrian )
                    break;

                /*
                **  Count the entry
                */
                windex++;

                /*
                **  If printing an HTML report then we need to mark
                **  the entry with a reference so that we can link to it
                */
                print( "\n");
                if ( report_html == html )
                {
                    print( "<A NAME=\"Team_%04d\">",team_buf.numb );
                    print( "</A>" );
                }

                /*
                **  Basic information
                **      - Team number - with Xref back to full result
                **      - Full team name
                **      - Full categoray name
                */
                print( "%s", placing(windex) );

                print( "  Team Name: ");
                if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), team_buf.numb );
                print( "%-*s ",     MAX_TM_NAME, team_buf.name );
                    if ( report_html == html ) print( "</A>" );

                print( "  Number: ");
                if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "finish" ,"html")), team_buf.numb );
                print( "%4d",       team_buf.numb );
                if ( report_html == html ) print( "</A>" );


                for( k = 0; k < MAX_MEMB; k++ )
                {

                    /*
                    **  Skip equestrian leg in the non-equestion display
                    */
                    if ( k + 1 == config.equestrian_leg && ptr->teamclass == config.nonequestrian_class)
                        continue;

                    print( "\n");
                    print( "    ");
                    print( "%-*s", MAX_PERSON_NAME, config.leg_name[k] ? config.leg_name[k] : "Competitor"  );
                    print( " %-*s", MAX_PERSON_NAME, team_buf.members[k].name );

                    print( "  %-8s", time_a( team_buf.leg[k+1].elapsed ) );
                }

                print( "\n");
                print( "    ");
                print_bold( TRUE );
                print( "%-*s %-*s  %-8s", MAX_PERSON_NAME , "Total" ,MAX_PERSON_NAME, "",time_a( team_buf.leg[0].elapsed ) );
                print_bold( FALSE );
                print( "\n" );
            }


            /*
            **  More to do
            */
            if ( windex >= config.class_winners[j-1] )
            {
                break;
            }
        }
    }

    /*
    ** Non Equestrian winners by category
    */
    if ( config.class_ne_winners_by_class)
    {
        header_done = false;

        /*
        **  Sort the data by class with NE data sorted by real class
        */
        sort_team_data( 0, S_LC_NE, true );      /* Generate class placement data */
        last_class = -1;                   /* Invalid class to start with */

        /*
        **  Only process the Non Equestrian teams in this pass
        */
        print( "\n");
        for( j = 1; ; j++ )
        {
            if (!config.team_class[j - 1].abr[0])
                continue;

            /*
            **  Tail for previous entry
            */
            if ( j == config.nonequestrian_class)
                continue;

            if ( j != 1 )
                if ( report_html == html ) print( "<A HREF=\"#%s\">Awards Index</A>",url_encode("INDEX"));

            if ( j > config.num_class  )
                break;

            /*
            **  Header for the (sub) class
            */
            if ( !header_done )
            {
                header_done = true;
                if ( report_html == html )
                {
                    print( "<A name=\"%s_by_cat\"></A>",url_encode(config.team_class[config.nonequestrian_class-1].full_name));
                }
            }

            print( "\n");
            if ( report_html == html )
            {
                print( "<hr>" );
                print( "<A name=\"%s_NE\"></A>",url_encode(config.team_class[j-1].full_name));
            }
            else
            {
                print( "%s\n", solid_line);
            }
            print( "Category: ");
            if ( report_html == html ) print( "<A HREF=\"%s\">",url_encode(p_filename(filebase, config.team_class[config.nonequestrian_class - 1].abr ,"html")));
            print( "%s",  config.team_class[config.nonequestrian_class-1].full_name );
            if ( report_html == html ) print( "</A>" );

            print (" :: ");

            if ( report_html == html ) print( "<A HREF=\"%s\">",url_encode(p_filename(filebase, config.team_class[j - 1].abr ,"html")));
            print( "%s",  config.team_class[j-1].full_name );
            if ( report_html == html ) print( "</A>" );

            if ( config.class_ne_winners[j-1] <= 0 )
            {
                print( "\n");
                print( "No winners awarded" );
                if ( report_html == html ) print("<br>");
                continue;
            }

            /*
            **  Enties for 'n' the best teams as configured
            */
            windex = 0;                     /* Winners done */
            unsigned int i;
            for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
            {
                if ( ptr->teamclass != config.nonequestrian_class ||  ptr->real_teamclass != j )
                {
                    continue;
                }

                /*
                **  Now read in the team record
                */
                if( valid_field( ptr->team ) && g_record( ptr->team, &team_buf ) )
                {
                    /*
                    **  Ensure we have a valid team
                    **  Can't award disqualified teams
                    **  Can't award NE teams unless its a NE award
                    */
                    if ( ptr->flags.bad_times || ptr->flags.disqualified || ptr->flags.vet_check || !ptr->flags.valid )
                        break;

                    if ( ptr->teamclass != config.nonequestrian_class  && ptr->flags.non_equestrian )
                        break;

                    /*
                    **  Count the entry
                    */
                    windex++;

                    /*
                    **  If printing an HTML report then we need to mark
                    **  the entry with a reference so that we can link to it
                    */
                    print( "\n");
                    if ( report_html == html )
                    {
                        print( "<A NAME=\"Team_%04d\">",team_buf.numb );
                        print( "</A>" );
                    }

                    /*
                    **  Basic information
                    **      - Team number - with Xref back to full result
                    **      - Full team name
                    **      - Full categoray name
                    */
                    print( "%s", placing(windex) );

                    print( "  Team Name: ");
                    if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), team_buf.numb );
                    print( "%-*s ",     MAX_TM_NAME, team_buf.name );
                        if ( report_html == html ) print( "</A>" );

                    print( "  Number: ");
                    if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "finish" ,"html")), team_buf.numb );
                    print( "%4d",       team_buf.numb );
                    if ( report_html == html ) print( "</A>" );


                    for( k = 0; k < MAX_MEMB; k++ )
                    {

                        /*
                        **  Skip equestrian leg in the non-equestion display
                        */
                        if ( k + 1 == config.equestrian_leg && ptr->teamclass == config.nonequestrian_class)
                            continue;

                        print( "\n");
                        print( "    ");
                        print( "%-*s", MAX_PERSON_NAME, config.leg_name[k] ? config.leg_name[k] : "Competitor"  );
                        print( " %-*s", MAX_PERSON_NAME, team_buf.members[k].name );

                        print( "  %-8s", time_a( team_buf.leg[k+1].elapsed ) );
                    }

                    print( "\n");
                    print( "    ");
                    print_bold( TRUE );
                    print( "%-*s %-*s  %-8s", MAX_PERSON_NAME , "Total" ,MAX_PERSON_NAME, "",time_a( team_buf.leg[0].elapsed ) );
                    print_bold( FALSE );
                    print( "\n" );
                }


                /*
                **  More to do
                */
                if ( windex >= config.class_ne_winners[j-1] )
                {
                    break;
                }
            }
        }
    }

    /*
    **  Generate the Hall of Fame information
    */
    print( "\n");
    if ( report_html == html )
    {
        print( "<hr>" );
        print( "<A name=\"%s\"></A>",url_encode("Hall Of Fame"));
    }
    else
    {
        print( "%s\n", solid_line);
    }
    print( "%s",  "Hall of Fame" );

    if ( config.num_fame  )
    {
        for( i = 1; i <= config.num_fame; i++ )
        {
            print( "\n");
            print( "    %-*s", MAX_PERSON_NAME, config.hall_fame[i-1] );
        }
    }
    else
    {
        qDebug( "There are no new stars for the Hall of Fame");
    }
    if ( report_html == html ) print( "\n");
    if ( report_html == html ) print( "<A HREF=\"#%s\">Awards Index</A>",url_encode("INDEX"));




    /*
    **  Insert additional Awards information
    */
    if (config.awardsfilename[0])
    {
        print( "\n");
        if ( report_html == html )
        {
            print( "<hr>" );
            print( "<A name=\"%s\"></A>",url_encode("Additional Awards"));
        }
        else
        {
            print( "%s\n", solid_line);
        }
        print( "%s",  "Additional Awards" );
        print ("\n");

        /*
        **  Read and process the named awards file
        */
        FILE       *adfile = NULL;
        char        line[201];

        QString name = QmConfig::getAddendemFile(config.awardsfilename);
        if (! name.isEmpty())
        {
            adfile = fopen( qPrintable(name), "rt" );  /* Open the file for reading */
        }


        if( adfile )
        {
            while( fgets( line, sizeof(line)-1, adfile ) ) {
                //  Process each line
                //  Attempt to perform some smart text replacements
                //  ie: <TeamNumber:63> <TeamName:63><TeamLegName:1:63>
                print( "%s", line );
            }
            fclose(adfile);
        }
        else
        {
            if ( report_html == html )  print ("<br>");
            print( "\nThe awards file could not be found: %s\n",  adfile);
        }


        if ( report_html == html ) print( "\n");
        if ( report_html == html ) print( "<A HREF=\"#%s\">Awards Index</A>",url_encode("INDEX"));
    }

    /*
    **  Generate the FASTEST information
    */
    print( "\n" );
    print( "\n");
    if ( report_html == html )
    {
        print( "<hr>" );
        print( "<A name=\"%s\"></A>",url_encode("FASTEST"));
    }
    else
    {
        print( "%s\n", solid_line);
    }
    print( "%s",  "FASTEST" );

    /*
    **  Sort the data and then generate the stats - again
    */
    do_big_sort();
    gen_stats();

    for( i = 1; i <= config.num_legs; i++ )
    {
        g_record( stats.fast.team[i][0], &team_buf );

        print( "\n");
        print( "    %-13s ", config.leg_name[i - 1] );
        print( "  Name: ");
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), team_buf.numb );
        print( "%-*s", MAX_PERSON_NAME, team_buf.members[i-1].name );
        if ( report_html == html ) print( "</A>" );
        print( "  Team:");
        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "finish" ,"html")), team_buf.numb );
        print( "%4d" , stats.fast.team[i][0] );
        if ( report_html == html ) print( "</A> " );
        print( "  Time:%s ", time_a( stats.fast.time[i][0] ) );

    }

    if ( report_html == html ) print( "\n");
    if ( report_html == html ) print( "<A HREF=\"#%s\">Awards Index</A>",url_encode("INDEX"));
    print( "\n");
    close_printer();
}

/*========================================================================
 *
 *  pri_master_index
 *
 *  Purpose:
 *      This function is called to create an HTML page that references all
 *      the other pages that have been generated
 *
 *      Assume that they are in the same directory
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/
void pri_master_index_entry(const char *name, const char *text)
{
    print( "<tr><td>");
    print ("<A HREF=\"%s\">%s</A>\n", url_encode(p_filename(filebase, name ,"html")), text );
}


void pri_master_index(void)
{
    int j;

    report_html = html;
    if( !open_printer( "", "index", 132, report_html, "Master Index" ) )
        return;

    /*
    **  Names
    */
    print( "<TABLE border=0 align=center>" );
    pri_master_index_entry( "name", "Team list" );
    pri_master_index_entry( "competitor", "Competitor list" );
    pri_master_index_entry( "finish", "Finishing Order for all Teams" );
    pri_master_index_entry( "team_order", "All Teams with results" );
    pri_master_index_entry( "awards", "Prizes and Awards" );
    print( "<tr><td>\n" );

    print( "\n" );
    for( j = 1; j <= config.num_class; j++ )
    {
        if (!config.team_class[j - 1].abr[0])
            continue;
        pri_master_index_entry( config.team_class[j - 1].abr, tprintf("Category Results for: %s", config.team_class[j-1].full_name) );
    }
        
    print( "</TABLE>" );

    close_printer();
    
    /*
    **  A small page to hold the Leg End displays
    */
    
    if( !open_printer( "", "legindex", 132, report_html, "Master Index with trace data" ) )
        return;

    /*
    **  Names
    */
    print( "<TABLE border=0 align=center>" );
#if 1
    pri_master_index_entry( "name", "Team list" );
    pri_master_index_entry( "competitor", "Competitor list" );
    pri_master_index_entry( "finish", "Finishing Order for all Teams" );
    pri_master_index_entry( "team_order", "All Teams with results" );
    pri_master_index_entry( "awards", "Prizes and Awards" );
    print( "<tr><td>\n" );

    print( "\n" );
    pri_master_index_entry( "summary", "Category Summary" );
    for( j = 1; j <= config.num_class; j++ )
    {
        if (!config.team_class[j - 1].abr[0])
            continue;
        pri_master_index_entry( config.team_class[j - 1].abr, tprintf("Category Results for: %s", config.team_class[j-1].full_name) );
    }
#endif
    print( "<tr><td>\n" );

    print( "\n" );
    for ( int leg = 1; leg <= config.num_legs; leg ++ )
    {
        pri_master_index_entry( tprintf("lg%1.1d", leg), tprintf("Leg End Results for: %d", leg) );    
    }

    print( "<tr><td>\n" );
    print( "\n" );
    
    for ( int leg = 1; leg <= config.num_legs; leg ++ )
    {
        pri_master_index_entry( tprintf("le%1.1d", leg), tprintf("Leg Elapsed Time Results for: %d", leg) );
    }    
        
    
    print( "</TABLE>" );

    close_printer();
    /*
     ** Tell the main system about this new report
     */
    MainWindow::registerReport(getPrinterFile(), "Master Leg Index");
    
}


/*========================================================================
 *
 *  Print interim results
 *
 *  Purpose:
 *      This function is called to Print interim results
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_interim(void)
{
    ty_s_data  *ptr;
    team_type   team_buf;
    unsigned    i;
    int         j, last_class;
    char       *report_title;

    if ( ! report_all )
    {
        ck_data( -1, C_DISQUAL );              /* Check the data - dummy check */
    }
    do_big_sort();                             /* Sort on every thing */
    gen_stats();                               /* Generate the stats too */

    /*
     * Now print the data on the printer 
     */

    if( !open_printer( "", "int", 132, report_html, "Interim Results" ) )
        return;

    /*
     * Print out the data 
     */
    print_class_header( -1, FALSE );                     /* Print the header */

    ptr = sort_data;
    sort_team_data( 0, S_TEAM, true );                   /* Re-sort on team number */
    for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
    {
        if ( ptr->teamclass == config.nonequestrian_class )
            continue;

        g_record( ptr->team, &team_buf );

        print( "%4d %4.4s %-*s",
               team_buf.numb,
               px_place(-1, team_buf.leg[config.num_legs].le_place, false, false, ptr->flags ),
               3,
               team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].abr );

        for( j = 1; j <= config.num_legs; j++ )
        {
            print( "  %-8s", time_fa( team_buf.leg[j].elapsed, ptr->flags.disqualified ));
            if ( config.num_legs != 1 ) print( " %4.4s", px_place(j, team_buf.leg[j].l_place, false, true, ptr->flags ));
            if ( config.num_legs != 1 ) print( " %4.4s", px_place(j, team_buf.leg[j].le_place, false, true, ptr->flags ));
            if ( config.num_legs == 1 ) print( " %-*s ",     MAX_TM_NAME, team_buf.name );
        }

        if ( config.num_legs != 1 ) print( "  %-8s", time_fa( team_buf.leg[0].elapsed, ptr->flags.disqualified ));
        print( " %4.4s\n", px_place(0, team_buf.leg[config.num_legs].lec_place, false, false, ptr->flags ));
    }

    print_class_stats( -1, FALSE );             /* Print statistics */
    print_legend(-1, 1);                        /* Print the legend */
    close_printer();                            /* Close the printer */


    /*
     * Now produce a breakdown on a class by class basis 
     * Now print out the class placement information
     */

    sort_team_data( 0, S_CLASS, true );              /* Generate class placement data */
    last_class = -1;                             /* Invalid class to start with */

    for( ptr = sort_data, i = 0; i < sort_num; i++, ptr++ )
    {
        /*
        **  Detect a change in the "class"
        **  All data is within the one array of data
        **  Use the in-memory class as this MAY differ from that stored
        **  The non-equestrian class does this.
        */
        if( last_class != ptr->teamclass )
        {
            if( last_class >= 0 )
            {
                print_class_stats( last_class, TRUE );
                print_legend(last_class, 1);
                close_printer();
            }

            report_title = tprintf( "Interim Category results for : %-*s", LEN_CLASS_NAME, team_buf.teamclass == 0 ? "" : config.team_class[ptr->teamclass - 1].full_name );

            if( !open_printer( "", tprintf( "i%2s", config.team_class[ptr->teamclass - 1].abr ), 132, report_html, report_title ) )
                continue;
            print_class_header( last_class = ptr->teamclass, FALSE );
        }

        /*
        **  Now read in the team record
        */
        g_record( ptr->team, &team_buf );
        print( "%4d %4.4s %-*s",
               team_buf.numb,
               px_place(-1, team_buf.leg[config.num_legs].lec_place, false, false, ptr->flags ),
               3,
               team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].abr );

        for( j = 1; j <= config.num_legs; j++ )
        {
            print( "  %-8s", time_fa( team_buf.leg[j].elapsed, ptr->flags.disqualified ));
            if ( config.num_legs != 1 ) print( " %4.4s", px_place(j, team_buf.leg[j].lc_place,  false, true, ptr->flags ));
            if ( config.num_legs != 1 ) print( " %4.4s", px_place(j, team_buf.leg[j].lec_place, false, true, ptr->flags ));
            if ( config.num_legs == 1 ) print( " %-*s ", MAX_TM_NAME, team_buf.name );
        }


        if ( config.num_legs != 1 ) print( "  %-8s", time_fa( team_buf.leg[0].elapsed, ptr->flags.disqualified ));
        print( " %4.4s\n", px_place(0, team_buf.leg[config.num_legs].le_place, false, false, ptr->flags ) );
    }

    print_class_stats( last_class, FALSE );
    print_legend(last_class, 1);
    close_printer();

}

/*----------------------------------------------------------------------------
** FUNCTION           : pri_csv_data
**
** DESCRIPTION        : Generate a CSV file of all the report data
**                      It can then be used to play with the data externally
**
**
** INPUTS             : None
**
** RETURNS            : Yes it does
**
----------------------------------------------------------------------------*/


void pri_csv_data ( void )
{
    int i;
    int j;
    int age_sum;
    team_type   team_buf;

    /*
    **  Sort on every thing
    **  Then generate all the stats too
    */
    do_big_sort();
    gen_stats();

    /*
     * Now print the data on the printer 
     */

    if( !open_printer( "full_data", "csv", 2000, text, NULL ) )
        return;

    /*
    **  Print headings
    */
    csv_print( "%s",   "Team Number" );
    csv_print( "%s",   "Team Name" );

    csv_print( "%s",    "Class Full");
    csv_print( "%s",    "Class Abr");
    csv_print( "%s",    "Class Start Time");
    csv_print( "%s",    "Class Start Time Number");

    csv_print( "%s",    "Team Country");

    for( j = 1; j <= config.num_legs; j++ )
    {
        csv_print( "%s", "Leg Number" );
        csv_print( "%s", "Leg Name");
        csv_print( "%s", "Competitor Name");
        csv_print( "%s", "Sex" );
        csv_print( "%s", "Age");
        csv_print( "%s", "Start Time");
        csv_print( "%s", "Start Time Number");
        csv_print( "%s", "End Time" );
        csv_print( "%s", "End Time Number" );
        csv_print( "%s", "Elapsed Time");
        csv_print( "%s", "Elapsed Time Number");
        csv_print( "%s", "Leg Place");
        csv_print( "%s", "Leg End Place");
        csv_print( "%s", "Leg Class Place");
        csv_print( "%s", "Leg End Class Place");
        csv_print( "%s", "Manual");
    }

    j = 0;
    csv_print( "%s", "Team Start Time");
    csv_print( "%s", "Team Start Time Number");
    csv_print( "%s", "Team End Time");
    csv_print( "%s", "Team End Time Number");
    csv_print( "%s", "Team Elapsed Time");
    csv_print( "%s", "Team Elapsed Time Number");
//            csv_print( "%s", team_buf.leg[j].l_place );
    csv_print( "%s", "Team Leg End Place");
//            csv_print( "%s", team_buf.leg[j].lc_place );
    csv_print( "%s", "Team Leg Class Place");
//            csv_print( "%s", team_buf.leg[j].manual );
            
    csv_print( "%s", "Total Team Age");
    csv_print( "%s", "Flag:valid Team");
    csv_print( "%s", "Flag:bad_times" );
    csv_print( "%s", "Flag:disqualified" );
    csv_print( "%s", "Flag:non_equestrian" );
    csv_print( "%s", "Flag:vet_check" );

    // Not available as the data is only in-memory
    // csv_print( "%s", "Flag:notInFastest" );
    // csv_print( "%s", "Flag:notInSort" );

    csv_print("\n");


    for( i = config.min_team; i <= config.max_team; i++ )
    {
        if( valid_field( i ) && g_record( i, &team_buf ) )
        {
            /*
            **  Basic information
            **      - Team number - with Xref back to full result
            **      - Full team name
            **      - Full categoray name - with Xref to category results
            **      - Country name
            */
            csv_print( "%d",   team_buf.numb );
            csv_print( "%s",   team_buf.name );

            csv_print( "%s",    team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].full_name );
            csv_print( "%s",    team_buf.teamclass == 0 ? "" : config.team_class[team_buf.teamclass - 1].abr );
            csv_print( "%s",    time_a (team_buf.teamclass == 0 ? 0 : config.team_class[team_buf.teamclass - 1].start ));
            csv_print( "%d",    team_buf.teamclass == 0 ? 0 : config.team_class[team_buf.teamclass - 1].start );

            csv_print( "%s",    config.num_countries == 0
                                || team_buf.country ==
                                0 ? "" : config.country_name[team_buf.country - 1].full_name );

            age_sum = 0;
            for( j = 1; j <= config.num_legs; j++ )
            {
                csv_print( "%d", j );
                csv_print( "%s", config.leg_name[j - 1] );
                csv_print( "%s", team_buf.members[j-1].name );
                csv_print( "%s", ( team_buf.members[j-1].sex == male ) ? "Male" : "Female" );
                csv_print( "%d", team_buf.members[j-1].age );
                if ( age_sum >= 0 )
                {
                    ushort age = team_buf.members[j-1].age;
                    if ( age > 0 && age < 255 )
                    {
                        age_sum += age;
                    }
                    else
                    {
                        age_sum = -1;
                    }
                }


                csv_print( "%s", time_a(team_buf.leg[j].start ));
                csv_print( "%d", team_buf.leg[j].start );
                csv_print( "%s", time_a(team_buf.leg[j].end ));
                csv_print( "%d", team_buf.leg[j].end );
                csv_print( "%s", time_a(team_buf.leg[j].elapsed ));
                csv_print( "%d", team_buf.leg[j].elapsed );
                csv_print( "%d", team_buf.leg[j].l_place );
                csv_print( "%d", team_buf.leg[j].le_place );
                csv_print( "%d", team_buf.leg[j].lc_place );
                csv_print( "%d", team_buf.leg[j].lec_place );
                csv_print( "%d", team_buf.leg[j].manual );
            }

            j = 0;
            csv_print( "%s", time_a(team_buf.leg[j].start ));
            csv_print( "%d", team_buf.leg[j].start );
            csv_print( "%s", time_a(team_buf.leg[j].end ));
            csv_print( "%d", team_buf.leg[j].end );
            csv_print( "%s", time_a(team_buf.leg[j].elapsed ));
            csv_print( "%d", team_buf.leg[j].elapsed );
//            csv_print( "%d", team_buf.leg[j].l_place );
            csv_print( "%d", team_buf.leg[j].le_place );
//            csv_print( "%d", team_buf.leg[j].lc_place );
            csv_print( "%d", team_buf.leg[j].lec_place );
//            csv_print( "%d", team_buf.leg[j].manual );
            
            csv_print( "%d", age_sum );
            csv_print( "%d", team_buf.flags.valid );
            csv_print( "%d", team_buf.flags.bad_times );
            csv_print( "%d", team_buf.flags.disqualified );
            csv_print( "%d", team_buf.flags.non_equestrian );
            csv_print( "%d", team_buf.flags.vet_check );
            // Not available as the data is only in-memory
            // csv_print( "%d", team_buf.flags.notInFastest );
            // csv_print( "%d", team_buf.flags.notInSort );

//How about class placings


            csv_print( "\n" );
        }
    }

    close_printer();
}


/*========================================================================
 *
 *  Print all reports at once
 *  Its all so fast, these days ...
 *
 *  Purpose:
 *      This function is called to print all reports at once
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_all_reports ( void )
{
    int leg;
    report_all = TRUE;
    
    pri_team();
    
    for ( leg = 1; leg <= config.num_legs; leg ++ )
    {
        pri_leg_body ( leg );
        pri_eleg_body ( leg );
        
        report_html = html;
        
        pri_leg_body ( leg );
        pri_eleg_body ( leg );

        //report_html = printed;
        //
        //pri_leg_body ( leg );
        //pri_eleg_body ( leg );        
        
        report_html = text;
    }

    pri_final();
    pri_final_teamOrder();
    pri_final_html();
    pri_csv_data();
    pri_summary_html();
    pri_summary();
    pri_awards_html();
    pri_awards();
    pri_master_index();

    report_html = html;
    pri_interim();
    report_html = text;
    pri_interim();

    report_all = FALSE;
}


/*========================================================================
 *
 *  Print a class header
 *
 *  Purpose:
 *      This function is called to print a class header
 *
 *  Parameters:
 *      class           Name of this class
 *      final           False - prelim results
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void print_class_header( int teamclass, int final )
{
    int         j;

    /*
    **  Give a clear indication that the report is preliminary
    */

    if( !final )
        print( "PRELIMINARY RESULTS ONLY\n\n" );

    /*
    **  Now printout the column headings
    **  This is a two line display
    **
    **  Line-1  Leg names
    */

    if (config.num_legs == 1 ) {
        print( "%-*s", 4, "" );
        print( "  %-*s", 18, config.leg_name[0] );
        print( " %-*s", MAX_TM_NAME + 1, "" );
        print( "%-4s\n", ( teamclass < 0 ) ? "Cat" : "Fin" );

    } else {
        print( "%-*s %-*s %-*s", 4, "", 4, "", 3, "" );
        for( j = 1; j <= config.num_legs; j++ )
        {
            print_bold( TRUE );
            print( "  %-*s", 18, config.leg_name[j - 1] );
            print_bold( FALSE );
        }
        print( "  %-8s %-4s\n", "Total", ( teamclass < 0 ) ? "Cat" : "Fin" );
    }




    /*
    **  Line-2  Details
    */
    print_underline( TRUE );
    print( "%-*s %*s %-*s", 4, final ? "Plce" : "Team",
                            4, final ? "Team" : "Plce",
                            3, "Cat" );


    if (config.num_legs == 1 ) {
        print( "  %-8s", "Time" );
        print( " %-*s", MAX_TM_NAME + 1, "Team Name" );
        print( "%-4s\n", "Plce" );

    } else {
        for( j = 1; j <= config.num_legs; j++ )
            print( "  %-8s %-4s %-4s", "Time", " LP", " EP" );
        print( "  %-8s %-4s\n", "Time", "Plce" );
    }


    print_underline( FALSE );
}

/*========================================================================
 *
 *  Generate the class stats
 *
 *  Purpose:
 *      This function is called to Generate the class stats
 *
 *  Parameters:
 *      c           Class to print
 *      final       TRUE: Final data
 *                  FALSE: Interim data
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void print_class_stats( int c, int final )
{
    int         i, j;
    const char        *title;

    if( c < 0 )
    {
        title = "Event";
        c = 0;
    }
    else
    {
        title = "Category";
    }

    print( "\n" );
    if ( report_html ) print_underline(TRUE);
    print( "%s statistics", title );
    if ( report_html ) print_underline(FALSE);
    print( "\n" );


    /*
    **  Print the names of the different legs
    */
    print( "%-*s       ", LEN_CLASS_NAME, "" );
    for( i = 1; i <= config.num_legs; i++ )
    {
        print_bold( TRUE );
        print( "%-13s  ", config.leg_name[i - 1] );
        print_bold( FALSE );
    }
    if ( config.num_legs != 1 ) print( "%-13s  ", final ? "Total" : "" );
    print( "\n" );

    /*
    **  Print the fastest teams for each leg and overall
    **  Add cross references to the team names for the fastest teams
    */
    print( "%*s : ", LEN_CLASS_NAME, "Fastest" );
    for( i = 0; i <= config.num_legs; i++ )
    {
        j = i + 1;
        if( i >= config.num_legs )
        {
            if( final )
                j = 0;                           /* Leg-0 last */
            else
                break;
        }

        if ( report_html == html ) print( "<A HREF=\"%s#Team_%04d\">", url_encode(p_filename(filebase, "name" ,"html")), stats.fast.team[j][c] );
        print( "%4d",  stats.fast.team[j][c] );
        if ( report_html == html ) print( "</A>" );
        print( " %s  ", time_a( stats.fast.time[j][c] ) );
        if ( config.num_legs == 1 ) break;
    }
    print( "\n" );

    /*
    **  Print the average time for each leg
    */
    print( "%*s : ", LEN_CLASS_NAME, "Average" );
    for( i = 0; i <= config.num_legs; i++ )
    {
        j = i + 1;
        if( i >= config.num_legs )
        {
            if( final )
                j = 0;                           /* Leg-0 last */
            else
                break;
        }
        print( "     %s  ", time_a( stats.average[j][c] ) );
        if ( config.num_legs == 1 ) break;
    }
}

/*========================================================================
 *
 *  Print the legend
 *
 *  Purpose:
 *      This function is called to Print the legend
 *
 *  Parameters:
 *      class       - Class currently being printed
 *      full        - Display full legend
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void print_legend ( int teamclass, int full )
{

    int         i;
    char        line[201];
    FILE       *adfile = NULL;
    int         count;

    /*
     * First the categories 
     */
    print( "\n\n" );
    if ( report_html ) print_underline(TRUE);
    print( "Category abbreviations" );
    if ( report_html ) print_underline(FALSE);
    print( "\n" );


    for( i = 1, count = 0; i <= config.num_class; i++ )
    {
        if (!config.team_class[i - 1].abr[0])
            continue;

        if ( report_html == html ) print( "<A HREF=\"%s\">",url_encode(p_filename(filebase, config.team_class[i - 1].abr ,"html")) );
        print( "%-*s", 3, config.team_class[i - 1].abr );
        if ( report_html == html ) print( "</A>" );
        print( " : %-*s  ", LEN_CLASS_NAME, config.team_class[i - 1].full_name );

        if( !( ++count % 5 ) )
            print( "\n" );
    }

    /*
    **  Add link to the finish order report
    */
    if ( report_html == html )
    {
        print( "<A HREF=\"%s\">", url_encode(p_filename(filebase, "finish" ,"html")) );
        print( "%-*s", 3, "All" );
        print( "</A>" );
        print( " : %-*s  ", LEN_CLASS_NAME, "Finishing Order" );
    }

    /*
    **  Country data - if countries have been defined
    */
    if( config.num_countries )
    {
        print( "\n\n" );
        if ( report_html ) print_underline(TRUE);
        print( "Country abbreviations" );
        if ( report_html ) print_underline(FALSE);
        print( "\n" );

        for( i = 0, count = 0; i < MAX_COUNTRY; i++ )
        {
            if( config.country_name[i].abr[0] )
            {
                print( "%-*s : %-*s  ", 4, config.country_name[i].abr,
                       LEN_CNTRY_NAME, config.country_name[i].full_name );
                if( !( ++count % 5 ) )
                    print( "\n" );
            }
        }
    }
    print( "\n" );

    /*
     * Other comments 
     */
    if ( full )
    {
        if ( config.num_legs != 1 )  {
            print( "\n" );
            if ( report_html ) print_underline(TRUE);
            print( "Place numbers (LP and EP)" );
            if ( report_html ) print_underline(FALSE);
            print( "\n" );
            print( "LP - Placing based on elapsed time within the leg.                Cat Plce - Placing within the category.\n" );
            print( "EP - Placing based on accumulated times to the end of that leg.   Fin Plce - Overall placing within the event.\n" );
            print( "U  - Placing not available.\n" );
        } else {
            print( "\n" );
            if ( report_html ) print_underline(TRUE);
            print( "Table Legend" );
            if ( report_html ) print_underline(FALSE);
            print( "\n" );

            print( "Cat Plce - Placing within the category.\n");
            print ("Fin Plce - Overall placing within the event.\n" );
            print( "U        - Placing not available.\n" );
        }
    }

    /*
     *  Insert the contents of the config.addendum file
     *  or a defualt message
     */
    if( config.addendum[0] )
    {
        QString name = QmConfig::getAddendemFile(config.addendum);
        if (! name.isEmpty())
        {
            adfile = fopen( qPrintable(name), "rt" );  /* Open the file for reading */
        }
    }

    if( adfile )
    {
        while( fgets( line, sizeof(line)-1, adfile ) )
            print( "%s", line );
        fclose (adfile);
    }
    else
    {
        print( "\nTiming and Results by\n" );
        print( "Embedded Solutions\n" );
    }
}


/*========================================================================
 *
 *  Return place data or NE flag
 *
 *  Purpose:
 *      This function is called to Return place data
 *
 *      This routine is called to fill a print team_buffer - to allow for
 *      multiple calls to this function ( before the data is used ) a number
 *      of static team_buffers are maintained
 *
 *  Parameters:
 *      leg         Leg number being processed or
 *                  -1: Unplaced on any bad flag
 *      num         place - if valid time
 *      epMode      true:  Event Time Mode - Show V and NE flags
 *                  false: Leg Time Mode -
 *      neMode      true: Display NE for NE Teams
 *                  false: Display data for NE teams
 *      flags       Team flags
 *
 *  Returns:
 *      This function returns a pointer to the character string for the
 *      number or a pointer to a status string.
 *
 *========================================================================*/
const char *px_place( int leg, int num, bool epMode, bool neMode, team_flags flags )
{
    static char store[2][5];                     /* 2 stores for 4 digit numbers */
    static int  i = 0;                           /* Current index into store */
    static const char *dis = "U";
    static const char *alt = "NE";
    static const char *vet = "U";

    if (flags.bad_times || flags.disqualified || !flags.valid)
        return dis;

    if (leg == -1 && flags.vet_check )
        return vet;

    if (neMode && flags.non_equestrian && (leg >= config.equestrian_leg || leg == 0))
        return alt;

    if (epMode && flags.vet_check && leg >= config.equestrian_leg)
        return vet;

    if ( flags.vet_check && (leg == config.equestrian_leg || leg == 0))
        return vet;

    if (num <= 0)
        return dis;

    i++;
    if( i >= 2 )
        i = 0;                                   /* Select next entry */
    sprintf( store[i], "%4d", num );
    return ( store[i] );

}

/*========================================================================
 *
 *  Check data for bad times
 *
 *  Purpose:
 *      This function is called to Check data for bad times
 *      Scan the sort data structure and locate entries that have incorrect
 *      times.
 *      
 *      Entries that have invalid leg times are displayed to the operator
 *      and the report process can be aborted
 *
 *  Parameters:
 *      leg             Leg to test
 *      mode            Either end or elapsed times to be tested
 *                          C_END     - Printing in Leg Finishing Time
 *                          C_DISQUAL - Interim results
 *                          C_ELAPSED - Elapsed times
 *
 *
 *========================================================================*/

void ck_data( int leg, int mode )
{
    ty_s_data  *ptr;
    unsigned    i;
    int         bad = 0;
    int         k;
    int         bad_leg;
    time_t     *t;                               /* An array of times */

    ptr = sort_data;
    for( i = 1; i <= sort_num; i++, ptr++ )
    {
        bad_leg = 0;
        if( mode == C_DISQUAL )
        {
            ptr->flags.bad_times = ptr->flags.disqualified;
            continue;
        }

        if( mode == C_ELAPSED )
            t = ptr->lege;
        else
            t = ptr->leg;


        // If the team is duff, then don;t include in Event or Leg Calcs
        if( !ptr->flags.valid || ptr->flags.bad_times || ptr->flags.disqualified)
        {
            ptr->flags.notInLP = TRUE;
        }
        else
        {
            // If any of the leg times are duff, then don't include the leg Event Calcs

            if( leg <= 0 )
            {
                for( k = 0; k <= config.num_legs; k++ )
                {
                    if ( !(config.equestrian_leg && ptr->flags.non_equestrian && config.equestrian_leg == k  ))
                        bad_leg |= ( t[k] <= 0 );
                }
            }
            else
            {
                bad_leg = t[leg] <= 0;
            }

            if( bad_leg )
            {
                if ( ! report_all )
                {
                    qDebug( "Team with incorrect time information: %d", ptr->team  );
                    bad++;
                }
            }
        }
    }

    if( bad )
    {
        qDebug( "%d teams with incorrect times. These have been flagged as unplaced", bad );
    }
}

/*========================================================================
 *
 *  Update placing information
 *
 *  Purpose:
 *      This function is called to Update placing information
 *
 *      This routine will rip through the data generating the team placing in
 *      a) Within a leg
 *      b) At the end of a leg
 *      c) Within a leg by  class
 *      d) At the end of a leg by class
 *
 *      This function is provided to allow the display routines to
 *      be accessed and updated without the need to run a report
 *
 *  Parameters:
 *      xxxx        a ptr to the xxxx stuff
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void srt_place(void)
{
    ck_data( -1, C_ELAPSED );
    do_big_sort();

    /*
     * Generate the stats
     */
    gen_stats();
}

/*========================================================================
 *
 *  Calculate summary information
 *
 *  Purpose:
 *      This function is called to calculate summary information
 *
 *  Parameters:
 *      ptr         - Address of a summary structure to fill in
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void calc_class_summary( t_class_summary * ptr )
{
    int i;
    team_type   team_buf;

    /*
    **  Reset the data
    */
    memset ( ptr, 0, sizeof (*ptr ));

    /*
     * Extract the required data from the data base
     * Only save that information required for the operation
     */

    for( i = config.min_team; i <= config.max_team; i++ )
    {
        if( valid_field( i ) && g_record( i, &team_buf ) )
        {
            bool valid = true;
            bool valid_ne = true;

            if ( team_buf.flags.disqualified )
            {
                ptr->teamclass[team_buf.teamclass].disqualified++;
                ptr->total.disqualified++;
                valid = false;
                valid_ne = false;
            }

            if ( team_buf.flags.vet_check )
            {
                ptr->teamclass[team_buf.teamclass].vet_check++;
                ptr->total.vet_check++;
                valid = false;
                valid_ne = false;
            }

            if ( config.nonequestrian_class && team_buf.flags.non_equestrian )
            {
                ptr->teamclass[team_buf.teamclass].non_equestrian++;
                ptr->total.non_equestrian++;
                valid = false;
            }
            else
            {
                valid_ne = false;
            }

            ptr->total.total++;
            ptr->teamclass[team_buf.teamclass].total++;
            if ( valid )
            {
                ptr->total.valid++;
                ptr->teamclass[team_buf.teamclass].valid++;

                if ( ! team_buf.flags.bad_times )
                {
                    ptr->total.valid_ev++;
                    ptr->teamclass[team_buf.teamclass].valid_ev++;
                }
            }

            if ( valid_ne )
            {
                if ( ! team_buf.flags.bad_times )
                {
                    ptr->total.valid_ne++;
                    ptr->teamclass[team_buf.teamclass].valid_ne++;
                }
            }
        }
    }

    /*
    **  Fix up the totals for the non equestrians
    **  This is not a real category but a summary of the others.
    */
    if ( config.nonequestrian_class  )
    {
        ptr->teamclass[config.nonequestrian_class].total += ptr->total.non_equestrian;
        ptr->teamclass[config.nonequestrian_class].valid += ptr->teamclass[config.nonequestrian_class].total;
    }
}

/*========================================================================
 *
 *  Print summary results
 *
 *  Purpose:
 *      This function is called to Print summary results
 *      Keep the page to 80 cols, so that it can be pronted on A4
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/
void pri_summary_html(void)
{
    report_type saved = report_html;
    /*
    **  Generate ALL results with HTML tags
    */
    report_html = html;
    pri_summary();
    report_html = printed;
    pri_summary();
    report_html = saved;
}

/*========================================================================
 *
 *  Print summary information
 *
 *  Purpose:
 *      This function is called to print summary information
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void pri_summary (void)
{

    t_class_summary data;
    int         i;

    if( !open_printer( "", "summary", 80, report_html, "Summary Information" ) )
        return;
    
    calc_class_summary( & data );


    /*
    **  Display summary stats
    */

    print( "%*s : %-7s %-7s %-7s %-7s %-7s %-7s %-7s\n",
                                      LEN_CLASS_NAME, "Category",
                                      "Total",
                                      "Valid",
                                      "Disq",
                                      "NonEq",
                                      "VetChk",
                                      "CompEv",
                                      "CompNe"
                                       );

    for( i = 0; i < config.num_class; i++ )
    {
        if (!config.team_class[i].abr[0])
            continue;
        /*
        **  The non-equestrian leg does not have any data
        **  Suppress the display
        */
        if ( config.nonequestrian_class == i+1  )
            continue;

        if ( report_html == html ) print( "<A HREF=\"%s\">",url_encode(p_filename(filebase, config.team_class[i].abr ,"html")) );
        print( "%*s ", LEN_CLASS_NAME, config.team_class[i].full_name );
        if ( report_html == html ) print( "</A>" );

        print( ": %-7d %-7d %-7d %-7d %-7d %-7d %-7d\n",
                                          data.teamclass[i+1].total,
                                          data.teamclass[i+1].valid,
                                          data.teamclass[i+1].disqualified,
                                          data.teamclass[i+1].non_equestrian,
                                          data.teamclass[i+1].vet_check,
                                          data.teamclass[i+1].valid_ev,
                                          data.teamclass[i+1].valid_ne
                                          );
    }

    print( "\n" );
    print( "%*s : %-7d %-7d %-7d%-7d %-7d %-7d %-7d\n",
                                      LEN_CLASS_NAME, "Totals",
                                      data.total.total,
                                      data.total.valid,
                                      data.total.disqualified,
                                      data.total.non_equestrian,
                                      data.total.vet_check,
                                      data.total.valid_ev,
                                      data.total.valid_ne
                                      );

    close_printer();
}



/*========================================================================
 *
 *  Main sort routine for final data
 *
 *  Purpose:
 *      This function is called to do the report sort routine for final data
 *      This routine will fill all the gaps in the sort_aux structure
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void do_big_sort(void)
{
    int         i, k, q;                             /* Looooopy things */
    unsigned    j;
    ty_s_data  *ptr;                                 /* Pointer to sort data */
    int         teamclass;                           /* Current class */
    int         teamclassq;                          /* Current class */
    team_type   team_buf;

    for( i = 0; i <= config.num_legs; i++ )
    {
        bool sortWithEq =  (i == 0);

        /*
        **  Sort on leg elapsed time
        **  Then save the teams elapsed place in each leg
        */
        sort_team_data( i, S_L, sortWithEq );
        for( j = 1, k = 1, q = 1, ptr = sort_data; j <= sort_num; ptr++, j++ )
        {
            sort_aux[ptr->team].team = ptr->team;

            if (i == config.equestrian_leg || i == 0) {
                if (ptr->flags.bad_times || ptr->flags.disqualified || ptr->flags.vet_check) {
                    if (ptr->isNeData) {
                        sort_aux[ptr->team].lq_place[i] = -1;
                    } else {
                        sort_aux[ptr->team].l_place[i] = -1;
                    }
                    continue;
                }
            }

            // Flag as unplaced NE teams on the EQ leg
            // DO NOT Flag as unplaced in overall (leg:0) NE teams as this is used in the full report
            if ( (i == config.equestrian_leg /*|| i == 0*/ ) && ptr->flags.non_equestrian ) {
                if (ptr->isNeData) {
                    sort_aux[ptr->team].lq_place[i] = -1;
                } else {
                    sort_aux[ptr->team].l_place[i] = -1;
                }
                continue;
            }

            if (ptr->isNeData) {
                sort_aux[ptr->team].lq_place[i] = q++;
            } else {
                sort_aux[ptr->team].l_place[i] = k++;
            }
        }

        /*
        **  Sort on leg end time
        **  Then save the teams place at the end of each leg
        */
        sort_team_data( i, S_LE, sortWithEq);
        for( j = 1, k = 1, q = 1, ptr = sort_data; j <= sort_num; ptr++, j++ )
        {
            if (i == 0 || i == config.equestrian_leg || sort_afterEquestrianLeg ) {
                if (ptr->flags.bad_times || ptr->flags.disqualified || ptr->flags.vet_check) {
                    if (ptr->isNeData) {
                        sort_aux[ptr->team].leq_place[i] = -1;
                    } else {
                        sort_aux[ptr->team].le_place[i] = -1;
                    }
                    continue;
                }
            }

            // Flag as unplaced NE teams on the EQ leg
            // Flag as unplaced NE teams after the EQ leg
            // Flag as unplaced in overall (leg:0) NE teams
            if ( (i == config.equestrian_leg || sort_afterEquestrianLeg || i == 0 ) && ptr->flags.non_equestrian ) {
                if (ptr->isNeData) {
                    sort_aux[ptr->team].leq_place[i] = -1;
                } else {
                    sort_aux[ptr->team].le_place[i] = -1;
                }
                continue;
            }

            if (ptr->isNeData) {
                sort_aux[ptr->team].leq_place[i] = q++;
            } else {
                sort_aux[ptr->team].le_place[i] = k++;
            }
        }

        /*
        **  Sort on elapsed time per class
        **  Then save the teams elapsed place in each leg per class
        */
        sort_team_data( i, S_LC, sortWithEq );
        teamclass = -1;
        teamclassq = -1;
        for( k = 1, j = 1, q = 1, ptr = sort_data; j <= sort_num; j++, ptr++ )
        {
            bool isNe = false;
            if ( sortWithEq && ptr->teamclass == config.nonequestrian_class  ) {
                if ( !ptr->isNeData)
                    isNe = true;
            }
            else {
                if (ptr->isNeData)
                    isNe = true;
            }

            // Flag as unplaced NE teams on the EQ leg
            if (i == 0 || i == config.equestrian_leg) {
                if (ptr->flags.bad_times || ptr->flags.disqualified || ptr->flags.vet_check) {
                    if (isNe) {
                        sort_aux[ptr->team].lq_place[i] = -1;
                    } else {
                        sort_aux[ptr->team].lc_place[i] = -1;
                    }
                    continue;
                }
            }

            if ( i == config.equestrian_leg && ptr->flags.non_equestrian ) {
                if (ptr->isNeData) {
                    sort_aux[ptr->team].lq_place[i] = -1;
                } else {
                    sort_aux[ptr->team].lc_place[i] = -1;
                }
                continue;
            }

            if (isNe) {
                if( teamclassq != ptr->teamclass )
                {
                    q = 1;
                    teamclassq = ptr->teamclass;
                }
                sort_aux[ptr->team].lq_place[i] = q++;

            }
            else {

                if( teamclass != ptr->teamclass )
                {
                    k = 1;
                    teamclass = ptr->teamclass;
                }
                sort_aux[ptr->team].lc_place[i] = k++;
            }

        }

        /*
        **  Sort on end time per class
        **  Then save the teams place at the end of each leg per class
        */
        sort_team_data( i, S_LEC, sortWithEq );
        teamclass = -1;
        teamclassq = -1;
        for( k = 1, j = 1, q = 1, ptr = sort_data; j <= sort_num; j++, ptr++ )
        {
            bool isNe = false;
            if ( sortWithEq &&  ptr->teamclass == config.nonequestrian_class  ) {
                if ( !ptr->isNeData)
                    isNe = true;
            }
            else {
                if (ptr->isNeData)
                    isNe = true;
            }

            if (i == 0 || i == config.equestrian_leg || sort_afterEquestrianLeg) {
                if (ptr->flags.bad_times || ptr->flags.disqualified || ptr->flags.vet_check) {
                    if (isNe) {
                        sort_aux[ptr->team].leq_place[i] = -1;
                    } else {
                        sort_aux[ptr->team].lec_place[i] = -1;
                    }
                    continue;
                }
            }

            // Flag as unplaced NE teams on the EQ leg
            // Flag as unplaced NE teams after the EQ leg
            // Flag as unplaced in overall (leg:0) NE teams
            if ( (i == config.equestrian_leg || sort_afterEquestrianLeg || i == 0 ) && ptr->flags.non_equestrian ) {
                if (ptr->isNeData) {
                    sort_aux[ptr->team].leq_place[i] = -1;
                } else {
                    sort_aux[ptr->team].lec_place[i] = -1;
                }
                continue;
            }

            if (isNe) {
                if( teamclassq != ptr->teamclass )
                {
                    q = 1;
                    teamclassq = ptr->teamclass;
                }
                sort_aux[ptr->team].leq_place[i] = q++;
            }
            else {
                if( teamclass != ptr->teamclass )
                {
                    k = 1;
                    teamclass = ptr->teamclass;
                }
                sort_aux[ptr->team].lec_place[i] = k++;
            }

        }
    }

    /*
    **  Write the place information back to disk for use in the displays
    */
    for( i = config.min_team; i <= config.max_team; i++ )
    {
        if( sort_aux[i].team && valid_field( i ) && g_record( i, &team_buf ) )
        {
            for( k = 0; k <= config.num_legs; k++ )
            {
                team_buf.leg[k].l_place = sort_aux[i].l_place[k];
                team_buf.leg[k].le_place = sort_aux[i].le_place[k];
                team_buf.leg[k].lc_place = sort_aux[i].lc_place[k];
                team_buf.leg[k].lec_place = sort_aux[i].lec_place[k];
            }
            put_team_record( i, &team_buf );
        }
    }
}

/*========================================================================
 *
 *  Sort in memory buffer
 *
 *  Purpose:
 *      This function is called to Sort in memory buffer
 *
 *  Parameters:
 *      leg         Requested leg
 *      mode        Defines the sort mode
 *      withEq      Sort with equestrian data (True=Normal)
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void sort_team_data( int leg, int mode, bool withEq )
{

    unsigned    j;
    ty_s_data  *ptr;

    sort_leg = leg;                              /* Leg is global for the comparison function */
    sort_mode = mode;                            /* Mode is global for compare function */
    sort_withEquestrian = withEq;                /* Mode is global for compare function */
    sort_afterEquestrianLeg = ( config.equestrian_leg && leg > config.equestrian_leg);

    qsort( ( char * ) sort_data, sort_num, sizeof( ty_s_data ), sort_comp );

    /*
     * Insert "place data" into the sorted data
     * This simply the index into the array of data - after its been
     * sorted.
     */
    ptr = sort_data;
    for( j = 1; j <= sort_num; j++, ptr++ )
    {
        ptr->place = j;
    }

    //qDebug() << "sort_team_data: Leg:" << leg << ",M:" << mode << ",E:" << withEq;


}

/*========================================================================
 *
 *  qsort comparison function
 *
 *  Purpose:
 *      This function is called by qsort as a Sort comparison function
 *
 *  Parameters:
 *      a       - 1st record
 *      b       - 2nd record
 *
 *  Returns:
 *      value to qsort
 *      Return negative if 'a' is less than 'b',
 *             zero if 'a' == 'b'
 *             positive if 'a' > 'b'
 *
 *========================================================================*/

int sort_comp( const void * aa, const void * bb )
{
    const ty_s_data * a = (ty_s_data *)aa;
    const ty_s_data * b = (ty_s_data *)bb;


    int         i;                               /* One of those */
    time_t      ta, tb;                          /* Leg times */
    int         na, nb;                          /* Number of valid legs */
    time_t      tta, ttb;                        /* Temp times */

    /*
    **  Sorting on Team Number
    */
    if( sort_mode == S_TEAM )
        return ( a->team - b->team );

    /*
    **  Sorting on Class and Team Number
    */
    if( sort_mode == S_CLASS )
    {
        if( a->teamclass != b->teamclass )
            return ( a->teamclass - b->teamclass );
        else
            return ( a->team - b->team );
    }

    /*
    **  Sorting within a class
    **  First sort on the class
    */
    if( sort_mode == S_LEC || sort_mode == S_LC || sort_mode == S_LC_NE ) {  /* Sort within a class */
        if (a->teamclass != b->teamclass)
            return ( a->teamclass - b->teamclass );
    }

    /*
    **  Always put the nonequestrian_class at the end
    **  Simplifies the creation of ordered lists as these
    **  Entries are not present in the main body
    */
    if ( sort_withEquestrian && ( a->teamclass == config.nonequestrian_class ||  b->teamclass == config.nonequestrian_class ))
    {
        if ( a->teamclass == config.nonequestrian_class &&  b->teamclass == config.nonequestrian_class )
        {
            /*
            **  Both are non equestrian
            **      S_LC_NE - Elapsed times per class, with NE sorted by real class
            **                Only used in awards
            */
            if (sort_mode == S_LC_NE )
            {
                if ( a->real_teamclass != b->real_teamclass )
                {
                    return ( a->real_teamclass - b->real_teamclass );
                }
            }
        }
        else
        {
            /*
            **  Only one in non-equestrian
            **  Place Non-equestrian AFTER equestrian
            */
            return ( a->teamclass == config.nonequestrian_class ? 1 : -1 );
        }

    }


    /*
    **  Now we need to examine the times as we have sorted
    **  on every thing else.
    **
    **  If one of the teams has bad_times, then that team is placed
    **  lower in the sorting order. If both teams have bad times
    **  then sort on team number. ie: Unplaced teams are sorted on
    **  team number
    **
    **  If not sorting within a class (ie Overall), then Non_Equestrian
    **  is better than a bad time. Places NE before disqualified
    **
    */
    bool aNoSort = false;
    bool bNoSort = false;

    if ( sort_leg != 0 && (sort_mode == S_LC || sort_mode == S_L ) ) {
        aNoSort = a->flags.notInLP;
        bNoSort = b->flags.notInLP;
    }

    if ( sort_withEquestrian && (sort_mode == S_LC || sort_mode == S_L ) ) {
        if (a->flags.vet_check)
            aNoSort = true;
        if (b->flags.vet_check)
            bNoSort = true;
    }

    if (a->flags.bad_times || a->flags.disqualified || !a->flags.valid)
        aNoSort = true;

    if (b->flags.bad_times || b->flags.disqualified || !b->flags.valid)
        bNoSort = true;

    if ( sort_mode == S_FIN || sort_mode == S_IFIN ) {
        if (a->flags.vet_check)
            aNoSort = true;
        if (b->flags.vet_check)
            bNoSort = true;
    }

    // On the Equestrian Leg, vetted out teams are treated as disqualified
    if (sort_leg == config.equestrian_leg || (sort_afterEquestrianLeg && ( sort_mode == S_LE || sort_mode == S_LEC))) {
        if (a->flags.vet_check)
            aNoSort = true;
        if (b->flags.vet_check)
            bNoSort = true;
    }

    if (aNoSort && bNoSort)
        return ( a->team - b->team );

    if (aNoSort || bNoSort)
        return ( aNoSort ? 1 : -1 );

    if( sort_withEquestrian && sort_mode != S_FIN && sort_mode != S_IFIN )
    {
        if( a->flags.non_equestrian && b->flags.non_equestrian )
        {
            /*
            **  Both are non equestrian
            **  Let the time sort operate ...
            */
            //return ( a->team - b->team );
        }
        else if( a->flags.non_equestrian || b->flags.non_equestrian )
        {
            /* One is equestrian */
            /* Good times better than NE */
            return ( a->flags.non_equestrian ? 1 : -1 );
        }
    }

    /*
    **  Before we sort on times we must determine which time to
    **  use. Finish time, Leg end times, Leg Elapsed times.
    */

    switch ( sort_mode )
    {
      /*
      **    Sort on finish times
      */
      case S_FIN:
        ta = a->leg[sort_leg];
        tb = b->leg[sort_leg];
        break;


      /*
      **    Sort on accumulated leg times
      */
      case S_LE:
      case S_LEC:
        if( sort_leg )
        {
            /*
            **  Calculate accumulated time up to the desired leg
            **  If the two teams have a different number of valid
            **  leg times then order by the team that has completed
            **  more legs.
            */
            ta = tb = ( time_t ) 0;
            na = nb = 0;
            for( i = 1; i <= sort_leg; i++ )
            {
                tta = a->lege[i];
                ttb = b->lege[i];
                if( tta > 0 )
                {
                    na++;
                    ta += tta;
                }
                if( ttb > 0 )
                {
                    nb++;
                    tb += ttb;
                }
            }
            if( na != nb )
                return ( nb - na );
        }
        else
        {
            ta = a->leg[sort_leg] - a->start;
            tb = b->leg[sort_leg] - b->start;
        }
        break;

      /*
      **    Sort on Elapsed times
      */
      case S_LC_NE:
      case S_LC:
      case S_L:
      case S_IFIN:
        ta = a->lege[sort_leg];
        tb = b->lege[sort_leg];
        break;

      /*
      **    Just to be sure ...
      */
      default:
        return ( 0 );
    }

    /*
     ** If we are ignore Equestrian config then place the Equestrian data last
    */

#if 1
    if (sort_withEquestrian) {
        if (a->isNeData != b->isNeData)
            return ( a->isNeData ? 1 : -1 );
    }
#endif

    if ( a->team == b->team) {
        return ( a->isNeData ? 1 : -1 );
    }

    /*
    **  Finally. Compare the required team times
    */
    if( ta == tb )
        return ( a->team - b->team );
    if( ( ta > 0 ) && ( tb > 0 ) )
        return ( ( int ) ( ta - tb ) );
    return ( ( ta > 0 ) ? -1 : 1 );
}

/*========================================================================
 *
 *  qsort comparison function - competitor names
 *
 *  Purpose:
 *      This function is called by qsort as a Sort comparison function
 *
 *  Parameters:
 *      a       - 1st record
 *      b       - 2nd record
 *
 *  Returns:
 *      value to qsort
 *
 *========================================================================*/

int sort_comp_cname( const void * aa, const void * bb )
{
    ty_s_namedata * a = (ty_s_namedata *)aa;
    ty_s_namedata * b = (ty_s_namedata *)bb;


    int         i;                               /* One of those */

    /*
    **  Sort by name
    */
    i = strcmp ( a->name, b->name );
    if ( i )
        return ( i );
    a->multi=1;
    b->multi=1;

    /*
    **  Sort by Leg
    */
    i = a->leg - b->leg;
    if ( i )
        return ( i );

    /*
    **  Sorting on Team Number
    */
    return ( a->team - b->team );

}

/*========================================================================
 *
 *  load report data into memory
 *
 *  Purpose:
 *      This routine will pull all the data into memory 
 *      Not all the team data is loaded. Only that essential for the
 *      operation of the sort routine is loaded
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      TRUE - All is well
 *
 *========================================================================*/

bool load_report_data(void)
{

    ty_s_data  *ptr;                             /* pointer to sort data type */
    ty_s_data  *last;
    unsigned    j;
    unsigned    num;
    team_type   team_buf;

    /*
     * Fetch memory for the data store
     */

    if( sort_data )
        free( ( char * ) sort_data );
    sort_data = 0;

    if( sort_aux )
        free( ( char * ) sort_aux );
    sort_aux = 0;
    sort_num = 0;                                /* Counter of records in the array */

    /*
    **  Allocate memory for the data structures
    **  This will be free'd
    */
    num = config.max_team - config.min_team + 1 ;

    /*
    **  Allow for non-equestrian teams - since some of the data is loaded twice
    **  OLD: Take a guess that at most 1/2 the teams will be non-equestrian
    **  NEW: Memory is cheap. Assume all teams could be non-equestrian
    */
    // OLD: num = num * 3 / 2;
    num = num * 2;

    sort_data = ( ty_s_data * ) calloc ( num , sizeof( ty_s_data ) );
    sort_aux = ( ty_s_aux * ) calloc( num + 1, sizeof( ty_s_aux ) );

    if( sort_data == 0 || sort_aux == 0 )
    {
        MainWindow::showMessage("Error in allocating memory");
        return ( FALSE );
    }

    /*
    **  Load data into the memory based data structure
    **  Only load data for valid-teams
    **  Load essential data
    **      Team Number, class and flags
    **      Leg end and elapsed times
    */
    ptr = sort_data;
    for( int team = config.min_team; team <= config.max_team; team++ )
    {
        if( valid_field( team ) )
        {
            g_record( team, &team_buf );
            if( team_buf.flags.valid )
            {
                last = ptr;
                ptr->team = team;
                ptr->isNeData = false;
                for( j = 0; j < MAX_LEGS + 1; j++ )
                {
                    ptr->lege[j] = team_buf.leg[j].elapsed;
                    ptr->leg[j] = team_buf.leg[j].end;
                }
                ptr->start = team_buf.leg[0].start;
                ptr->teamclass = team_buf.teamclass;
                ptr->real_teamclass = team_buf.teamclass;

                ptr->flags = team_buf.flags;

                // Add a few flags to simplify processing
                //  Vetted out teams to be included inthe fastest calcs - except for Equestian Leg
                //  Bodgey teams are not sorted by place - only after all places and then by team number
                ptr->flags.notInLP = 0;
                ptr->flags.notInFastest = 0;

                if ( ptr->flags.bad_times || ptr->flags.disqualified || !ptr->flags.valid) {
                    ptr->flags.notInLP = 1;
                    ptr->flags.notInFastest = 1;
                }

                ptr++;
                sort_num++;


                /*
                **  If non-equestrian support is enabled then
                **  duplicate and modify data for the non-equestrian teams
                **      - Change the class
                **      - Modify the leg time
                */
#if 1
                if ( config.nonequestrian_class && team_buf.flags.non_equestrian )
                {
                    ptr->team = team;
                    ptr->isNeData = true;
                    ptr->lege[0] = 0;

                    for( j = 0; j < MAX_LEGS + 1; j++ )
                    {
                        if ( (short)j == config.equestrian_leg )
                        {
                            last->lege[j] = ptr->lege[j] = 0;
                            if ( config.equestrian_leg > 1 )
                                last->leg[j] = ptr->leg[j] = ptr->leg[j-1];
                        }
                        else
                        {
                            if ( j )
                                last-> lege[j] = ptr->lege[j] = team_buf.leg[j].elapsed;
                            last->leg[j] = ptr->leg[j] = team_buf.leg[j].end;
                            ptr->lege[0] += ptr->lege[j] ;
                            last->lege[0] = ptr->lege[0] ;
                        }
                    }

                    last->start = ptr->start = team_buf.leg[0].start;
                    ptr->teamclass = config.nonequestrian_class;
                    ptr->real_teamclass = team_buf.teamclass;
                    ptr->flags = team_buf.flags;

                    ptr++;
                    sort_num++;
                }
#endif
            }
        }
    }

    // Debug
//    qDebug() << "--- Start ---";

//    sort_team_data( 0, S_L, 1 );

//    for( j = 1, ptr = sort_data; j <= sort_num; j++, ptr++ )
//    {
//        qDebug() << "T:" << ptr->team
//                << "Class:" << ptr->teamclass
//                << "NEData:" << (ptr->isNeData? 1:0)
//                << "iLP:" << (ptr->flags.notInLP ? 1:0)
//                << "iF:" << (ptr->flags.notInFastest ? 1:0)
//                << "Valid:" << (ptr->flags.valid ? 1:0)
//                << "NE:" << (ptr->flags.non_equestrian? 1:0)
//                << "B:" << (ptr->flags.bad_times ? 1 : 0 )
//                << "D:" << (ptr->flags.disqualified ? 1:0 )
//                << "V:" << (ptr->flags.vet_check ? 1:0 )
//                << "LE0:" << ptr->lege[0]
//                << "L0:" << ptr->leg[0]
//                << "LE1:" << ptr->lege[3]
//                ;
//    }

//    qDebug() << "-----------------------------------End---";
    return ( TRUE );
}

/*========================================================================
 *
 *  Generate all the stats
 *
 *  Purpose:
 *      This function is called to Generate all the stats
 *
 *  Parameters:
 *      None
 *
 *  Returns:
 *      Nothing
 *
 *========================================================================*/

void gen_stats(void)
{
    ty_s_data  *ptr;
    unsigned    i;
    int         j;
    int         k;

    /*
     * Init all the stats 
     */
    for( i = 0; i <= MAX_LEGS; i++ )
    {
        for( j = 0; j <= MAX_CLASS; j++ )
        {
            stats.team[i][j] = 0;
            stats.fast.team[i][j] = 0;
            stats.fast.time[i][j] = ( time_t ) - 1;
            stats.average[i][j] = 0;
        }
    }

    for( i = 1, ptr = sort_data; i <= sort_num; i++, ptr++ )
    {
        /*
        **  If there is any bad times in the team record, then none
        **  of the data can be trusted.
        **
        **  If the team has been disqualified, then don't use any
        **  of the data.
        */
        if (ptr->flags.notInFastest) {
            continue;
        }

        //if( ptr->flags.bad_times || ptr->flags.disqualified  )
        //    continue;

        /*
        **  Ignore non-Equestrian, unless we are processing the psuedo NE class
        **  2021: Now allow NE to be considered in the fastest stats
        */
        //if ( ptr->teamclass != config.nonequestrian_class && ptr->flags.non_equestrian)
        //    continue;


        for( j = 0; j <= config.num_legs; j++ )
        {
            if( ptr->lege[j] <= 0 )              /* Ignore bad data */
                continue;

            /*
            ** If this is the Equestrian Leg and the team has been vetted out, then this team cannot
            ** be included into the stats for this leg
            **
            ** If team has been vetted out then don't consider in fastest overall
            */
            if ( ptr->flags.vet_check && (j == config.equestrian_leg || j== 0)) {
                continue;
            }

            /* If this is the Equestrian Leg and the team is a Non-Equestrian Team, then don't include
            ** it into the stats.
            **
            ** If this is a NE team then don't consider it to be fastest overall
            */
            if ( ptr->teamclass != config.nonequestrian_class && ptr->flags.non_equestrian &&  (j == config.equestrian_leg || j== 0)) {
                continue;
            }

            /*
            **  Determine fastest team : overall
            **  Ignore the non-equestrian data as this is in the list twice
            */
            if( ( ptr->lege[j] < stats.fast.time[j][0] )
                || ( stats.fast.time[j][0] < 0 ) )
            {
                if ( ptr->teamclass != config.nonequestrian_class )
                {
                    stats.fast.team[j][0] = ptr->team;
                    stats.fast.time[j][0] = ptr->lege[j];
                }
            }

            /*
            **  Determine fastest team : within a class
            */
            if( ( ptr->lege[j] < stats.fast.time[j][ptr->teamclass] )
                || stats.fast.time[j][ptr->teamclass] < 0 )
            {
                stats.fast.team[j][ptr->teamclass] = ptr->team;
                stats.fast.time[j][ptr->teamclass] = ptr->lege[j];
            }

            /*
            **  Sum the end times : overall
            */
            if ( ptr->teamclass != config.nonequestrian_class )
            {
                stats.average[j][0] += ptr->lege[j];
                stats.team[j][0]++;
            }


            /*
            **  Sum the end times : within a class
            */
            stats.average[j][ptr->teamclass] += ptr->lege[j];
            stats.team[j][ptr->teamclass]++;
        }
    }

    /*
     * Calculate the averages 
     *      Can ignore that fact that some categories don't exists
     *      The results will be -1
     */
    for( k = 0; k <= config.num_legs; k++ )
    {
        for( j = 0; j <= config.num_class; j++ )
        {
            if( stats.team[k][j] )
                stats.average[k][j] /= stats.team[k][j];
            else
                stats.average[k][j] = ( time_t ) - 1;
        }
    }
}

/********************************* EOF ***********************************/