#include "FieldModel.h"

#include "FieldFile.h"
#include "ModelFile.h"
#include "common/File.h"
#include "common/Logger.h"
#include "../../common/OgreGenUtilites.h"



FieldModel::FieldModel()
{
}



FieldModel::~FieldModel()
{
}



void
FieldModel::Export( const Ogre::String& model_file, const Ogre::String& texture_file )
{
    MeshData data;
    data.name = "lahan";
    data.tex_width = 4096;
    data.tex_height = 512;

    VectorTexForGen textures;

    FieldFile* field_file = new FieldFile( model_file );
    File* export_file = field_file->Extract( 2 );
    delete field_file;

    int number_of_models = export_file->GetU32LE( 0x00 );

    for( int i = 0; i < number_of_models; ++i )
    {
        Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().create( data.name + Ogre::StringConverter::toString( i ) + "export", "General" );
        Ogre::SkeletonPtr skeleton = Ogre::SkeletonManager::getSingleton().create( data.name + Ogre::StringConverter::toString( i ) + "export", "General" );
        Ogre::Bone* root1 = skeleton->createBone( "0", 0 );
        Ogre::Bone* root2 = skeleton->createBone( "1", 1 );
        root1->addChild( root2 );

        Ogre::Animation* anim = skeleton->createAnimation( "Idle", 1 );
        Ogre::NodeAnimationTrack* track1 = anim->createNodeTrack( 0, root1 );
        track1->removeAllKeyFrames();
        Ogre::TransformKeyFrame* frame1 = track1->createNodeKeyFrame( 0 );
        Ogre::Matrix3 matrix;
        matrix.FromEulerAnglesYXZ( Ogre::Radian( Ogre::Degree( 0 ) ), Ogre::Radian( Ogre::Degree( -90 ) ), Ogre::Radian( Ogre::Degree( 0 ) ) );
        Ogre::Quaternion rot;
        rot.FromRotationMatrix( matrix );
        frame1->setRotation( rot );



        Logger* export_text = new Logger( "exported/" + data.name + Ogre::StringConverter::toString( i ) + ".txt" );



        int mesh_offset = export_file->GetU32LE( 0x4 + i * 0x04 );
        int mesh_next_offset = ( i + 1 == number_of_models ) ? export_file->GetFileSize() : export_file->GetU32LE( 0x8 + i * 0x04 );
        int mesh_size = mesh_next_offset - mesh_offset;

        ModelFile* mesh_file = new ModelFile( export_file, mesh_offset, mesh_size );
        mesh_file->GetModel( mesh, data, textures, export_text );
        delete mesh_file;


        skeleton->optimiseAllAnimations();
        Ogre::SkeletonSerializer skeleton_serializer;
        skeleton_serializer.exportSkeleton( skeleton.getPointer(), "exported/models/field/maps/" + data.name + "/" + data.name + Ogre::StringConverter::toString( i ) + ".skeleton" );

        // Update bounds
        Ogre::AxisAlignedBox aabb( -999, -999, -999, 999, 999, 999 );
        mesh->_setBounds( aabb, false );
        mesh->_setBoundingSphereRadius( 999 );

        mesh->setSkeletonName( "models/field/maps/" + data.name + "/" + data.name + Ogre::StringConverter::toString( i ) + ".skeleton" );

        Ogre::MeshSerializer ser;
        ser.exportMesh( mesh.getPointer(), "exported/models/field/maps/" + data.name + "/" + data.name + Ogre::StringConverter::toString( i ) + ".mesh" );
    }



    // create and export textures for model
    if( textures.size() > 0 )
    {
        File* texture = new File( texture_file );
        Vram* vram = Vram::MakeInstance().release();

        for( u32 i = 0; i < texture->GetFileSize(); )
        {
            u32 texture_header_id = texture->GetU32LE( i + 0x00 );
            u16 texture_header_vram_x = texture->GetU16LE( i + 0x04 );
            u16 texture_header_vram_y = texture->GetU16LE( i + 0x06 );
            u16 texture_header_move_x = texture->GetU16LE( i + 0x08 );
            u16 texture_header_move_y = texture->GetU16LE( i + 0x0a );
            u16 texture_header_width = texture->GetU16LE( i + 0x0c );
            u16 texture_header_height = texture->GetU16LE( i + 0x0e );

            u32 number_of_chunk = texture->GetU32LE( i + 0x18 );

            u16 vram_x = ( texture_header_vram_x + texture_header_move_x ) * 2;
            u16 vram_y = texture_header_vram_y + texture_header_move_y;

            // move to data (header always in separate sector)
            u32 temp_i = i;
            i += 0x0800;

            for( u32 j = 0; j < number_of_chunk; ++j )
            {
                // get height of current chunk
                u16 height = texture->GetU16LE( temp_i + 0x1C + j * 0x02 );
//LOGGER->Log("texture_header_width = " + ToHexString(texture_header_width, 8, '0') + "\n");
//LOGGER->Log("height = " + ToHexString(height, 8, '0') + "\n");
//LOGGER->Log("vram_x = " + ToHexString(vram_x, 8, '0') + "\n");
//LOGGER->Log("vram_y = " + ToHexString(vram_y, 8, '0') + "\n");
                // palette
                if( texture_header_id == 0x00001201 )
                {
                    for( int y = 0; y < height; ++y )
                    {
                        for( int x = 0; x < texture_header_width; ++x )
                        {
                            u16 color = texture->GetU16LE( i + x * 0x02 + y * texture_header_width * 0x02 );
                            vram->PutU16( x * 2 + vram_x, y + vram_y, color );
//LOGGER->Log("put color (" + ToHexString(color, 8, '0') + ") to x (" + ToHexString(x * 2 + vram_x, 8, '0') + ") y (" + ToHexString(y + vram_y, 8, '0') + ")\n");
                        }
                    }
                }

                // textures
                else if( texture_header_id == 0x00001200 )
                {
                    for( int y = 0; y < height; ++y )
                    {
                        for( int x = 0; x < texture_header_width * 2; ++x )
                        {
                            u8 data = texture->GetU8( i + x + y * texture_header_width * 2 );
                            vram->PutU8( x + vram_x, y + vram_y, data );
                        }
                    }
                }

                // move pointer to start of next texture
                i += ceil( ( float )( texture_header_width * 2 * height) / 0x0800 ) * 0x0800;
                // update height
                texture_header_height += height;
                vram_y += height;
            }
        }

        //vram->Save( "1.png" );

        CreateTexture( vram, data, "exported/models/field/maps/" + data.name + "/" + data.name + ".png", textures );

        delete vram;
        delete texture;
    }



    CreateMaterial( "xeno/field/" + data.name, "exported/models/field/maps/" + data.name + "/" + data.name + ".material", (textures.size() > 0) ? "models/field/maps/" + data.name + "/" + data.name + ".png" : "", "", "" );



    for( int i = 0; i < number_of_models; ++i )
    {
        Ogre::SceneManager* scene_manager = Ogre::Root::getSingleton().getSceneManager( "Scene" );
        Ogre::Entity* thisEntity = scene_manager->createEntity( data.name + Ogre::StringConverter::toString( i ), "models/field/maps/" + data.name + "/" + data.name + Ogre::StringConverter::toString( i ) + ".mesh" );
        thisEntity->setVisible( true );
        thisEntity->getAnimationState( "Idle" )->setEnabled( true );
        thisEntity->getAnimationState( "Idle" )->setLoop( true );
        Ogre::SceneNode* thisSceneNode = scene_manager->getRootSceneNode()->createChildSceneNode();
        thisSceneNode->setPosition( 0, 0, 0 );
        thisSceneNode->roll( Ogre::Radian( Ogre::Degree( 180.0f ) ) );
        thisSceneNode->yaw( Ogre::Radian( Ogre::Degree( 120.0f ) ) );
        thisSceneNode->pitch( Ogre::Radian( Ogre::Degree( 90.0f ) ) );
        thisSceneNode->attachObject( thisEntity );
    }



    delete export_file;
}
