/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: t; c-basic-offset: 3 -*- */
/*================================================================
 *
 * Project : OpenRaider
 * Author  : Mongoose
 * Website : http://www.westga.edu/~stu7440/
 * Email   : stu7440@westga.edu
 * Object  : OpenRaider
 * License : No use w/o permission (C) 2001 Mongoose
 * Comments: Loads TR 1, 2, 3, and 4 maps, meshes, and textures
 *
 *           This file was generated using Mongoose's C++
 *           template generator script.  <stu7440@westga.edu>
 *
 *-- History ------------------------------------------------
 *
 * 2003.05.13:
 * Mongoose - New API, maintance cost was becoming so high
 *            it was needed to sort out methods in groups
 *            like my newer source code -- of course some
 *            methods were altered or removed ( clean up )
 *
 * 2001.06.19:
 * Mongoose - New texture API for the new TR4/TR5 bump map
 *            support, also purged textile exposure
 *
 * 2001.05.21:
 * Mongoose - Added to project OpenRaider, more documentation
 *            than Freyja version I wrote ( 3d modeler )
 *
 *
 * 2000-05-13:
 * Mongoose - Added gcc and VC++ pragmas for packing
 *
 *            id style typedefs for structs
 *
 *            Heavy clean up and ported to C++
 *
 *            I saved yuri's notes as best I could and
 *            reformatted and corected as needed
 *
 * Mongoose - Created, based on:
 *                tr_view's tr2io.c by Yuri Zhivago, PhD,
 *                TR Rosetta Stone ( TombRaider pak format specs )
 ================================================================*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdarg.h>

#include <TombRaider.h>

#ifdef __TEST_TR5_DUMP_TGA
#include <TGA.h>
#endif

#ifdef DEBUG_MEMORY
#include <memory_test.h>
#endif


void dump_textures(TombRaider *tr, char *mapname)
{
#ifdef __TEST_TR5_DUMP_TGA
    int i;
    unsigned char *image;
    unsigned char *bumpmap;
    char buffer[128];
    FILE *f;


    if (!tr || !mapname)
        return;

    // Dump textures
    printf("\n\t[Texture dumping for '%s']\n", mapname);

    for (i = 0; i < tr->NumTextures(); i++)
    {
        tr->Texture(i, &image, &bumpmap);

        if (image)
        {
            snprintf(buffer, 128, "%s-%03i-texture.tga", mapname, i);

            f = fopen(buffer, "wb");

            if (f)
            {
                if (!mtk_image__tga_save(f, image, 256, 256, 4))
                    printf("\tWrote texture %s\n", buffer);

                fclose(f);
            }

            snprintf(buffer, 128, "%s.lst", mapname);

            f = fopen(buffer, "a");

            if (f)
            {
                fprintf(f, "%s-%03i-texture.tga;\n", mapname, i);
                fclose(f);
            }

            delete [] image;
        }

        if (bumpmap)
        {
            snprintf(buffer, 64, "%s-%03i-bumpmap.tga", mapname, i);

            f = fopen(buffer, "wb");

            if (f)
            {
                if (!mtk_image__tga_save(f, bumpmap, 256, 256, 4))
                    printf("\tWrote texture %s\n", buffer);

                fclose(f);
            }

            delete [] bumpmap;
        }
    }

    for (i = 0; i < tr->NumSpecialTextures(); i++)
    {
        image = tr->SpecialTexTile(i);

        snprintf(buffer, 128, "%s-%03i-special.tga", mapname, i);

        f = fopen(buffer, "wb");

        if (f)
        {
            if (!mtk_image__tga_save(f, image, 256, 256, 4))
                printf("\tWrote texture %s\n", buffer);

            fclose(f);
        }
        else
        {
            printf("\tFailed to write texture %s\n", buffer);
        }

        delete [] image;
    }
#else
    printf("Texture dumping not in this build\n");
#endif
}


void dump_mesh(TombRaider *tr, char *mapname, int index)
{
    tr2_object_texture_t *object_texture = NULL;
    tr2_mesh_t *meshes = NULL;
    unsigned int v, check;
    int i, triangles, rectangles, t_index;
    char buffer[128];
    float rgba[4];
    float s, t;
    char id[8];
    FILE *f;


    if (!mapname || !tr)
    {
        return;
    }

    snprintf(buffer, 128, "%s-%03i.mesh", mapname, index);

    object_texture = tr->ObjectTextures();
    meshes = tr->Mesh();


    f = fopen(buffer, "wb");

    if (!f)
    {
        perror("Failed to write mesh :");
        return;
    }

    // Setup header id and check points
    strncpy(id, "TRMESH", 7);
    id[7] = 0;
    check = 0xcdcdcdcd;


    fwrite(id, 8, 1, f);
    fwrite(&meshes[index].num_vertices, 2, 1, f);
    fwrite(&meshes[index].num_textured_triangles, 2, 1, f);
    fwrite(&meshes[index].num_textured_rectangles, 2, 1, f);
    fwrite(&meshes[index].num_coloured_triangles, 2, 1, f);
    fwrite(&meshes[index].num_coloured_rectangles, 2, 1, f);
    fwrite(&meshes[index].collision_size, 4, 1, f);


    // Textured triangles ////////////////////////
    fwrite(&check, 4, 1, f);
    triangles = meshes[index].num_textured_triangles;

    for (i = 0; triangles > 0 && i < triangles; i++)
    {
        t_index = meshes[index].textured_triangles[i].texture;

        // Store texture info
        fwrite(&object_texture[t_index].tile, 2, 1, f);
        fwrite(&object_texture[t_index].transparency_flags, 2, 1, f);

        // Store vertices
        v = meshes[index].textured_triangles[i].vertices[0];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].textured_triangles[i].vertices[1];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].textured_triangles[i].vertices[2];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        // Store texels
        s = tr->adjustTexel(object_texture[t_index].vertices[0].xpixel,
                object_texture[t_index].vertices[0].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[0].ypixel,
                object_texture[t_index].vertices[0].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);


        s = tr->adjustTexel(object_texture[t_index].vertices[1].xpixel,
                object_texture[t_index].vertices[1].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[1].ypixel,
                object_texture[t_index].vertices[1].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);

        s = tr->adjustTexel(object_texture[t_index].vertices[2].xpixel,
                object_texture[t_index].vertices[2].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[2].ypixel,
                object_texture[t_index].vertices[2].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);
    }

    fwrite(&check, 4, 1, f);


    // Textured rectangles ////////////////////////
    fwrite(&check, 4, 1, f);
    rectangles = meshes[index].num_textured_rectangles;

    for (i = 0; rectangles > 0 && i < rectangles; i++)
    {
        t_index = meshes[index].textured_rectangles[i].texture;

        // Store texture info
        fwrite(&object_texture[t_index].tile, 2, 1, f);
        fwrite(&object_texture[t_index].transparency_flags, 2, 1, f);

        // Store vertices
        v = meshes[index].textured_rectangles[i].vertices[0];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].textured_rectangles[i].vertices[1];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].textured_rectangles[i].vertices[2];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].textured_rectangles[i].vertices[3];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        // Store texels
        s = tr->adjustTexel(object_texture[t_index].vertices[0].xpixel,
                object_texture[t_index].vertices[0].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[0].ypixel,
                object_texture[t_index].vertices[0].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);


        s = tr->adjustTexel(object_texture[t_index].vertices[1].xpixel,
                object_texture[t_index].vertices[1].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[1].ypixel,
                object_texture[t_index].vertices[1].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);

        s = tr->adjustTexel(object_texture[t_index].vertices[2].xpixel,
                object_texture[t_index].vertices[2].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[2].ypixel,
                object_texture[t_index].vertices[2].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);

        s = tr->adjustTexel(object_texture[t_index].vertices[3].xpixel,
                object_texture[t_index].vertices[3].xcoordinate);
        t = tr->adjustTexel(object_texture[t_index].vertices[3].ypixel,
                object_texture[t_index].vertices[3].ycoordinate);
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);
    }

    fwrite(&check, 4, 1, f);

    // Coloured triangles ////////////////////////
    fwrite(&check, 4, 1, f);
    triangles = meshes[index].num_coloured_triangles;

    for (i = 0; triangles > 0 && i < triangles; i++)
    {
        // Store vertices
        v = meshes[index].coloured_triangles[i].vertices[0];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].coloured_triangles[i].vertices[1];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].coloured_triangles[i].vertices[2];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        // Store color
        switch (tr->Engine())
        {
            case TR_VERSION_1:
                tr->getColor(meshes[index].coloured_triangles[i].texture
                        & 0xff, rgba);
                break;
            default:
                tr->getColor((meshes[index].coloured_triangles[i].texture>>8)
                        & 0xff, rgba);
        }

        s = rgba[0];
        t = rgba[1];
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);

        s = rgba[2];
        t = rgba[3];
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);
    }

    fwrite(&check, 4, 1, f);

    // Coloured rectangles ////////////////////////
    fwrite(&check, 4, 1, f);
    rectangles = meshes[index].num_coloured_rectangles;

    for (i = 0; rectangles > 0 && i < rectangles; i++)
    {
        // Store vertices
        v = meshes[index].coloured_rectangles[i].vertices[0];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].coloured_rectangles[i].vertices[1];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].coloured_rectangles[i].vertices[2];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        v = meshes[index].coloured_rectangles[i].vertices[3];
        fwrite(&meshes[index].vertices[v].x, 2, 1, f);
        fwrite(&meshes[index].vertices[v].y, 2, 1, f);
        fwrite(&meshes[index].vertices[v].z, 2, 1, f);

        // Store color
        switch (tr->Engine())
        {
            case TR_VERSION_1:
                tr->getColor(meshes[index].coloured_rectangles[i].texture
                        & 0xff, rgba);
                break;
            default:
                tr->getColor((meshes[index].coloured_rectangles[i].texture>>8)
                        & 0xff, rgba);
        }

        s = rgba[0];
        t = rgba[1];
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);

        s = rgba[2];
        t = rgba[3];
        fwrite(&s, 4, 1, f);
        fwrite(&t, 4, 1, f);
    }

    fwrite(&check, 4, 1, f);
    fclose(f);

    printf(".");
    fflush(stdout);
}


void dump_lara_stuff(char *mapname, TombRaider &tr)
{
    unsigned int i, j, k, n;
    tr2_moveable_t *moveable = tr.Moveable();
    unsigned int numMoveables = tr.NumMoveables();
    char filename[64];
    unsigned char *riff;
    unsigned int riffSz, total = 0;
    FILE *f;


    snprintf(filename, 63, "%s-lara.nfo", mapname);
    f = fopen(filename, "w");

    if (!f)
    {
        perror("Failed to write lara.nfo: ");
        return;
    }

    for (i = 0; i < numMoveables; ++i)
    {
        j = n = 0;

        if (moveable[i].object_id == 0)
        {
            j = moveable[i].starting_mesh;
            n = moveable[i].num_meshes + j;

            fprintf(f, "Lara (%i)\n", i);
        }
        else if (moveable[i].object_id == 30)
        {
            j = moveable[i].starting_mesh;
            n = moveable[i].num_meshes + j;

            fprintf(f, "Lara ponytail\n");
        }
        else if (tr.Engine() == TR_VERSION_4)
        {
            switch (moveable[i].object_id)
            {
                case 8: // Joints, ( interconnecting skin/ploys )
                case 9:
                    fprintf(f, "TR4 lara joints (%i)\n", i);
                    j = moveable[i].starting_mesh;
                    n = moveable[i].num_meshes + j;
            }
        }

        for (k = j; j < n; ++j)
        {
            fprintf(f, "\tMesh[%i] = %i\n", (j - k), j);
        }
    }

    fclose(f);

    printf("\nDumping %i audio samples: ", tr.getSoundSamplesCount());

    for (i = 0, j = 0; i < tr.getSoundSamplesCount(); ++i)
    {
        tr.getSoundSample(i, &riffSz, &riff);

        total += riffSz;

        snprintf(filename, 63, "%s-%03i.wav", mapname, j++);
        f = fopen(filename, "wb");

        if (!f)
        {
            perror("Failed to write riff.wav: ");
            continue;
        }

        fwrite(riff, 1, riffSz, f);
        fclose(f);

        delete [] riff;

        printf(".");
        fflush(stdout);
    }

    printf("\n");

    if (total)
    {
        printf("Dumped %ibytes (%.2f MB) of audio samples\n",
                total, (float)total/1024000.0);
    }
}


void percent_callback(int p)
{
    printf("Level %i%% loaded\n", p);
}


int test_main(int argc, char *argv[])
{
    TombRaider tr;
    char mapname[128];
    int len, i, j;


    printf("[TombRaider class test]\n");

    tr.setDebug(true);

    if (argc > 2)
    {
        // Strip for mapname //////////////////////////////
        len = strlen(argv[2]);

        for (i = len, j = 0; i > 0; i--, j++)
            if (argv[2][i] == '/' || argv[2][i] == '\\')
                break;

        j--;

        memset(mapname, 0, 128);

        for (i = 0; i < len - j && i < 30; i++)
            mapname[i] = argv[2][i + len - j];

        ////////////////////////////////////////////////////


        if (strncmp(argv[1], "load", 4) == 0)
        {
            if (!tr.Load(argv[2], percent_callback))
            {
                printf("\nmain: Load reports success.\n");
            }
        }
        else if (strncmp(argv[1], "dump", 4) == 0)
        {
            if (!tr.Load(argv[2], percent_callback))
            {
                printf("\nmain: Load reports success.\n");

                dump_textures(&tr, mapname);

                printf("Mesh dumping: ");
                for (i = 0; i < tr.getMeshCount(); i++)
                {
                    dump_mesh(&tr, mapname, i);
                }

                if (argc > 3)
                {
                    printf("\nLoading external sound SFX.\n");
                    tr.loadSFX(argv[3]);
                }

                dump_lara_stuff(mapname, tr);

                printf("\n");
            }
            else
            {
                printf("\nmain: Load failed.\n");
            }
        }
        else
            printf("\n\n%s [ load | dump ] filename [sound.sfx]\n", argv[0]);
    }
    else
    {
        printf("\n\n%s [ load | dump ] filename [sound.sfx]\n", argv[0]);
    }

    return 0;
}


int main(int argc, char *argv[])
{
    test_main(argc, argv);

#ifdef DEBUG_MEMORY
    dump_memory_report();
#endif

    return 0;
}