#include "OgreGenUtilites.h"

#include "Logger.h"



void
CreateTextureFromVram( const Ogre::PixelBox& pb, Vram* vram, const int start_x, const int start_y, const int clut_x, const int clut_y, const int texture_x, const int texture_y, const BPP bpp, const bool transparency )
{
    for( u32 y = 0; y < 256; ++y )
    {
        u32* data = static_cast< u32* >( pb.data ) + ( y + start_y ) * pb.rowPitch;

        for( u32 x = 0; x < 256; ++x )
        {
            if( bpp == BPP_4 )
            {
                u8 pixel = vram->GetU8( texture_x * 2 + x / 2, texture_y + y );

                u16 clut1 = vram->GetU16( clut_x * 2 + ( pixel & 0xf ) * 2, clut_y );
                u16 clut2 = vram->GetU16( clut_x * 2 + ( pixel >> 4 ) * 2, clut_y );

                u32 ogre_pixel1 = ( ( ( clut1 & 0x1f ) * 255 / 31 ) << 0x18 ) | ( ( ( ( clut1 >>  5 ) & 0x1f ) * 255 / 31 ) << 0x10 ) | ( ( ( ( clut1 >> 10 ) & 0x1f ) * 255 / 31 ) << 0x8 ) | 0;

                u8 stp = ( clut1 & 0x8000 ) >> 15;
                AddTransparency( ogre_pixel1, transparency, stp );

                u32 ogre_pixel2 = ( ( ( clut2 & 0x1f ) * 255 / 31 ) << 0x18 ) | ( ( ( ( clut2 >>  5 ) & 0x1f ) * 255 / 31 ) << 0x10 ) | ( ( ( ( clut2 >> 10 ) & 0x1f ) * 255 / 31 ) << 0x8 ) | 0;

                stp = ( clut2 & 0x8000 ) >> 15;
                AddTransparency( ogre_pixel2, transparency, stp );

                data[ start_x + x ] = ogre_pixel1;
                ++x;
                data[ start_x + x ] = ogre_pixel2;
            }
            else if( bpp == BPP_8 )
            {
                u8 pixel = vram->GetU8( texture_x * 2 + x, texture_y + y );
                u16 clut = vram->GetU16( clut_x * 2 + pixel * 2, clut_y );

                u32 ogre_pixel = ( ( ( clut & 0x1f ) * 255 / 31 ) << 0x18 ) | ( ( ( ( clut >>  5 ) & 0x1f ) * 255 / 31 ) << 0x10 ) | ( ( ( ( clut >> 10 ) & 0x1f ) * 255 / 31 ) << 0x8 ) | 0;

                u8 stp = ( clut & 0x8000 ) >> 15;
                AddTransparency( ogre_pixel, transparency, stp );

                data[ start_x + x ] = ogre_pixel;
            }
            else if( bpp == BPP_BLACK )
            {
                data[ start_x + x ] = 0xffffffff;
            }
        }
    }
}



void
CreateTexture( Vram* vram, const MeshData& mesh_data, const Ogre::String& texture_file_name, const VectorTexForGen& textures )
{
    Ogre::TexturePtr ptex;
    Ogre::HardwarePixelBufferSharedPtr buffer;
    ptex = Ogre::TextureManager::getSingleton().createManual( "DynaTex", "General", Ogre::TEX_TYPE_2D, mesh_data.tex_width, mesh_data.tex_height, 0, Ogre::PF_R8G8B8A8, Ogre::TU_STATIC );
    buffer = ptex->getBuffer( 0, 0 );
    buffer->lock( Ogre::HardwareBuffer::HBL_DISCARD );
    const Ogre::PixelBox& pb = buffer->getCurrentLock();

    for( unsigned int i = 0; i < textures.size(); ++i )
    {
        LOGGER->Log( "CreateTexture palette_x=\"" + Ogre::StringConverter::toString( textures[ i ].palette_x ) + "\" palette_y=\"" + Ogre::StringConverter::toString( textures[ i ].palette_y ) + "\" bpp=\"" + Ogre::StringConverter::toString( textures[ i ].bpp ) + "\"." );
        CreateTextureFromVram( pb, vram, textures[ i ].start_x, textures[ i ].start_y, textures[ i ].palette_x, textures[ i ].palette_y, textures[ i ].texture_x, textures[ i ].texture_y, textures[ i ].bpp, true );
    }

    Ogre::Image image;
    image.loadDynamicImage( ( Ogre::uchar* )pb.data, mesh_data.tex_width, mesh_data.tex_height, Ogre::PF_R8G8B8A8 );
    image.save( texture_file_name );
    buffer->unlock();

    return;
}



void
CreateMaterial( const Ogre::String& material_name, const Ogre::String& material_file_name, const Ogre::String& texture_name, const Ogre::String& vertex_program, const Ogre::String& fragment_program )
{
    Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(material_name, "General");
    Ogre::Technique* tech = material->getTechnique(0);
    Ogre::Pass* pass1 = tech->getPass(0);
    pass1->setVertexColourTracking(Ogre::TVC_AMBIENT);
    //pass1->setCullingMode(Ogre::CULL_NONE);
    if( texture_name != "" )
    {
        pass1->setAlphaRejectFunction(Ogre::CMPF_GREATER);
        pass1->setAlphaRejectValue(0);
        Ogre::TextureUnitState* tex = pass1->createTextureUnitState();
        tex->setTextureName(texture_name);
        tex->setNumMipmaps(-1);
        tex->setTextureFiltering(Ogre::TFO_NONE);
    }

    if( vertex_program != "" )
    {
        pass1->setVertexProgram( vertex_program, true );
    }

    if( fragment_program != "" )
    {
        pass1->setFragmentProgram( fragment_program, true );
    }

    Ogre::MaterialSerializer mat;
    mat.exportMaterial(material, material_file_name);
}



void
AddTexture( TexForGen& texture, const MeshData& data, VectorTexForGen& textures, Logger* logger )
{
    for( unsigned int i = 0; i < textures.size(); ++i )
    {
        if( texture == textures[ i ] )
        {
            texture.start_x = textures[ i ].start_x;
            texture.start_y = textures[ i ].start_y;
            return;
        }
    }

    if( logger != NULL )
    {
        //logger->Log("New Texture: X:" + ToHexString(texture.texture_x, 4, '0') + ", Y:" + ToHexString(texture.texture_y, 4, '0') + ", CLUTX:" + ToHexString(texture.palette_x, 4, '0') + ", CLUTY:" + ToHexString(texture.palette_y, 4, '0') + ", bpp:" + ToHexString(texture.bpp, 4, '0') + "\n");
    }

    float start_x = 0;
    float start_y = 0;

    if( textures.size() > 0 )
    {
        start_x = float(textures[ textures.size() - 1 ].start_x);
        start_y = float(textures[ textures.size() - 1 ].start_y);

        if( start_x + 256 >= data.tex_width )
        {
            if( start_y + 256 >= data.tex_height )
            {
                if( logger != NULL )
                {
                    logger->Log( "[ERROR] Can't add anymore textures. Increase tex size.\n");
                }
                return;
            }

            start_x = 0;
            start_y += 256;
        }
        else
        {
            start_x += 256;
        }
    }
    texture.start_x = start_x;
    texture.start_y = start_y;
    textures.push_back( texture );

    if( logger != NULL )
    {
        //logger->Log("Startx:" + ToHexString(texture.start_x, 4, '0') + ", Starty:" + ToHexString(texture.start_y, 4, '0') + "\n");
    }
}



void
AddTransparency( u32& colour, const bool transparency, const bool stp )
{
    if( transparency == false )
    {
        if( stp == false && ( colour & 0xffffff00 ) == 0 )
        {
            colour |= 0;
        }
        else
        {
            colour |= 255;
        }
    }
    else
    {
        if( stp == false && ( colour & 0xffffff00 ) == 0 )
        {
            colour |= 0;
        }
        else if( stp == false && ( colour & 0xffffff00 ) != 0 )
        {
            colour |= 255;
        }
        else if( stp == true && ( colour & 0xffffff00 ) == 0)
        {
            colour |= 255;
        }
        else if( stp == true && ( colour & 0xffffff00 ) != 0)
        {
            colour |= 255 / 4;
        }
    }
}
