#include "DevLib.h"
/******************************************************************************
 * Tile.cpp                                                                   *
 ******************************************************************************
 * Project      : Futura (DevLib Demo)                                        *
 * License      : LGPL (full notice can be found at root directory)           *
 * Created by   : Arnaud Storq (norecess@devlib-central.org)                  *
 ******************************************************************************/
#include "Tile.h"
#include "Application.h"

// ---------------------------------------------------------------------------- LOCAL DEFINES
#define SIZE                0.05f
#define CUBE_HEIGHT         0.7f

// ---------------------------------------------------------------------------- CONSTRUCTOR
Tile::Tile( TileType type, const string textureFilename )
{
    m_type = type;
    m_texture = 0;
    m_mesh = 0;

    if ( m_type != TILE_EMPTY )
    {
        m_texture = Texture::createTexture( false, textureFilename );

        if ( m_type == TILE_CUBE )
            createCube( );
        else
            createQuad( );
    }
}

// ---------------------------------------------------------------------------- DESTRUCTOR
Tile::~Tile( )
{
    if ( m_texture )
        delete m_texture;

    if ( m_mesh )
        delete m_mesh;
}

// ---------------------------------------------------------------------------- CREATE TILE
Tile *Tile::createTile( TileType type, const string textureFilename )
{
    return new Tile( type, textureFilename );
}

// ---------------------------------------------------------------------------- RENDER
void Tile::render( )
{
    if ( m_type == TILE_EMPTY )
        return;

    RenderSystem *rs = RenderSystemManager::getRenderSystem( );

    if ( m_type == TILE_CUBE )
        rs->setRenderState( RS_CULLING, RV_CULLING_CW );
    else
        rs->setRenderState( RS_CULLING, RV_CULLING_CCW );

    m_mesh->render( );
}

// ---------------------------------------------------------------------------- GET MESH
Mesh *Tile::getMesh( )
{
    return m_mesh;
}

// ---------------------------------------------------------------------------- GET SIZE
float Tile::getSize( )
{
    return SIZE * 2.0f;
}

// ---------------------------------------------------------------------------- GET TYPE
TileType Tile::getType( )
{
    return m_type;
}

// ---------------------------------------------------------------------------- CREATE QUAD
void Tile::createQuad( )
{
    // Create mesh quad
    m_mesh = Mesh::createMesh( );

    VertexBuffer *vertexBuffer = VertexBuffer::createVertexBuffer( false, 4 );
    assert( vertexBuffer );

    Vertice *vertices = vertexBuffer->lock( );
    vertices[0].posX = -SIZE;
    vertices[0].posY = SIZE;
    vertices[0].posZ = 0.0f;
    vertices[0].u1 = 0.0f;
    vertices[0].v1 = 1.0f;
    vertices[0].u2 = 0.0f;
    vertices[0].v2 = 1.0f;
    vertices[1].posX = SIZE;
    vertices[1].posY = SIZE;
    vertices[1].posZ = 0.0f;
    vertices[1].u1 = 1.0f;
    vertices[1].v1 = 1.0f;
    vertices[1].u2 = 1.0f;
    vertices[1].v2 = 1.0f;
    vertices[2].posX = -SIZE;
    vertices[2].posY = -SIZE;
    vertices[2].posZ = 0.0f;
    vertices[2].u1 = 0.0f;
    vertices[2].v1 = 0.0f;
    vertices[2].u2 = 0.0f;
    vertices[2].v2 = 0.0f;
    vertices[3].posX = SIZE;
    vertices[3].posY = -SIZE;
    vertices[3].posZ = 0.0f;
    vertices[3].u1 = 1.0f;
    vertices[3].v1 = 0.0f;
    vertices[3].u2 = 1.0f;
    vertices[3].v2 = 0.0f;
    vertexBuffer->unlock( );
    m_mesh->setVertexBuffer( vertexBuffer );

    IndexBuffer *indexBuffer = IndexBuffer::createIndexBuffer( false, 6 );
    int *indices = indexBuffer->lock( );
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 2;
    indices[4] = 1;
    indices[5] = 3;
    indexBuffer->unlock( );
    m_mesh->setIndexBuffer( indexBuffer );

    Material *material = m_mesh->createMaterial( );
    material->setBoundary( 0, indexBuffer->getNumIndices( ), 0, vertexBuffer->getNumVertices( ) );

    material->setRenderState( 0, RS_BLENDING, RV_BLENDING_NONE );
    material->setRenderState( 0, RS_MAPPING, RV_MAPPING_VERTEX_UV_CLAMP );
    material->setTexture( 0, m_texture );

    // Set texture stage 1 properties
    material->setRenderState( 1, RS_BLENDING, RV_BLENDING_MULTITEXTURE_MODULATE );
    material->setRenderState( 1, RS_MAPPING, RV_MAPPING_VERTEX_UV_WRAPPING );
    material->setTexture( 1, Application::getInstance( )->getDetail( ) );
}

// ---------------------------------------------------------------------------- STATIC CUBE
const Tile::Cube Tile::cubeFace[ ] =
{
	// background
	-SIZE, SIZE, 0.0f,
	SIZE, SIZE, 0.0f,
	-SIZE, -SIZE, 0.0f,
	SIZE, -SIZE, 0.0f,	

	// right
	SIZE, SIZE, 0.0f,
	SIZE, SIZE, SIZE * CUBE_HEIGHT,
	SIZE, -SIZE, 0.0f,
	SIZE, -SIZE, SIZE * CUBE_HEIGHT,

	// front
	SIZE, SIZE, SIZE * CUBE_HEIGHT,
	-SIZE, SIZE, SIZE * CUBE_HEIGHT,
	SIZE, -SIZE, SIZE * CUBE_HEIGHT,
	-SIZE, -SIZE, SIZE * CUBE_HEIGHT,

	// left
	-SIZE, SIZE, SIZE * CUBE_HEIGHT,
	-SIZE, SIZE, 0.0f,
	-SIZE, -SIZE, SIZE * CUBE_HEIGHT,
	-SIZE, -SIZE, 0.0f,

	// down
	SIZE, -SIZE, 0.0f,
	SIZE, -SIZE, SIZE * CUBE_HEIGHT,
	-SIZE, -SIZE, 0.0f,
	-SIZE, -SIZE, SIZE * CUBE_HEIGHT,

	// up
	-SIZE, SIZE, 0.0f,
	-SIZE, SIZE, SIZE * CUBE_HEIGHT,
	SIZE, SIZE, 0.0f,
	SIZE, SIZE, SIZE * CUBE_HEIGHT
};

// ---------------------------------------------------------------------------- CREATE CUBE
void Tile::createCube( )
{
    m_mesh = Mesh::createMesh( );

    VertexBuffer *vertexBuffer = VertexBuffer::createVertexBuffer( false, 4 * 6 );
    IndexBuffer *indexBuffer = IndexBuffer::createIndexBuffer( false, 6 * 6 );

    for ( int face = 0; face < 6; ++face )
    {
        Material *material = m_mesh->createMaterial( );
        material->setBoundary( face * 6, 6, face * 4, 4 );
        material->setRenderState( 0, RS_MAPPING, RV_MAPPING_VERTEX_UV_CLAMP );
        material->setTexture( 0, m_texture );

        // Set texture stage 1 properties
        material->setRenderState( 1, RS_BLENDING, RV_BLENDING_MULTITEXTURE_MODULATE );
        material->setRenderState( 1, RS_MAPPING, RV_MAPPING_VERTEX_UV_WRAPPING );
        material->setTexture( 1, Application::getInstance( )->getDetail( ) );

        Vertice *vertices = vertexBuffer->lock( );
        vertices[ ( face * 4 ) ].posX = cubeFace[ face ].posX1;
        vertices[ ( face * 4 ) ].posY = cubeFace[ face ].posY1;
        vertices[ ( face * 4 ) ].posZ = cubeFace[ face ].posZ1;
        vertices[ ( face * 4 ) ].u1 = 0.0f;
        vertices[ ( face * 4 ) ].v1 = 0.0f;
        vertices[ ( face * 4 ) ].u2 = 0.0f;
        vertices[ ( face * 4 ) ].v2 = 0.0f;

        vertices[ ( face * 4 ) + 1 ].posX = cubeFace[ face ].posX2;
        vertices[ ( face * 4 ) + 1 ].posY = cubeFace[ face ].posY2;
        vertices[ ( face * 4 ) + 1 ].posZ = cubeFace[ face ].posZ2;
        vertices[ ( face * 4 ) + 1 ].u1 = 1.0f;
        vertices[ ( face * 4 ) + 1 ].v1 = 0.0f;
        vertices[ ( face * 4 ) + 1 ].u2 = 1.0f;
        vertices[ ( face * 4 ) + 1 ].v2 = 0.0f;

        vertices[ ( face * 4 ) + 2 ].posX = cubeFace[ face ].posX3;
        vertices[ ( face * 4 ) + 2 ].posY = cubeFace[ face ].posY3;
        vertices[ ( face * 4 ) + 2 ].posZ = cubeFace[ face ].posZ3;
        vertices[ ( face * 4 ) + 2 ].u1 = 0.0f;
        vertices[ ( face * 4 ) + 2 ].v1 = 1.0f;
        vertices[ ( face * 4 ) + 2 ].u2 = 0.0f;
        vertices[ ( face * 4 ) + 2 ].v2 = 1.0f;

        vertices[ ( face * 4 ) + 3 ].posX = cubeFace[ face ].posX4;
        vertices[ ( face * 4 ) + 3 ].posY = cubeFace[ face ].posY4;
        vertices[ ( face * 4 ) + 3 ].posZ = cubeFace[ face ].posZ4;
        vertices[ ( face * 4 ) + 3 ].u1 = 1.0f;
        vertices[ ( face * 4 ) + 3 ].v1 = 1.0f;
        vertices[ ( face * 4 ) + 3 ].u2 = 1.0f;
        vertices[ ( face * 4 ) + 3 ].v2 = 1.0f;

        vertexBuffer->unlock( );
    }

    int *indices = indexBuffer->lock( );
    for ( int i = 0; i < 6; ++i )
    {
        *indices = ( i * 4 );
        indices++;
        *indices = ( i * 4 ) + 1;
        indices++;
        *indices = ( i * 4 ) + 2;
        indices++;

        *indices = ( i * 4 ) + 2;
        indices++;
        *indices = ( i * 4 ) + 1;
        indices++;
        *indices = ( i * 4 ) + 3;
        indices++;
    }
    indexBuffer->unlock( );

    m_mesh->setVertexBuffer( vertexBuffer );
    m_mesh->setIndexBuffer( indexBuffer );
}
