// <ZZ> This file contains functions for reading data from a DirectX .X
//	format file.  It only handles text format files.
//	xfile_open          - Opens the specified X file and checks the header
//	xfile_close         - Closes the currently open X file
//	xfile_next_header   - Returns the text of the next section header
//	xfile_read_section  - Reads the current section, and saves all the data somewhere...
//  xfile_load_object   - A wrapper for loading an object from an xfile
FILE* xfile;

#define SECTION_EOF       0
#define SECTION_JUNK      1
#define SECTION_FRAME     2
#define SECTION_MATRIX    3
#define SECTION_MESH      4
#define SECTION_DECLDATA  5
#define SECTION_TEXTURE   6
#define SECTION_TEXPOS    7
#define SECTION_NORMAL    8

float xfile_matrix[16];
unsigned char xfile_invert_triangles = FALSE;
unsigned char xfile_matrix_valid = FALSE;

//------------------------------------------------------------------------------------
unsigned char* xfile_section_string(unsigned char section_type)
{
    if(section_type == SECTION_EOF) { return "SECTION_EOF"; }
    if(section_type == SECTION_JUNK) { return "SECTION_JUNK"; }
    if(section_type == SECTION_FRAME) { return "SECTION_FRAME"; }
    if(section_type == SECTION_MATRIX) { return "SECTION_MATRIX"; }
    if(section_type == SECTION_MESH) { return "SECTION_MESH"; }
    if(section_type == SECTION_DECLDATA) { return "SECTION_DECLDATA"; }
    if(section_type == SECTION_TEXTURE) { return "SECTION_TEXTURE"; }
    if(section_type == SECTION_TEXPOS) { return "SECTION_TEXPOS"; }
    if(section_type == SECTION_NORMAL) { return "SECTION_NORMAL"; }
}

//-----------------------------------------------------------------------------------------------
signed char xfile_open(unsigned char* filename, unsigned char invert_triangles)
{
    // <ZZ> This function opens a specified .X file.
    //      It returns TRUE if it worked okay, FALSE if there was a
    //      problem.
    unsigned char header_string[256];


    log_message("Attempting to open datafile %s", filename);
    xfile_matrix_valid = FALSE;
    xfile = fopen(filename, "r");
    xfile_invert_triangles = invert_triangles;
    if(xfile)
    {
        log_message("  File opened okay, now checking header...", filename);
        fscanf(xfile, "%16[^\n]", header_string);
        strupr(header_string);
        log_message("    %s", header_string);
        if(strstr(header_string, "XOF"))
        {
            log_message("    File is a .X file (header begins with XOF)");
        }
        else
        {
            log_message("ERROR:  File is not a .X file (header doesn't begin with XOF)");
            fclose(xfile);
            return FALSE;
        }
        if(strstr(header_string, "TXT"))
        {
            log_message("    File is text formatted (header contains TXT)");
        }
        else
        {
            log_message("ERROR:  File is not text formatted (header doesn't contain TXT)");
            fclose(xfile);
            return FALSE;
        }
        return TRUE;
    }
    log_message("ERROR:  Unable to open datafile %s", filename);
    return FALSE;
}

//-----------------------------------------------------------------------------------------------
void xfile_close(void)
{
    // <ZZ> This function closes the xfile
    if(xfile)
    {
        log_message("Closing datafile");
        fclose(xfile);
    }
}

//-----------------------------------------------------------------------------------------------
unsigned char xfile_next_header()
{
    // <ZZ> This function finds the next { in the xfile, then
    //      walks back up to 2 words (seperated by whitespace)
    //      to tell us what the text in front of the { was...
    //      It returns a value that tells us what type of section
    //      we're dealing with...
    unsigned char lastword[4][256];
    unsigned char flip;
    unsigned char keep_going;
    unsigned char return_value;



    if(xfile)
    {
        lastword[0][0] = 0;
        lastword[1][0] = 0;
        lastword[2][0] = 0;
        lastword[3][0] = 0;
        return_value = SECTION_EOF;
        if(xfile)
        {
            // Read through every word in the file until we hit the next {
            flip = 0;
            keep_going = TRUE;
            while(keep_going)
            {
                // Did we hit the end of file?
                if(fscanf(xfile, "%s", lastword[flip]) == EOF)
                {
                    keep_going = FALSE;
                    return_value = SECTION_EOF;
                }
                //log_message("Read %s from xfile", lastword[flip]);


                // Did we find the start of a new section?
                if(lastword[flip][0] == '{')
                {
                    //log_message("Found start of section");


                    // Check the last two words to see if they contain any of our keywords...
                    strupr(lastword[(flip+2)&3]);
                    strupr(lastword[(flip+3)&3]);
                    return_value = SECTION_JUNK;
                    if(strcmp(lastword[(flip+2)&3], "FRAME") == 0 || strcmp(lastword[(flip+3)&3], "FRAME") == 0)
                    {
                        // Section is the start of a new frame...
                        return_value = SECTION_FRAME;
                    }
                    if(strcmp(lastword[(flip+2)&3], "FRAMETRANSFORMMATRIX") == 0 || strcmp(lastword[(flip+3)&3], "FRAMETRANSFORMMATRIX") == 0)
                    {
                        // Section is a transformation matrix...
                        return_value = SECTION_MATRIX;
                    }
                    if(strcmp(lastword[(flip+2)&3], "MESH") == 0 || strcmp(lastword[(flip+3)&3], "MESH") == 0)
                    {
                        // Section is a mesh...
                        return_value = SECTION_MESH;
                    }
                    if(strcmp(lastword[(flip+2)&3], "DECLDATA") == 0 || strcmp(lastword[(flip+3)&3], "DECLDATA") == 0)
                    {
                        // Section is a data declaration (used for normal & texture coordinates)...
                        return_value = SECTION_DECLDATA;
                    }
                    if(strcmp(lastword[(flip+2)&3], "TEXTUREFILENAME") == 0 || strcmp(lastword[(flip+3)&3], "TEXTUREFILENAME") == 0)
                    {
                        // Section tells us what file to use for the texture...
                        return_value = SECTION_TEXTURE;
                    }
                    if(strcmp(lastword[(flip+2)&3], "MESHTEXTURECOORDS") == 0 || strcmp(lastword[(flip+3)&3], "MESHTEXTURECOORDS") == 0)
                    {
                        // Section tells us what coordinates to use for our texture...
                        return_value = SECTION_TEXPOS;
                    }
                    if(strcmp(lastword[(flip+2)&3], "MESHNORMALS") == 0 || strcmp(lastword[(flip+3)&3], "MESHNORMALS") == 0)
                    {
                        // Section tells us what coordinates to use for our vertex normals...
                        return_value = SECTION_NORMAL;
                    }
                    if(strcmp(lastword[(flip+2)&3], "TEMPLATE") == 0 || strcmp(lastword[(flip+3)&3], "TEMPLATE") == 0)
                    {
                        // Template definitions can be skipped...
                        return_value = SECTION_JUNK;
                    }


                    if(return_value > SECTION_JUNK)
                    {
                        log_message("%s == %s %s", xfile_section_string(return_value), lastword[(flip+2)&3], lastword[(flip+3)&3]);
                    }

                    keep_going = FALSE;
                }
                flip = (flip + 1)&3;
            }
        }
    }
    return return_value;
}

//-----------------------------------------------------------------------------------------------
void xfile_read_section(unsigned char section_type)
{
    // <ZZ> This function should be called after xfile_next_header()...
    //      If the header is one of the ones we're interested in, this
    //      function will read its contents -- and save the data with the
    //      corresponding object...
    float x, y, z, u, v, nx, ny, nz;
    unsigned short a, b, c, t;
    int count;
    int i;
    DWORD dx, dy, dz, du, dv;
    unsigned char texture_filename[1024];
    unsigned char fullname[1024];


    if(xfile)
    {
        if(section_type == SECTION_MESH)
        {
            object_end();
            object_start();
            fscanf(xfile, "%d;", &count);
            repeat(i, count)
            {
                fscanf(xfile, "%f;%f;%f;,", &x, &y, &z);
                if(xfile_matrix_valid)
                {
                    nx = x*xfile_matrix[0] + y*xfile_matrix[1] + z*xfile_matrix[2] + xfile_matrix[12];
                    ny = x*xfile_matrix[4] + y*xfile_matrix[5] + z*xfile_matrix[6] + xfile_matrix[13];
                    nz = x*xfile_matrix[8] + y*xfile_matrix[9] + z*xfile_matrix[10] + xfile_matrix[14];
                    x = nx;
                    y = ny;
                    z = nz;
                }
                object_add_vertex(x, y, z);
            }
            fscanf(xfile, ";");
            fscanf(xfile, "%d;", &count);
            repeat(i, count)
            {
                fscanf(xfile, "%d;%d,%d,%d;,", &t, &a, &b, &c);
                if(t == 3)
                {
                    if(xfile_invert_triangles)
                    {
                        object_add_triangle(c, b, a);
                    }
                    else
                    {
                        object_add_triangle(a, b, c);
                    }
                }
                else
                {
                    log_message("ERROR:  Object not triangulated correctly");
                }
            }
            fscanf(xfile, ";");
        }
        if(section_type == SECTION_DECLDATA)
        {
            log_message("Reading DeclData section...");
            fscanf(xfile, "%d;", &t);
            if(t == 2)
            {
                log_message("  DeclData has two pieces of information");
                fscanf(xfile, "%d;%d;%d;%d;,", &a, &b, &c, &t);
                if(a == 2 && b == 0 && c == 3 && t == 0)
                {
                    log_message("  Normal data for each vertex");
                    fscanf(xfile, "%d;%d;%d;%d;;", &a, &b, &c, &t);
                    if(a == 1 && b == 0 && c == 5 && t == 0)
                    {
                        log_message("  And texture coordinates for each triangle");
                        fscanf(xfile, "%d;", &count);
                        if(num_object < MAX_OBJECT)
                        {
                            if(object_num_vertex[num_object]*5 == count)
                            {
                                log_message("  And the count is correct for number of vertices");
                                count = object_num_vertex[num_object];
                                repeat(i, count)
                                {
                                    fscanf(xfile, "%d,%d,%d,%d,%d,", &dx, &dy, &dz, &du, &dv);
                                    x = * ((float *) (&dx));
                                    y = * ((float *) (&dy));
                                    z = * ((float *) (&dz));
                                    u = * ((float *) (&du));
                                    v = * ((float *) (&dv));
                                    log_message("    Read %f, %f, %f, %f, %f", x, y, z, u, v);
                                    object_set_normal((unsigned short) i, x, y, z);
                                    object_set_texpos((unsigned short) i, u, v);
                                }
                            }
                        }
                    }
                }
            }
        }
        if(section_type == SECTION_TEXTURE)
        {
            fscanf(xfile, "%[^\"]", texture_filename);
            fscanf(xfile, "%[\"]", texture_filename);
            fscanf(xfile, "%[.]", texture_filename);
            fscanf(xfile, "%[\\]", texture_filename);
            fscanf(xfile, "%[^\"]", texture_filename);
            sprintf(fullname, "MODELS\\%s", texture_filename);
            log_message("Texture filename == %s", fullname);
            object_set_texture(display_load_texture_file_rgba(fullname));
        }
        if(section_type == SECTION_TEXPOS)
        {
            log_message("Reading texture coordinates...");
            fscanf(xfile, "%d;", &count);
            if(num_object < MAX_OBJECT)
            {
                log_message("File has texture coordinates for %d of %d vertices", count, object_num_vertex[num_object]);
                if(count <= object_num_vertex[num_object])
                {
                    repeat(i, count)
                    {
                        fscanf(xfile, "%f;%f;,", &u, &v);
                        log_message("    Read %f, %f", u, v);
                        object_set_texpos((unsigned short) i, u, v);
                    }
                }
            }
        }
        if(section_type == SECTION_NORMAL)
        {
            log_message("Reading vertex normals...");
            fscanf(xfile, "%d;", &count);
            if(num_object < MAX_OBJECT)
            {
                log_message("File has vertex normals for %d of %d vertices", count, object_num_vertex[num_object]);
                if(count <= object_num_vertex[num_object])
                {
                    repeat(i, count)
                    {
                        fscanf(xfile, "%f;%f;%f;,", &x, &y, &z);
                        log_message("    Read %f, %f, %f", x, y, z);
                        object_set_normal((unsigned short) i, x, y, z);
                    }
                }
            }
        }
        if(section_type == SECTION_MATRIX)
        {
            if(xfile_matrix_valid == FALSE)
            {
                log_message("Reading first matrix...");
                repeat(i, 16)
                {
                    fscanf(xfile, "%f,", &x);
                    xfile_matrix[i] = x;
                }
                xfile_matrix_valid = TRUE;
            }
        }
    }
}

//------------------------------------------------------------------------------------
void xfile_load_object(unsigned char* filename, unsigned char invert_triangles)
{
    // <ZZ> This function sets up the object structure based on the contents of
    //      a .X file...  Basically a wrapper for some of the other functions...
    unsigned char fullname[1024];
    unsigned char section_type;


    // Are we trying to load the generic airway model?
    make_uppercase(filename);
    log_message("xfile_load_object(%s, %d)", filename, invert_triangles);
    if(strcmp(filename, "AIRWAY.X") == 0)
    {
        log_message("Entering Generic Airway Mode");
        object_generic_mode = TRUE;
    }
    else
    {
        log_message("Entering Non-Generic Mode");
        object_generic_mode = FALSE;
    }


    sprintf(fullname, "MODELS\\%s", filename);
    object_destroy_all();
    if(xfile_open(fullname, invert_triangles))
    {
        section_type = SECTION_JUNK;
        while(section_type)
        {
            section_type = xfile_next_header();
            xfile_read_section(section_type);
        }
        xfile_close();
        object_end();

        if(object_generic_mode)
        {
            object_weld_redundant_vertices(INTER_OBJECT_WELD_TOLERANCE, 3, 2);
            object_weld_redundant_vertices(INTER_OBJECT_WELD_TOLERANCE, 2, 0);
            object_weld_redundant_vertices(INTER_OBJECT_WELD_TOLERANCE, 0, 1);
            object_weld_redundant_vertices(INTER_OBJECT_WELD_TOLERANCE, 1, 5);
            object_weld_redundant_vertices(INTER_OBJECT_WELD_TOLERANCE, 0, 4);
            object_add_weight_hack(0.0f, -130.0f, 250.0f, 30.0f);
            object_add_weight_hack(0.0f, -220.0f, 250.0f, 30.0f);
            object_add_weight_hack(0.0f, -530.0f, 300.0f, 20.0f);
            object_add_weight_hack(0.0f, -560.0f, 310.0f, 20.0f);
            object_add_weight_hack(0.0f, -590.0f, 315.0f, 20.0f);
            object_add_weight_hack(0.0f, -620.0f, 330.0f, 20.0f);
            object_add_weight_hack(0.0f, -660.0f, 340.0f, 20.0f);
            object_add_weight_hack(0.0f, -700.0f, 345.0f, 20.0f);
        }

        object_calculate_normals();
        object_calculate_press();
    }
}

//-----------------------------------------------------------------------------------------------