/**
  *  \file u/t_ui_res_ccimageloader.cpp
  *  \brief Test for ui::res::CCImageLoader
  */

#include "ui/res/ccimageloader.hpp"

#include "t_ui_res.hpp"
#include "afl/io/constmemorystream.hpp"
#include "gfx/types.hpp"

namespace {
    String_t decodePixels(afl::base::Memory<gfx::ColorQuad_t> cs)
    {
        String_t result;
        while (gfx::ColorQuad_t* c = cs.eat()) {
            if (ALPHA_FROM_COLORQUAD(*c) == 0) {
                result += ' ';
            } else {
                switch (*c) {
                 case COLORQUAD_FROM_RGB(0, 0, 0):       result += 'b'; break;
                 case COLORQUAD_FROM_RGB(255, 255, 255): result += 'W'; break;
                 case COLORQUAD_FROM_RGB(64, 129, 64):   result += '~'; break;
                 case COLORQUAD_FROM_RGB(194, 194, 194): result += '.'; break;
                 case COLORQUAD_FROM_RGB(222, 222, 222): result += ':'; break;
                 case COLORQUAD_FROM_RGB(97, 97, 97):    result += '+'; break;
                 case COLORQUAD_FROM_RGB(125, 125, 125): result += '*'; break;
                 case COLORQUAD_FROM_RGB(141, 141, 141): result += '#'; break;
                 case COLORQUAD_FROM_RGB(238, 238, 238): result += '$'; break;
                 default:                                result += '?'; break;
                }
            }
        }
        return result;
    }

    String_t decodeRow(gfx::Canvas& can, int width, int row)
    {
        std::vector<gfx::Color_t> color(width);
        std::vector<gfx::ColorQuad_t> quad(width);
        can.getPixels(gfx::Point(0, row), color);
        can.decodeColors(color, quad);
        return decodePixels(quad);
    }
}

/** Load a compressed ".cd" image. */
void
TestUiResCCImageLoader::testCompressedCD()
{
    // share/resource/ui/cb0.cd
    static const uint8_t IMAGE[] = {
        0xc6, 0x01, 0x00, 0x00, 0xc6, 0x01, 0xff, 0x43, 0x44, 0x10, 0x00, 0x10,
        0xff, 0xc1, 0x00, 0x37, 0xff, 0x0e, 0x00, 0x37, 0x00, 0xff, 0x0e, 0x3d,
        0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00,
        0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e,
        0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f,
        0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff,
        0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x00, 0xff, 0x0e, 0x3d,
        0x0f, 0x00, 0xff, 0x0e, 0x3d, 0x0f, 0x37, 0xff, 0x0e, 0x0f, 0x3e, 0x00,
        0x00
    };
    afl::io::ConstMemoryStream ms(IMAGE);
    ui::res::CCImageLoader testee;

    // Load it
    afl::base::Ptr<gfx::Canvas> can(testee.loadImage(ms));
    TS_ASSERT(can.get() != 0);
    TS_ASSERT_EQUALS(can->getSize().getX(), 16);
    TS_ASSERT_EQUALS(can->getSize().getY(), 16);

    // Verify pixels
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  0), "*bbbbbbbbbbbbbb*");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  1), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  2), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  3), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  4), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  5), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  6), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  7), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  8), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  9), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 10), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 11), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 12), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 13), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 14), "b::::::::::::::W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 15), "*WWWWWWWWWWWWWW$");
}

/** Test an uncompressed ".gfx" image. */
void
TestUiResCCImageLoader::testUncompressedGFX()
{
    // share/resource/ui/radio0.gfx
    static const uint8_t IMAGE[] = {
        0x00, 0x08, 0x14, 0x00, 0x14, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0x20, 0x20, 0x18, 0x1d, 0x1d, 0x1d, 0x1d, 0x17, 0x20,
        0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x17, 0x20, 0x17,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x20, 0x01, 0x17, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x20, 0x17, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f,
        0xff, 0xff, 0xff, 0x20, 0x18, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0xff, 0xff, 0x20,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0xff, 0xff, 0x20, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x0f, 0xff, 0xff, 0x20, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0xff, 0xff, 0x20,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0xff, 0xff, 0x20, 0x18, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x0f, 0xff, 0xff, 0xff, 0x20, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0xff, 0xff, 0xff, 0xff,
        0x20, 0x17, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0x17, 0x20, 0x01, 0x1d, 0x1d, 0x1d, 0x1d,
        0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f, 0x1d, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0x17, 0x01, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x0f,
        0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    };

    afl::io::ConstMemoryStream ms(IMAGE);
    ui::res::CCImageLoader testee;

    // Load it
    afl::base::Ptr<gfx::Canvas> can(testee.loadImage(ms));
    TS_ASSERT(can.get() != 0);
    TS_ASSERT_EQUALS(can->getSize().getX(), 20);
    TS_ASSERT_EQUALS(can->getSize().getY(), 20);

    // Verify pixels
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  0), "                    ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  1), "       bbbbbb       ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  2), "     bb#::::*bb     ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  3), "   *b*::::::::b+*   ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  4), "   b::::::::::::+   ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  5), "  b*::::::::::::WW  ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  6), "  b::::::::::::::W  ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  7), " b#:::::::::::::::W ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  8), " b::::::::::::::::W ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20,  9), " b::::::::::::::::W ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 10), " b::::::::::::::::W ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 11), " b::::::::::::::::W ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 12), " b#:::::::::::::::W ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 13), "  b::::::::::::::W  ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 14), "  b*:::::::::::::W  ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 15), "   b::::::::::::W   ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 16), "   *b+:::::::::W:   ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 17), "     *+::::::WW     ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 18), "       WWWWWW       ");
    TS_ASSERT_EQUALS(decodeRow(*can, 20, 19), "                    ");
}

/** Test a compressed ".cc" file. */
void
TestUiResCCImageLoader::testCompressedCC()
{
    // PCC1: cc/res/cb-cond.cc
    static const uint8_t IMAGE[] = {
        0x95, 0x00, 0x00, 0x00, 0x95, 0x00, 0xfe, 0x43, 0x43, 0x10, 0x00, 0x10,
        0xfe, 0x18, 0x00, 0x20, 0xfe, 0x06, 0x22, 0xf2, 0x20, 0xfe, 0x06, 0x22,
        0xf2, 0x20, 0x22, 0x22, 0x55, 0x55, 0x25, 0x22, 0xf2, 0x20, 0x22, 0x52,
        0xfe, 0x03, 0x55, 0x22, 0xf2, 0x20, 0x22, 0x55, 0x25, 0x22, 0x55, 0x25,
        0xf2, 0x20, 0x22, 0x55, 0x22, 0x22, 0x52, 0x22, 0xf2, 0x20, 0x22, 0x55,
        0xfe, 0x04, 0x22, 0xf2, 0x20, 0x22, 0x55, 0xfe, 0x04, 0x22, 0xf2, 0x20,
        0x22, 0x55, 0x22, 0x22, 0x52, 0x22, 0xf2, 0x20, 0x22, 0x55, 0x25, 0x22,
        0x55, 0x25, 0xf2, 0x20, 0x22, 0x52, 0xfe, 0x03, 0x55, 0x22, 0xf2, 0x20,
        0x22, 0x22, 0x55, 0x55, 0x25, 0x22, 0xf2, 0x20, 0xfe, 0x06, 0x22, 0xf2,
        0x20, 0xfe, 0x06, 0x22, 0xf2, 0xf0, 0xfe, 0x07, 0xff, 0x00, 0x00
    };

    afl::io::ConstMemoryStream ms(IMAGE);
    ui::res::CCImageLoader testee;

    // Load it
    afl::base::Ptr<gfx::Canvas> can(testee.loadImage(ms));
    TS_ASSERT(can.get() != 0);
    TS_ASSERT_EQUALS(can->getSize().getX(), 16);
    TS_ASSERT_EQUALS(can->getSize().getY(), 16);

    // Verify pixels
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  0), "bbbbbbbbbbbbbbbb");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  1), "b..............W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  2), "b..............W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  3), "b.....~~~~~....W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  4), "b....~~~~~~~...W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  5), "b...~~~...~~~..W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  6), "b...~~.....~...W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  7), "b...~~.........W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  8), "b...~~.........W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16,  9), "b...~~.....~...W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 10), "b...~~~...~~~..W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 11), "b....~~~~~~~...W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 12), "b.....~~~~~....W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 13), "b..............W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 14), "b..............W");
    TS_ASSERT_EQUALS(decodeRow(*can, 16, 15), "bWWWWWWWWWWWWWWW");
}
