#include "lib/self_test.h"

#include "lib/res/graphics/tex.h"
#include "lib/res/graphics/tex_codec.h"
#include "lib/res/graphics/tex_internal.h"  // tex_encode

class TestTex : public CxxTest::TestSuite 
{
    void generate_encode_decode_compare(uint w, uint h, uint flags, uint bpp,
        const char* filename)
    {
        // generate test data
        const size_t size = w*h*bpp/8;
        u8* img = new u8[size];
        for(size_t i = 0; i < size; i++)
            img[i] = rand() & 0xFF;

        // wrap in Tex
        Tex t;
        TS_ASSERT_OK(tex_wrap(w, h, bpp, flags, img, &t));

        // encode to file format
        DynArray da;
        TS_ASSERT_OK(tex_encode(&t, filename, &da));
        memset(&t, 0, sizeof(t));

        // decode from file format
        MEM_DTOR dtor = 0;  // we'll free da manually
        TS_ASSERT_OK(tex_decode(da.base, da.cur_size, dtor, &t));

        // make sure pixel format gets converted completely to plain
        TS_ASSERT_OK(tex_transform_to(&t, 0));

        // compare img
        TS_ASSERT_SAME_DATA(tex_get_data(&t), img, size);

        // cleanup
        TS_ASSERT_OK(tex_free(&t));
        TS_ASSERT_OK(da_free(&da));
        delete[] img;
    }

public:

    // this also covers BGR and orientation transforms.
    void test_encode_decode()
    {
        // for each codec
        const TexCodecVTbl* c = 0;
        for(;;)
        {
            c = tex_codec_next(c);
            if(!c)
                break;

            // get an extension that this codec will support
            // (so that tex_encode uses this codec)
            char extension[30] = {'.'};
            strcpy_s(extension+1, 29, c->name);
            // .. make sure the c->name hack worked
            const TexCodecVTbl* correct_c;
            TS_ASSERT_OK(tex_codec_for_filename(extension, &correct_c));
            TS_ASSERT_EQUALS(c, correct_c);

            // for each test width/height combination
            const uint widths [] = { 4, 5, 4, 256, 384 };
            const uint heights[] = { 4, 4, 5, 256, 256 };
            for(size_t i = 0; i < ARRAY_SIZE(widths); i++)
            {
                // for each bit depth
                for(uint bpp = 8; bpp <= 32; bpp += 8)
                {
                    uint flags = 0;
                    if(!strcmp(extension, ".dds"))
                        flags |= (TEX_DXT&3);   // DXT3
                    if(bpp == 8)
                        flags |= TEX_GREY;
                    else if(bpp == 32)
                        flags |= TEX_ALPHA;

                    // normal
                    generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
                    // top-down
                    flags &= ~TEX_ORIENTATION; flags |= TEX_TOP_DOWN;
                    generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
                    // bottom up
                    flags &= ~TEX_ORIENTATION; flags |= TEX_BOTTOM_UP;
                    generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);

                    flags &= ~TEX_ORIENTATION;
                    flags |= TEX_BGR;

                    // bgr, normal
                    generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
                    // bgr, top-down
                    flags &= ~TEX_ORIENTATION; flags |= TEX_TOP_DOWN;
                    generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
                    // bgr, bottom up
                    flags &= ~TEX_ORIENTATION; flags |= TEX_BOTTOM_UP;
                    generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
                }   // for bpp
            }   // for width/height
        }    // foreach codec
    }

    // have mipmaps be created for a test image; check resulting size and pixels
    void test_mipmap_create()
    {
        u8 img[] = { 0x10,0x20,0x30, 0x40,0x60,0x80, 0xA0,0xA4,0xA8, 0xC0,0xC1,0xC2 };
        // assumes 2x2 box filter algorithm with rounding
        const u8 mipmap[] = { 0x6C,0x79,0x87 };
        Tex t;
        TS_ASSERT_OK(tex_wrap(2, 2, 24, 0, img, &t));
        TS_ASSERT_OK(tex_transform_to(&t, TEX_MIPMAPS));
        const u8* const out_img = tex_get_data(&t);
        TS_ASSERT_EQUALS((int)tex_img_size(&t), 12+3);
        TS_ASSERT_SAME_DATA(out_img, img, 12);
        TS_ASSERT_SAME_DATA(out_img+12, mipmap, 3);
    }

    void test_img_size()
    {
        char dummy_img[100*100*4];  // required

        Tex t;
        TS_ASSERT_OK(tex_wrap(100, 100, 32, TEX_ALPHA, dummy_img, &t));
        TS_ASSERT_EQUALS((int)tex_img_size(&t), 40000);

        // DXT rounds up to 4x4 blocks; DXT1a is 4bpp
        Tex t2;
        TS_ASSERT_OK(tex_wrap(97, 97, 4, DXT1A, dummy_img, &t2));
        TS_ASSERT_EQUALS((int)tex_img_size(&t2),  5000);
    }
};