/*
 * 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 "KeosD3D9Texture.h"
#include "KeosD3D9Enum.h"
#include "KeosD3D9RenderSystem.h"
#include "KeosD3D9Exception.h"
#include "KeosPixelUtils.h"
#include "KeosRectangle.h"

namespace Keos
{
    //=======================================================================
    // CD3D9Texture implementation
    //=======================================================================

    //-----------------------------------------------------------------------
    CD3D9Texture::CD3D9Texture(const TVector2I& Size, TPixelFormat Format, bool bHasMipmaps, bool bAutoMipmaps, IDirect3DTexture9* pTexture) :
    ITextureBase(Size, Format, bHasMipmaps, bAutoMipmaps),
    m_pTexture   (pTexture)
    {

    }

    //-----------------------------------------------------------------------
    CD3D9Texture::~CD3D9Texture()
    {
       
    }

    //-----------------------------------------------------------------------
    IDirect3DTexture9* CD3D9Texture::GetD3D9Texture() const
    {
        return m_pTexture;
    }

    //-----------------------------------------------------------------------
    void CD3D9Texture::Update(const CRectangle& Rect)
    {
        Assert(CRectangle(0, 0, m_Size.x, m_Size.y).Intersects(Rect) == INT_IN);

        // The format is the same --> simple copy
        if (m_Format == m_Data.GetFormat())
        {
            // Lock the texture
            D3DLOCKED_RECT LockedRect;
            RECT Lock = {Rect.Left(), Rect.Top(), Rect.Right(), Rect.Bottom()};
            m_pTexture->LockRect(0, &LockedRect, &Lock, 0);

            // Copy pixels
            UpdateSurface(LockedRect, Rect);

            // Unlock the texture
            m_pTexture->UnlockRect(0);
        }
        else
        {
            // Get the device
            LPDIRECT3DDEVICE9 Device = NULL;
            m_pTexture->GetDevice(&Device);

            // Create a texture in memory
            LPDIRECT3DSURFACE9 Src = NULL;
            if (FAILED(Device->CreateOffscreenPlainSurface(Rect.Width(), Rect.Height(), CD3D9Enum::Get(m_Data.GetFormat()), D3DPOOL_SYSTEMMEM, &Src, NULL)))
                D3D9_EXCEPT("Error in fonction CreateOffscreenPlainSurface", "CD3D9Texture::Update");

            // Lock the temp texture
            D3DLOCKED_RECT LockedRect;
            Src->LockRect(&LockedRect, NULL, 0);

            // Copy pixels
            UpdateSurface(LockedRect, Rect);

            // Unlock the temp texture
            Src->UnlockRect();

            // Get the level 0 surface of the texture
            LPDIRECT3DSURFACE9 Dest = NULL;
            m_pTexture->GetSurfaceLevel(0, &Dest);

            // Copy Src surface on Dest Surface (with format conversion)
            RECT DestRect = {Rect.Left(), Rect.Top(), Rect.Right(), Rect.Bottom()};
            if (FAILED(D3DXLoadSurfaceFromSurface(Dest, NULL, &DestRect, Src, NULL, NULL, D3DX_DEFAULT, 0)))
                D3D9_EXCEPT("Error in fonction D3DXLoadSurfaceFromSurface", "CD3D9Texture::Update");
        }

        // Mipmapping levels generation
        if (m_bHasMipmaps)
        {
            if (m_bAutoMipmaps)
                m_pTexture->GenerateMipSubLevels();
            else
                D3DXFilterTexture(m_pTexture, NULL, D3DX_DEFAULT, D3DX_DEFAULT);
        }
    }

    void CD3D9Texture::UpdateSurface(const D3DLOCKED_RECT& LockedRect, const CRectangle& Rect)
    {
        // Get A pointer on source and destination pixels
        unsigned char* DestPix = reinterpret_cast<unsigned char*>(LockedRect.pBits);
        const unsigned char* SrcPix = m_Data.GetData() + (Rect.Left() + Rect.Top() * m_Size.x) * GetBytesPerPixel(m_Data.GetFormat());

        // Copy pixels on the surface
        unsigned int Bpp = GetBytesPerPixel(m_Data.GetFormat());
        for (int i = 0; i < Rect.Height(); ++i)
        {
            std::copy(SrcPix, SrcPix + Rect.Width() * Bpp, DestPix);
            SrcPix  += m_Size.x * Bpp;
            DestPix += LockedRect.Pitch;
        }
    }
} // namespace Keos
