/*
 * Glyph Keeper benchmark common routines
 *
 * Copyright (c) 2003-2007 Kirill Kryukov
 *
 * This file is part of Glyph Keeper library, and may only be used,
 * modified, and distributed under the terms of the Glyph Keeper
 * license, located in the file 'license.txt' within this package.
 */



/* Glyph cache object. */
GLYPH_KEEP* keep = 0;

/* Font face - it keeps the font data. */
GLYPH_FACE* fc1;

/* Font renderer objects. */
GLYPH_REND** rends = 0;
GLYPH_REND** shadow_rends = 0;
GLYPH_REND** outline_rends = 0;


void screen_message(int x,int y,char* text);

void screen_rect(int x1,int y1,int x2,int y2);



/*
 * Messenger function.
 */
void message(const char* const s)
{
    if (logfile)
    {
        fprintf(logfile,"%s",s);
        fflush(logfile);
    }
}



/*
 * Common cleanup code for Glyph Keeper benchmarks.
 */
void glyph_keeper_finish()
{
    if (rends) { free(rends); rends = 0; }
    if (shadow_rends) { free(shadow_rends); shadow_rends = 0; }
    if (outline_rends) { free(outline_rends); outline_rends = 0; }
}



/*
 * Common code for initializing Glyph Keeper library,
 * loading a font, setting up a cache, etc.
 */
void glyph_keeper_startup()
{
    /* Setting Glyph Keeper font search path. */
    if (font_path)
    {
        fprintf(logfile,"font_path is set to \"%s\"\n",font_path);
        gk_set_font_path(font_path);
    }

    /* Setting Glyph Keeper messenger function. */
    /* Not that we really need it in benchmark, but just for case. */
    gk_set_messenger(message);


    /* Loading a font from file. */
    if (font_file)
    {
        fprintf(logfile,"Using font file \"%s\", size: %d\n",font_file,text_size);
        screen_message(text_x,text_y,"Loading font from file ..");
        fc1 = gk_load_face_from_file(font_file,0);
        if (!fc1) { fprintf(logfile,"Can't load font face from file \"%s\"\n",font_file); exit(1); }
        screen_message(text_x,text_y,"Loading font from file .. OK");
        text_y += text_line_height;
    }
    else { fprintf(logfile,"font file is not specified\n"); exit(1); }


    /* Creating a glyph cache. */
    if (cache_on)
    {
        keep = gk_create_keeper(0,cache_size);
        if (!keep) { fprintf(logfile,"Can't create glyph cache\n"); exit(1); }
    }
}



/*
 * Creating and configuring all font renderers that will be used in benchmark.
 */
void create_font_renderers()
{
    int i;

    /* Creating and configuring text renderers. */
    /* This is suggested practice for user program: create a bunch of renderers,
       configure them, and use them in the program. */

    screen_message(text_x,text_y,"Creating text renderers ..");

    rends = (GLYPH_REND**) malloc (number_of_renderers * sizeof(GLYPH_REND*));
    if (!rends) { fprintf(logfile,"Can't allocate memory for array of %d renderers\n",number_of_renderers); exit(1); }
    if (shadow_on)
    {
        shadow_rends = (GLYPH_REND**) malloc (number_of_renderers * sizeof(GLYPH_REND*));
        if (!shadow_rends) { fprintf(logfile,"Can't allocate memory for array of %d shadow renderers\n",number_of_renderers); exit(1); }
    }
    if (outline_on)
    {
        outline_rends = (GLYPH_REND**) malloc (number_of_renderers * sizeof(GLYPH_REND*));
        if (!outline_rends) { fprintf(logfile,"Can't allocate memory for array of %d outilne renderers\n",number_of_renderers); exit(1); }
    }

    for (i=0; i<number_of_renderers; i++)
    {
        /* Creating a renderer object. */
        rends[i] = gk_create_renderer(fc1,0);
        if (!rends[i]) { fprintf(logfile,"Can't create renderer #%d\n",i); exit(1); }

        /* Setting undefined char to 0. */
        /* We don't need anything rendered instead of missing characters. */
        gk_rend_set_undefined_char(rends[i],0);

        /* Setting font size. */
        /* All our renderers use the same size - it's a benchmark. */
        gk_rend_set_size_pixels(rends[i],text_size,text_size);

        /* Setting hinting (grid-fitting). */
        /* It does not affect the speed if you use the glyph cache. */
        if (!hinting_on) gk_rend_set_hinting_off(rends[i]);

        /* Setting italic angle. */
        if (random_italic_on)
            gk_rend_set_italic_angle_in_degrees( rends[i], (double)(rand_4()%(90*1000+1)) / 1000 - 45 );
        else if (fixed_italic)
            gk_rend_set_italic_angle_in_degrees( rends[i], fixed_italic );

        /* Setting boldness. */
        if (random_bold_on)
            gk_rend_set_bold_strength( rends[i], rand_4()%601-300 );
        else if (fixed_bold)
            gk_rend_set_bold_strength( rends[i], fixed_bold );

        /* Setting antialiasing. */
        if (antialiasing_on) gk_rend_set_antialiasing_on(rends[i]);
        else gk_rend_set_antialiasing_off(rends[i]);

        /* Setting alpha transparency and color. */
        if (transparent_on)
        {
            unsigned a = (rand_4()%250)+5;
            gk_rend_set_text_alpha_color( rends[i], (a << 24) | (0xFFFFFF&rand_4()) );
        }
        else { gk_rend_set_text_alpha_color( rends[i], 0xFF000000|rand_4() ); }

        /* Setting the background color. */
        if (background_on) gk_rend_set_back_color(rends[i],rand_4()&0xFFFFFF);

        /* Setting angle. */
        if (angle_on) gk_rend_set_angle_in_radians(rends[i],ONE_DEGREE*(rand_4()&0xFF));

        /* Connecting this renderer to the glyph cache object. */
        /* Single cache is used by all renderers. */
        if (cache_on) gk_rend_set_keeper(rends[i],keep);


        /* Adding a shadow renderer. */
        if (shadow_on)
        {
            shadow_rends[i] = gk_create_copy_of_renderer(rends[i]);
            if (!shadow_rends[i]) { fprintf(logfile,"Can't create shadow renderer #%d\n",i); exit(1); }

            /* Mixing shadow alpha with the text alpha. */
            /* Shadow color is black (0x000000). */
            gk_rend_set_text_alpha_color( shadow_rends[i],
                ((( (gk_rend_get_text_alpha(rends[i])+1) * (shadow_alpha+1) >> 8) - 1) << 24) | 0x000000 );

            gk_rend_set_before_renderer(rends[i],shadow_rends[i],shadow_dx,shadow_dy);

            /*if (background_on) gk_rend_set_back_color(rends[i],-1);*/
        }


        /* Adding an outline renderer. */
        if (outline_on)
        {
            outline_rends[i] = gk_create_copy_of_renderer(rends[i]);
            if (!outline_rends[i]) { fprintf(logfile,"Can't create outline renderer #%d\n",i); exit(1); }

            gk_rend_set_bold_strength( outline_rends[i], gk_rend_get_bold_strength(rends[i]) - outline_width );

            if (transparent_on)
            {
                unsigned a = (rand_4()%250)+5;
                gk_rend_set_text_alpha_color( outline_rends[i], (a << 24) | (0xFFFFFF&rand_4()) );
            }
            else { gk_rend_set_text_alpha_color( outline_rends[i], 0xFF000000|rand_4() ); }

            gk_rend_set_back_color(outline_rends[i],-1);

            gk_rend_set_after_renderer(rends[i],outline_rends[i],0,0);
        }
    }

    screen_message(text_x,text_y,"Creating text renderers .. OK");
    text_y += text_line_height;

    text_ascender = gk_rend_ascender_pixels(rends[0]);


    {
        int char_num = 0;

        /* Checking how many characters we can render within the specified range. */
        /* This is the number of characters specified in the font file. */
        for (i=range_32_start; i<range_32_start+range_32_length; i++)
        {
            if (gk_rend_has_character(rends[0],i)) char_num++;
        }

        fprintf(logfile,"Can render %d out of %d characters\n",char_num,range_32_length);

        /* If there are no characters then we can't benchmark. */
        if (char_num <= 0)
        {
            fprintf(logfile,"Can't render any characters!\n");
            exit(1);
        }
    }
}



/*
 * Pre-caching 
 */
void precache_all_glyphs()
{
    if (cache_on)
    {
        int xmin = 204, xmax = xmin+78, xl = xmin, xr = xl;
        int i;

        fprintf(logfile,"Pre-caching the glyphs\n");
        screen_message(text_x,text_y,"Pre-caching the glyphs [          ]");

        for (i=0; i<number_of_renderers; i++)
        {
            gk_precache_range(rends[i],range_32_start,range_32_start+range_32_length-1);

            /* Updating progress bar. */
            xr = xmin+(i*(xmax-xmin)/number_of_renderers);
            if (xr > xl) { screen_rect(xl,text_y,xr,text_y+6); xl = xr; }
        }

        fprintf(logfile," - done pre-caching!\n");
        screen_message(text_x,text_y,"Pre-caching the glyphs [          ] OK");
        text_y += text_line_height;
    }
}



/*
 * Filling the test dataset with random data.
 * Also making sure that each entry fits the screen.
 */
void fill_test_dataset()
{
    if (test_dataset_num_elements > 0)
    {
        int ri = 0;
        int xmin = 220, xmax = xmin+78, xl = xmin, xr = xl;
        int i;

        fprintf(logfile,"Filling the test dataset of %d elements\n",test_dataset_num_elements);
        screen_message(text_x,text_y,"Filling the test dataset [          ]");

        for (i=0; i<test_dataset_num_elements; i++)
        {
            int w,h,px,py,c,n=0;

            /* Trying to create a random string which will fit within the screen. */
            /* Because theoretically you can set font size to 100x100 and what should I do then. */
            do
            {
                test_dataset[i].length = min_string_length + rand_4() % string_length_variety;
                for (c=0; c<test_dataset[i].length; c++)
                {
                    do
                    {
                        test_dataset[i].text[c] = range_32_start + (rand_4() % range_32_length);
                    }
                    while (!gk_rend_has_character(rends[ri],test_dataset[i].text[c]));
                }
                test_dataset[i].text[c] = 0;

                gk_text_dimensions_utf32(rends[ri],test_dataset[i].text,&w,&h,&px,&py);

                n++;
            }
            while ( (w >= area_width || h >= area_height) && n<100 );

            /* No way, man! This font size is too big! You can't benchmark with it. */
            /* At least try to set a larger screen (window) size. */
            /* ("screen_width" and "screen_height" options in bench.cfg file). */
            if (n >= 100)
            {
                fprintf(logfile,"Error: Font size is too large! One line of text does not even fit the screen!\n");
                exit(1);
            }

            /* Nicely placing the text so that it will never run out of screen. */
            /* Running out of screen is bad because it the prints instantly */
            /* and distorts the benchmark result. */
            test_dataset[i].x = area_left + rand_4()%(area_width-w) + px;
            test_dataset[i].y = area_top + rand_4()%(area_height-h) + py;

            /* Going to the next renderer. */
            ri = (ri+1)%number_of_renderers;

            /* Updating progress bar. */
            xr = xmin+(i*(xmax-xmin)/test_dataset_num_elements);
            if (xr > xl) { screen_rect(xl,text_y,xr,text_y+6); xl = xr; }
        }
        fprintf(logfile,"Test dataset filled!\n");
        screen_message(text_x,text_y,"Filling the test dataset [          ] OK");
        text_y += text_line_height;
    }
}
