/*
 * Glyph Keeper benchmark utility 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.
 */

#define MAX_STRING_LENGTH 20

typedef struct PIECE_OF_TEXT
{
    int x,y,length;
    int text_color, back_color;
    unsigned text[MAX_STRING_LENGTH+1];
} PIECE_OF_TEXT;


#define ONE_DEGREE (3.14159265/128)


char *default_config_file_name = "bench.cfg";
char program_name[100] = "";
char logfile_name[100] = "";

FILE* logfile = 0;

int screen_width = 800;
int screen_height = 600;
int area_width = 800, area_height = 600, area_left = 5, area_top = 30;
int color_depth = 32;
int draw_to_screen = 1;
int clip_on = 0;

int text_size = 24;
int text_ascender = 24;

int hinting_on = 0;
int random_italic_on = 0;
int fixed_italic = 0;
int random_bold_on = 0;
int fixed_bold = 0;

int shadow_on = 0;
int shadow_dx = 0;
int shadow_dy = 0;
int shadow_alpha = 0;

int outline_on = 0;
int outline_width = 0;

int antialiasing_on = 1;
int transparent_on = 0;
int background_on = 0;
int angle_on = 0;
int cache_on = 1;
int cache_size = 10*1024*1024;
const int max_cache_size = 512*1024*1024;

int test_dataset_size = 0;
int test_dataset_num_elements = 0;
int test_dataset_num_bytes = 0;
PIECE_OF_TEXT *test_dataset = 0;

int number_of_renderers = 0;

int min_string_length = 1;
int max_string_length = MAX_STRING_LENGTH;
int string_length_variety;

char *font_path = 0;
char *font_file = 0;

int range_8_start = 32;
int range_8_end = 126;
int range_8_length = 95;
int range_32_start = 32;
int range_32_end = 126;
int range_32_length = 95;


/* Where the text will be printed. */
int text_x = 12;
int text_y = 7;
int text_line_height = 12;




/* LCG32 */
/*unsigned seed = 0;
#define rand_2() ( seed=(seed)*1103515245+12345, (unsigned)((seed>>16)&0xFFFF) )
#define rand_1() (rand_2()&0xFF)
#define rand_4() ( seed=(seed)*1103515245+12345, (unsigned)(seed^rand_2()) )
#define rand_3() (rand_4()&0xFFFFFF)*/

/* Random10 */
unsigned SEED_X = 521288629;
unsigned SEED_Y = 362436069;
#define rand_4() (   ( (SEED_X=18000*(SEED_X&65535)+(SEED_X>>16))<<16 ) + \
                     ( (SEED_Y=30903*(SEED_Y&65535)+(SEED_Y>>16))&65535 )   )

/* LCG64 */
/*unsigned long long rand_next = 0;
#define rand_4() (rand_next=rand_next*6364136223846793005LL+1,rand_next>>21)*/

/* Sapparot */
/*unsigned a=0, b=0;
#define rand_4()  (a+=0x9e3779b9,a=(a<<7)|(a>>25),b^=(~a)^(a<<3),b=(b<<7)|(b>>25),b^=a,a^=b,b^=a,a^b)*/


unsigned char str8[101] = "";
unsigned str32[101] = {0};


#define reset_str8()                                                                    \
{                                                                                       \
    int i = 0;                                                                          \
    length = min_string_length + rand_4() % string_length_variety;                      \
    for (; i<length; i++) { str8[i] = range_8_start + (rand_4() % range_8_length); }    \
    str8[length] = 0;                                                                   \
}


#define reset_str32()                                                                       \
{                                                                                           \
    int i = 0;                                                                              \
    length = min_string_length + rand_4() % string_length_variety;                          \
    for (; i<length; i++) { str32[i] = range_32_start + (rand_4() % range_32_length); }     \
    str32[length] = 0;                                                                      \
}


#define reset_x_y()                                     \
{                                                       \
    x = rand_4()%(area_width);                          \
    y = rand_4()%(area_height)+text_ascender+area_top;  \
}


/* */
char* strdup1(const char* s)
{
    char *a;
    if (!s) return 0;
    a = (char*)malloc(strlen(s)+1);
    if (!a) return 0;
    strcpy(a,s);
    return a;
}


/*
 * Since the config file is also used by SDL benchmark, we can't rely on
 * Allegro's config routines here.
 */
char* read_str(char* buf,char* name,char** var)
{
    char *start = buf, *c;
    int len;
    while (*start==' ') start++;
    if (*start=='#' || *start==0) return 0;
    { char *t = start; while (*t) { if (*t==10 || *t==13) *t = 0; t++; } }
    len = strlen(name);
    if (strncmp(start,name,len)) return 0;
    c = start+len;
    while (*c==' ') c++;
    if (*c!='=') return 0;
    c++;
    while (*c==' ') c++;
    if (!*c) return 0;
    return (*var = strdup1(c));
}


int read_int(char* buf,char* name,int* var)
{
    char *str = 0, *c;
    if (!read_str(buf,name,&str)) return 0;
    if (!str) return 0;
    c = str;
    if (*c=='-') c++;
    while (*c) { if (!isdigit(*c)) { free(str); return 0; } c++; }
    *var = atoi(str);
    free(str);
    return 1;
}


void done_benchmark()
{
    if (logfile) { fclose(logfile); logfile = 0; }
    if (test_dataset) { free(test_dataset); test_dataset = 0; }
}


void read_config_file(char* config_file_name)
{
    FILE *cfg;
    char buf[100];

    if (!config_file_name) return;
    if (logfile) fprintf(logfile,"Reading config file \"%s\"\n",config_file_name);

    cfg = fopen(config_file_name,"r");
    if (!cfg)
    {
        if (logfile) fprintf(logfile,"Can't open \"%s\"\n",config_file_name);
        return;
    }

    while (fgets(buf,100,cfg))
    {
        if (buf[0]==0 || buf[0]==13 || buf[0]==10 || buf[0]=='#') continue;
        if (read_str(buf,"font_path",&font_path)) continue;
        if (read_str(buf,"font_file",&font_file)) continue;
        if (read_int(buf,"screen_width",&screen_width)) continue;
        if (read_int(buf,"screen_height",&screen_height)) continue;
        if (read_int(buf,"color_depth",&color_depth)) continue;
        if (read_int(buf,"draw_to_screen",&draw_to_screen)) continue;
        if (read_int(buf,"clip_on",&clip_on)) continue;
        if (read_int(buf,"text_size",&text_size)) continue;
        if (read_int(buf,"hinting_on",&hinting_on)) continue;
        if (read_int(buf,"random_italic_on",&random_italic_on)) continue;
        if (read_int(buf,"fixed_italic",&fixed_italic)) continue;
        if (read_int(buf,"random_bold_on",&random_bold_on)) continue;
        if (read_int(buf,"fixed_bold",&fixed_bold)) continue;

        if (read_int(buf,"shadow_on",&shadow_on)) continue;
        if (read_int(buf,"shadow_dx",&shadow_dx)) continue;
        if (read_int(buf,"shadow_dy",&shadow_dy)) continue;
        if (read_int(buf,"shadow_alpha",&shadow_alpha)) continue;

        if (read_int(buf,"outline_on",&outline_on)) continue;
        if (read_int(buf,"outline_width",&outline_width)) continue;

        if (read_int(buf,"antialiasing_on",&antialiasing_on)) continue;
        if (read_int(buf,"transparent_on",&transparent_on)) continue;
        if (read_int(buf,"background_on",&background_on)) continue;
        if (read_int(buf,"angle_on",&angle_on)) continue;
        if (read_int(buf,"cache_on",&cache_on)) continue;
        if (read_int(buf,"cache_size",&cache_size)) continue;
        if (read_int(buf,"min_string_length",&min_string_length)) continue;
        if (read_int(buf,"max_string_length",&max_string_length)) continue;
        if (read_int(buf,"range_8_start",&range_8_start)) continue;
        if (read_int(buf,"range_8_end",&range_8_end)) continue;
        if (read_int(buf,"range_32_start",&range_32_start)) continue;
        if (read_int(buf,"range_32_end",&range_32_end)) continue;
        if (read_int(buf,"test_dataset_size",&test_dataset_size)) continue;
        if (read_int(buf,"number_of_renderers",&number_of_renderers)) continue;
        if (logfile) fprintf(logfile,"[config] Can't parse line: \"%s\"\n",buf);
    }
}


void init_benchmark(char* prg_name,char* exe_name,char* config_file_name)
{
    if (!prg_name || !strlen(prg_name)) { return; }

    atexit(done_benchmark);

    strncpy(program_name,prg_name,95);
    strncpy(logfile_name,program_name,95);
    strcat(logfile_name,".log");
    logfile = fopen(logfile_name,"w");
    if (logfile)
    {
        setbuf(logfile,0);
        fprintf(logfile,"%s (%s)\n",program_name,exe_name);
    }

    read_config_file(default_config_file_name);
    if (config_file_name) read_config_file(config_file_name);


    if (color_depth!=8 && color_depth!=15 && color_depth!=16 && color_depth!=24 && color_depth!=32) color_depth = 32;
    if (logfile) fprintf(logfile,"[config] graphics mode: %d x %d x %d\n",screen_width,screen_height,color_depth);

    if (draw_to_screen != 0) draw_to_screen = 1;
    if (clip_on != 0) clip_on = 1;
    if (text_size<4) text_size = 4;
    if (text_size>500) text_size = 500;
    text_ascender = text_size;

    if (hinting_on) hinting_on = 1;

    if (fixed_italic < -45) fixed_italic = -45;
    if (fixed_italic > 45) fixed_italic = 45;
    if (fixed_bold < -300) fixed_bold = -300;
    if (fixed_bold > 300) fixed_bold = 300;

    if (antialiasing_on) antialiasing_on = 1;
    if (transparent_on) transparent_on = 1;
    if (background_on) background_on = 1;
    if (angle_on != 0) angle_on = 1;
    if (cache_on != 0) cache_on = 1;
    if (cache_size < 0) cache_size = 0;
    if (cache_size > max_cache_size) cache_size = max_cache_size;

    if (min_string_length < 1) min_string_length = 1;
    if (min_string_length > MAX_STRING_LENGTH) min_string_length = MAX_STRING_LENGTH;
    if (max_string_length < min_string_length) max_string_length = min_string_length;
    if (max_string_length > MAX_STRING_LENGTH) max_string_length = MAX_STRING_LENGTH;
    string_length_variety = max_string_length - min_string_length + 1;

    if (range_8_start < 1) range_8_start = 1;
    if (range_8_start > 255) range_8_start = 255;
    if (range_8_end < range_8_start) range_8_end = range_8_start;
    if (range_8_end > 255) range_8_end = 255;
    range_8_length = range_8_end - range_8_start + 1;

    if (range_32_start < 1) range_32_start = 1;
    if (range_32_start > 0x10FFFF) range_32_start = 0x10FFFF;
    if (range_32_end < range_32_start) range_32_end = range_32_start;
    if (range_32_end > 0x10FFFF) range_32_end = 0x10FFFF;
    range_32_length = range_32_end - range_32_start + 1;

    area_width = screen_width - 10;
    area_height = screen_height - area_top - 5;

    /* I don't think you need more than 1000 renderes. */
    /* If you do, then just change this code. */
    if (number_of_renderers > 1000) number_of_renderers = 1000;
    if (number_of_renderers < 1) number_of_renderers = 1;
    if (logfile) fprintf(logfile,"Using %d renderer objects\n",number_of_renderers);


    if (logfile) fprintf(logfile,"[config] Hinting: %s, antialiasing: %s, transparency: %s, background: %s, angle: %s\n",
        hinting_on?"ON":"OFF",antialiasing_on?"ON":"OFF",transparent_on?"ON":"OFF",
        background_on?"ON":"OFF",angle_on?"ON":"OFF");

    if (logfile) fprintf(logfile,"[config] Cache: %s, cache size: %d\n",
        cache_on?"ON":"OFF",cache_on?cache_size:0);

    if (logfile) fprintf(logfile,"[config] %d <= length <= %d\n",min_string_length,max_string_length);


    if (logfile) fprintf(logfile,"Configuration loaded successfully\n");


    /* Calculating number of elements in test dataset and allocating the memory. */
    test_dataset_size -= (test_dataset_size % sizeof(PIECE_OF_TEXT)); 
    if (test_dataset_size > 0)
    {
        if (logfile) fprintf(logfile,"Test dataset is %d bytes\n",test_dataset_size);
        test_dataset_num_elements = test_dataset_size / sizeof(PIECE_OF_TEXT);
        test_dataset_num_bytes = test_dataset_num_elements * sizeof(PIECE_OF_TEXT);
        test_dataset = (PIECE_OF_TEXT*) malloc (test_dataset_num_bytes);
        if (test_dataset == 0)
        {
            if (logfile) fprintf(logfile,"Can't allocate %d bytes for test dataset!\n",test_dataset_num_bytes);
            if (logfile) fprintf(logfile,"Switching to real-time benchmark mode\n");
            test_dataset_size = 0;
            test_dataset_num_elements = 0;
            test_dataset_num_bytes = 0;
        }
        else
        {
            if (logfile) fprintf(logfile,"Allocated %d bytes for %d elements of test dataset\n",
                                         test_dataset_num_bytes, test_dataset_num_elements );
        }
    }
}
