/*
 * pawstexturemanager.cpp - Author: Andrew Craig
 *
 * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation (version 2 of the License)
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
// pawstexturemanager.cpp: implementation of the pawsTextureManager class.
//
//////////////////////////////////////////////////////////////////////
#include <psconfig.h>

#include <csutil/xmltiny.h>
#include <iutil/databuff.h>
#include <igraphic/imageio.h>
#include <igraphic/image.h>

#include <ivideo/txtmgr.h>
#include <ivideo/graph2d.h>


#include "util/log.h"

#include "pawstexturemanager.h"



//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

pawsTextureManager::pawsTextureManager( iObjectRegistry* object )
{
    objectReg = object;

    vfs = CS_QUERY_REGISTRY( objectReg, iVFS );
    xml = csPtr<iDocumentSystem>(new csTinyDocumentSystem);
}

pawsTextureManager::~pawsTextureManager()
{
    imageDescriptions.DeleteAll();
}

psImageDescription* pawsTextureManager::GetImageDescription( const char* name )
{
    if ( name == NULL ) 
        return NULL;
        
    for ( size_t x = 0; x < imageDescriptions.Length(); x++ )
    {
        if ( imageDescriptions[x]->resourceName == name )
        {
            imageDescriptions[x]->refCount++;

            if ( imageDescriptions[x]->texture == NULL )
            {
                if ( !imageDescriptions[x]->CreateTexture(objectReg) )
                    return NULL;
            }

            return imageDescriptions[x];
        }
    }

    return NULL;
}

bool pawsTextureManager::LoadImageList( const char* listName )
{    
    csRef<iDataBuffer> buff = vfs->ReadFile( listName );

    if ( !buff || !buff->GetSize() )
    {
        return false;
    }
    
    csRef<iDocument> doc = xml->CreateDocument();
    const char* error = doc->Parse( buff );
    if ( error )
    {
        Error3("Error %s in %s", error, listName);
        return false;
    }

    csRef<iDocumentNode> root = doc->GetRoot();
    if (!root)
    {
        Error2("XML error in %s", listName);
        return false;
    }
    
    csRef<iDocumentNode> topNode = root->GetNode("image_list");
    if(!topNode)
    {
        Error2("Missing tag <image_list> in %s", listName);
        return false;
    }

    csRef<iDocumentNodeIterator> iter = topNode->GetNodes();

    while ( iter->HasNext() )
    {
        csRef<iDocumentNode> node = iter->Next();

        if ( node->GetType() != CS_NODE_ELEMENT )
            continue;

        if ( strcmp( node->GetValue(), "image" ) == 0 )
        {
            // Do not load 2 of the same name
            bool found = false;
            for ( size_t x = 0; x < imageDescriptions.Length(); x++ )
            {
                if ( imageDescriptions[x]->resourceName == node->GetAttributeValue("resource"))
                {
                    printf("Skipping '%s' because it's already loaded\n",imageDescriptions[x]->resourceName.GetData());
                    found = true;
                    break;
                }
            }

            // load
            if(!found)
                ReadImageDescription( node );
        }
    }

    return true;     
}

void pawsTextureManager::Remove( const char* resource )
{
    for ( size_t x = 0; x < imageDescriptions.Length(); x++ )
    {
        if ( imageDescriptions[x]->resourceName == resource )
        {
            imageDescriptions.DeleteIndex(x);
            return;
        }
    }
}

void pawsTextureManager::AddImageDescription( psImageDescription* desc )
{
    imageDescriptions.Push(desc);
}

void pawsTextureManager::ReadImageDescription( iDocumentNode* node )
{
    psImageDescription *imageDescription = new psImageDescription( this ); 

    // Read off the image and file vars
    imageDescription->imageFileLocation = node->GetAttributeValue( "file" );
    imageDescription->resourceName = node->GetAttributeValue( "resource" );
    
    const char *t = node->GetAttributeValue("tiled");
    if (t && !strcmp(t,"true") )
        imageDescription->tiled = true;
    else
        imageDescription->tiled = false;

    csRef<iDocumentNodeIterator> iter = node->GetNodes();

    while ( iter->HasNext() )
    {
        csRef<iDocumentNode> childNode = iter->Next();       

        // Read the texture rectangle for this image.
        if ( strcmp( childNode->GetValue(), "texturerect" ) == 0 )
        {
            imageDescription->textureRectangle.xmin = childNode->GetAttributeValueAsInt("x");
            imageDescription->textureRectangle.ymin = childNode->GetAttributeValueAsInt("y");

            int width = childNode->GetAttributeValueAsInt("width");
            int height = childNode->GetAttributeValueAsInt("height");

            imageDescription->textureRectangle.SetSize(width, height);
        }

        // Read the default alpha value.
        if ( strcmp( childNode->GetValue(), "alpha" ) == 0 )
        {
            imageDescription->defaultAlphaValue = childNode->GetAttributeValueAsInt("level");            
        }

        // Read the default transparent colour.
        if ( strcmp( childNode->GetValue(), "trans" ) == 0 )
        {
            imageDescription->defaultTransparentColourRed   = childNode->GetAttributeValueAsInt("r");            
            imageDescription->defaultTransparentColourGreen = childNode->GetAttributeValueAsInt("g");            
            imageDescription->defaultTransparentColourBlue  = childNode->GetAttributeValueAsInt("b");                                    
        }

        // Load states
        if ( strcmp( childNode->GetValue(), "state" ) == 0 )
        {
            csString preload = childNode->GetAttributeValue("preload");
            if ( preload == "yes" )
                imageDescription->preLoad = true;
            else
                imageDescription->preLoad = false;


            csString stay = childNode->GetAttributeValue("remaininmemory");
            if ( stay == "yes" )
                imageDescription->stayInMemory = true;
            else
                imageDescription->stayInMemory = false;
        }
    }

    
    imageDescription->refCount = 0;

    imageDescriptions.Push( imageDescription );   
}

pawsImage::pawsImage( iObjectRegistry* objectReg ) : csSimplePixmap ( NULL )
{
    imageDesc = NULL;
    tiled     = false;
    graphics3D = CS_QUERY_REGISTRY( objectReg, iGraphics3D );
}



pawsImage::~pawsImage()
{
}

void pawsImage::Description( psImageDescription* desc )
{
    if ( desc == NULL )
    {
        if ( imageDesc ) imageDesc->DecRef();
        imageDesc=NULL;
        return;
    }
    
    if ( imageDesc )
        imageDesc->DecRef();

    imageDesc = desc;

    SetTextureHandle( desc->texture );

    SetTextureRectangle( desc->textureRectangle.xmin,
                         desc->textureRectangle.ymin,
                         desc->textureRectangle.Width(),
                         desc->textureRectangle.Height() );    

    alphaValue = desc->defaultAlphaValue;

    tiled = desc->tiled;
}

void pawsImage::Draw( int x, int y )
{
    if ( imageDesc )
    {
        csSimplePixmap::Draw( graphics3D, x, y, alphaValue );
    }
}

void pawsImage::Draw( csRect rect )
{
    if ( imageDesc )
    {
        if (!tiled)
        {
            csSimplePixmap::DrawScaled( graphics3D,
                                        rect.xmin,
                                        rect.ymin,
                                        rect.Width(),
                                        rect.Height(),
                                        alphaValue );
        }
        else
        {
            csSimplePixmap::DrawTiled(  graphics3D,
                                        rect.xmin,
                                        rect.ymin,
                                        rect.Width(),
                                        rect.Height(),
                                        0, 0,
                                        alphaValue );
        }
    }
}

void pawsImage::Draw( int x, int y, int newWidth, int newHeight )
{   
    // If 0 then assume default
    if ( newWidth == 0 ) 
        newWidth = GetWidth();
    if ( newHeight == 0 )
        newHeight = GetHeight();

    if (!tiled)
    {
        csSimplePixmap::DrawScaled( graphics3D,
                                    x,
                                    y,
                                    newWidth,
                                    newHeight,
                                    alphaValue );
    }
    else
    {
        csSimplePixmap::DrawTiled( graphics3D,
                                   x,
                                   y,
                                   newWidth,
                                   newHeight,
                                   0, 0,
                                   alphaValue );
    }
}

psImageDescription::psImageDescription( pawsTextureManager  *manager )
{
    texManager = manager;
    refCount = 0;
    
    defaultTransparentColourBlue  = -1;
    defaultTransparentColourGreen = -1;
    defaultTransparentColourRed   = -1;

    defaultAlphaValue = 0;
}

void psImageDescription::DecRef()
{    
    if ( refCount == 0 )
    {
        if ( !stayInMemory )
        {
            texManager->Remove( resourceName );
        }      
    }
}


bool psImageDescription::CreateTexture( iObjectRegistry* object )
{
    csRef<iImageIO>    imageLoader = CS_QUERY_REGISTRY( object, iImageIO );
    csRef<iVFS>        vfs         = CS_QUERY_REGISTRY( object, iVFS );
    csRef<iGraphics3D> graphics3D  = CS_QUERY_REGISTRY( object, iGraphics3D );

    csRef<iTextureManager> textureManager = graphics3D->GetTextureManager();

    int textureFormat = textureManager->GetTextureFormat();

    csRef<iImage> ifile;
    csRef<iDataBuffer> buf( vfs->ReadFile( imageFileLocation, false ) );

    if ( buf == NULL || buf->GetSize() == 0 )
    {
        Error2( "Could not open image: >%s<", (const char*)imageFileLocation );
        return false;
    }
    
    ifile = imageLoader->Load( buf, textureFormat );

    
    if ( !ifile )
    {
        Error2( "Image >%s< could not be loaded by the iImageID", 
                (const char*)imageFileLocation );
        return false;
    }

    width = ifile->GetWidth();
    height = ifile->GetHeight();


    texture = textureManager->RegisterTexture( ifile, 
                                               CS_TEXTURE_2D |
                                               CS_TEXTURE_3D |
                                               CS_TEXTURE_NOMIPMAPS |
                                            //  This doesn't seem to have an effect, and crashes some Macs.
											   CS_TEXTURE_CLAMP |
                                               CS_TEXTURE_NPOTS);

    if ( !texture )
    {
        Error1("Failed to Register Texture");
        return false;
    }
    
    // If colour key exists.
    if ( defaultTransparentColourBlue  != -1 &&
         defaultTransparentColourGreen != -1 &&
         defaultTransparentColourRed   != -1 )
    {
        texture->SetKeyColor( defaultTransparentColourRed, 
                              defaultTransparentColourGreen, 
                              defaultTransparentColourBlue );
    }

    if ( textureRectangle.Width() == 0 || textureRectangle.Height() == 0 )
    {
        textureRectangle.xmax = width;
        textureRectangle.ymax = height;        
    }   
    
    return true;
}

