/*
 * This source file is part of KEOS (Free 3D Engine)
 * For the latest info, see http://www.keosengine.org/
 * E-mails : thierry.vouriot@keosengine.org, yeri@keosengine.org
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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, or go to
 * http://www.gnu.org/copyleft/lesser.txt.
 */

#include "KeosTexture.h"
#include "KeosTextureBase.h"
#include "KeosMediaManager.h"
#include "KeosResourceManager.h"
#include "KeosRenderSystem.h"
#include "KeosImage.h"
#include "KeosPixelUtils.h"
#include "KeosLogger.h"

namespace Keos
{
    //=======================================================================
    // CTexture implementation
    //=======================================================================
    
    //-----------------------------------------------------------------------
    void CTexture::Create(const TVector2I& Size, TPixelFormat Format, ulong nFlags, const String& strName)
    {
        // Creaion of the texture
        CreateFromImage(CImage(Size, Format), Format, nFlags, strName);
    }

    //-----------------------------------------------------------------------
    void CTexture::CreateFromFile(const String& strFilename, TPixelFormat Format, ulong nFlags)
    {
        // We look if the texture was not already loaded
        m_Texture = CResourceManager::Instance().Get<ITextureBase>(strFilename);

        if (!m_Texture)
        {
            // Load of the image
            CSmartPtr<CImage> Image = CMediaManager::Instance().LoadMediaFromFile<CImage>(strFilename);

            // Creation of the texture
            Load(*Image, Format, nFlags, strFilename);
        }
    }

    //-----------------------------------------------------------------------
    void CTexture::CreateFromImage(const CImage& Image, TPixelFormat Format, ulong nFlags, const String& strName)
    {
        // We look if the texture was not already loaded
        m_Texture = CResourceManager::Instance().Get<ITextureBase>(strName);

        if (!m_Texture)
            Load(Image, Format, nFlags, strName);
    }

    //-----------------------------------------------------------------------
    void CTexture::Load(const CImage& Pixels, TPixelFormat Format, ulong nFlags, const String& strName)
    {
        // If the format is compressed and that the compression is not supported, we pass in a standard format (PXF_A8R8G8B8)
        if (FormatCompressed(Format) && !Renderer->GetCapabilities()->HasCapability(CAP_TEXTURE_COMPRESSION_DXT))
        {
            Format = PXF_A8R8G8B8;
            ILogger::Log("%s %s (texture : \"%s\")",
                         "WARNING - Chosen compressed format, but not supported by the render system.\n",
                         "The used format will be PXF_A8R8G8B8. ",
                         strName.c_str());
        }

        // If the system requires dimensions in powers of 2 and if it is not the case, we modify these
        TVector2I Size(NearestPowerOfTwo(Pixels.GetSize().x), NearestPowerOfTwo(Pixels.GetSize().y));
        if ((Size != Pixels.GetSize()) && !Renderer->GetCapabilities()->HasCapability(CAP_NON_POWER_OF_2_TEXTURES))
        {
            ILogger::Log("%s %s (%dx%d -> %dx%d)",
                         "WARNING - Dimensions of texture non-powers of 2, but not supported by the render system.\n",
                         "The dimensions will be adjusted. ",
                         Pixels.GetSize().x, Pixels.GetSize().y, Size.x, Size.y);
        }
        else
        {
            Size = Pixels.GetSize();
        }

        try
        {
            // Creation of the texture
            m_Texture = Renderer->CreateTexture(Size, Format, nFlags);
        }
        catch (const std::exception& E)
        {
            LOADINGFAILED_EXCEPT(strName, E.what(),"CTexture::Load");
        }

        // Addition in the resources
        if (strName != "")
            CResourceManager::Instance().Add(strName, m_Texture);

        // If the format is compressed we change the format of the copy system of the texture (PXF_A8R8G8B8)
        if (FormatCompressed(Format))
            GetPixels() = CImage(GetSize(), PXF_A8R8G8B8);

        // Copy of the pixels
        GetPixels().CopyImage(Pixels);
        Update();
    }

    //-----------------------------------------------------------------------
    void CTexture::SaveToFile(const String& strFilename) const
    {
        CMediaManager::Instance().SaveMediaToFile(&m_Texture->m_Data, strFilename);
    }

    //-----------------------------------------------------------------------
    void CTexture::Update(const CRectangle& Rect)
    {
        if (Rect.Width() == -1)
            m_Texture->Update(CRectangle(0, 0, GetSize().x, GetSize().y));
        else
            m_Texture->Update(Rect);
    }

    //-----------------------------------------------------------------------
    CImage& CTexture::GetPixels()
    {
        return m_Texture->m_Data;
    }

    //-----------------------------------------------------------------------
    const TVector2I& CTexture::GetSize() const
    {
        return m_Texture->m_Size;
    }

    //-----------------------------------------------------------------------
    TPixelFormat CTexture::GetFormat() const
    {
        return m_Texture->m_Format;
    }

    //-----------------------------------------------------------------------
    const String& CTexture::GetName() const
    {
        return m_Texture->GetName();
    }

    //-----------------------------------------------------------------------
    const ITextureBase* CTexture::GetTexture() const
    {
        return m_Texture;
    }

    //-----------------------------------------------------------------------
    bool CTexture::operator ==(const CTexture& Texture) const
    {
        return m_Texture == Texture.m_Texture;
    }

    //-----------------------------------------------------------------------
    bool CTexture::operator !=(const CTexture& Texture) const
    {
        return !(*this == Texture);
    }

} // namespace Keos
