/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.

#include "StdAfx.h"
#include "TerrainGrid.h"

#include "Terrain/Heightmap.h"
#include "TerrainTexGen.h"

#include <Terrain/Bus/LegacyTerrainBus.h>

//////////////////////////////////////////////////////////////////////////
CTerrainGrid::CTerrainGrid()
{
    m_numSectors = 0;
    m_resolution = 0;
    m_sectorSize = 0;
    m_sectorResolution = 0;
    m_pixelsPerMeter = 0;
}

//////////////////////////////////////////////////////////////////////////
CTerrainGrid::~CTerrainGrid()
{
    ReleaseSectorGrid();
}

//////////////////////////////////////////////////////////////////////////
void CTerrainGrid::InitSectorGrid(int numSectors)
{
    ReleaseSectorGrid();
    m_numSectors = numSectors;
    m_sectorGrid.resize(m_numSectors * m_numSectors);
    for (int i = 0; i < m_numSectors * m_numSectors; i++)
    {
        m_sectorGrid[i] = 0;
    }

    SSectorInfo si;
    GetIEditor()->GetHeightmap()->GetSectorsInfo(si);
    m_sectorSize = si.sectorSize;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainGrid::ReleaseSectorGrid()
{
    IRenderer* pRenderer = GetIEditor()->GetRenderer();

    for (int i = 0; i < m_numSectors * m_numSectors; i++)
    {
        if (m_sectorGrid[i])
        {
            pRenderer->RemoveTexture(m_sectorGrid[i]->textureId);

            delete m_sectorGrid[i];
        }
    }
    m_sectorGrid.clear();
    m_numSectors = 0;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainGrid::SetResolution(int resolution)
{
    m_resolution = resolution;

    if (abs(m_numSectors) > 0)
    {
        m_sectorResolution = m_resolution / m_numSectors;
    }
    else
    {
        m_sectorResolution = 0;
    }

    if (abs(m_sectorSize) > 0)
    {
        m_pixelsPerMeter = ((float)m_sectorResolution) / m_sectorSize;
    }
    else
    {
        m_pixelsPerMeter = 0;
    }
}

//////////////////////////////////////////////////////////////////////////
CTerrainSector* CTerrainGrid::GetSector(const QPoint& sector)
{
    assert(sector.x() >= 0 && sector.x() < m_numSectors && sector.y() >= 0 && sector.y() < m_numSectors);
    int index = sector.x() + sector.y() * m_numSectors;
    if (!m_sectorGrid[index])
    {
        m_sectorGrid[index] = new CTerrainSector;
    }
    return m_sectorGrid[index];
}

//////////////////////////////////////////////////////////////////////////
QPoint CTerrainGrid::SectorToTexture(const QPoint& sector)
{
    return sector * m_sectorResolution;
}

//////////////////////////////////////////////////////////////////////////
QPoint CTerrainGrid::WorldToSector(const Vec3& wp)
{
    //swap x/y
    return QPoint(int(wp.y / m_sectorSize), int(wp.x / m_sectorSize));
}

//////////////////////////////////////////////////////////////////////////
Vec3 CTerrainGrid::SectorToWorld(const QPoint& sector)
{
    //swap x/y
    return Vec3(sector.y() * m_sectorSize, sector.x() * m_sectorSize, 0);
}

//////////////////////////////////////////////////////////////////////////
Vec2 CTerrainGrid::WorldToTexture(const Vec3& wp)
{
    //swap x/y
    return Vec2(wp.y * m_pixelsPerMeter, wp.x * m_pixelsPerMeter);
}

//////////////////////////////////////////////////////////////////////////
void CTerrainGrid::GetSectorRect(const QPoint& sector, QRect& rect)
{
    rect.setTopLeft(sector * m_sectorResolution);
    rect.setSize(QSize(m_sectorResolution, m_sectorResolution));
}

//////////////////////////////////////////////////////////////////////////
int CTerrainGrid::LockSectorTexture(const QPoint& sector, const uint32 dwTextureResolution, bool& bTextureWasRecreated)
{
    CTerrainSector* st = GetSector(sector);
    assert(st);
    GetIEditor()->GetHeightmap()->AddModSector(sector.x(), sector.y());
    bTextureWasRecreated = false;

    IRenderer* pRenderer = GetIEditor()->GetRenderer();

    // if the texture exists already - make sure the size fits
    {
        ITexture* pTex = pRenderer->EF_GetTextureByID(st->textureId);

        if (pTex)
        {
            if (pTex->GetWidth() != dwTextureResolution || pTex->GetHeight() != dwTextureResolution)
            {
                pRenderer->RemoveTexture(st->textureId);
                pTex = 0;

                LegacyTerrain::LegacyTerrainDataRequestBus::Broadcast(&LegacyTerrain::LegacyTerrainDataRequests::SetTerrainSectorTexture
                    , sector.y(), sector.x(), 0, 0, 0, true);
                st->textureId = 0;
                bTextureWasRecreated = true;
            }
        }
    }

    if (!st->textureId)
    {
        st->textureId = pRenderer->DownLoadToVideoMemory(0, dwTextureResolution, dwTextureResolution,
                eTF_B8G8R8A8, eTF_B8G8R8A8, 0, false, FILTER_LINEAR, 0, 0, FT_USAGE_ALLOWREADSRGB);

    }

    // Regardless of whether or not the texture was just created, tell the terrain system what the texture ID is.
    // It's possible that the runtime terrain has been destroyed / recreated since the first time this texture was
    // created.
    LegacyTerrain::LegacyTerrainDataRequestBus::Broadcast(&LegacyTerrain::LegacyTerrainDataRequests::SetTerrainSectorTexture
        , sector.y(), sector.x(), st->textureId, dwTextureResolution, dwTextureResolution, true);

    return st->textureId;
}

void CTerrainGrid::UnlockSectorTexture(const QPoint& sector)
{
    CTerrainSector* st = GetSector(sector);
    assert(st);

    IRenderer* pRenderer = GetIEditor()->GetRenderer();
    ITexture* pTex = pRenderer->EF_GetTextureByID(st->textureId);

    if (pTex)
    {
        pRenderer->RemoveTexture(st->textureId);
        pTex = 0;

        LegacyTerrain::LegacyTerrainDataRequestBus::Broadcast(&LegacyTerrain::LegacyTerrainDataRequests::SetTerrainSectorTexture
            , sector.y(), sector.x(), 0, 0, 0, true);

        st->textureId = 0;
    }
}


//////////////////////////////////////////////////////////////////////////
void CTerrainGrid::GetMemoryUsage(ICrySizer* pSizer)
{
    pSizer->Add(*this);

    pSizer->Add(m_sectorGrid);

    for (int i = 0; i < m_numSectors * m_numSectors; i++)
    {
        if (m_sectorGrid[i])
        {
            pSizer->Add(*m_sectorGrid[i]);
        }
    }
}