#include "ProceduralTextureSystem.hpp"
#include "../components/ProceduralTexture.hpp"
#include <OgreTextureManager.h>
#include <OgreImage.h>
#include <OgreLogManager.h>
#include <ProceduralTextureGenerator.h>
#include <ProceduralTextureBuffer.h>

ProceduralTextureSystem::ProceduralTextureSystem(flecs::world& world, Ogre::SceneManager* sceneMgr)
    : m_world(world)
    , m_sceneMgr(sceneMgr)
    , m_query(world.query<ProceduralTextureComponent>())
{
}

ProceduralTextureSystem::~ProceduralTextureSystem() = default;

void ProceduralTextureSystem::initialize()
{
    if (m_initialized) return;
    m_initialized = true;
    
    Ogre::LogManager::getSingleton().logMessage("ProceduralTextureSystem initialized");
}

void ProceduralTextureSystem::update()
{
    if (!m_initialized) return;

    m_query.each([&](flecs::entity entity, ProceduralTextureComponent& component) {
        if (component.dirty || !component.generated) {
            generateTexture(entity, component);
        }
    });
}

void ProceduralTextureSystem::generateTexture(flecs::entity entity, ProceduralTextureComponent& component)
{
    try {
        // Ensure persistent ID is assigned.
        if (component.textureId.empty()) {
            component.textureId = generatePersistentId();
        }

        // Generate unique texture name if not set
        if (component.textureName.empty()) {
            component.textureName = "ProceduralTex_" + component.textureId;
        }
        
        const int gridSize = ProceduralTextureComponent::GRID_SIZE;
        const int rectCount = ProceduralTextureComponent::RECT_COUNT;
        const int texSize = component.textureSize;
        const int rectSize = texSize / gridSize;
        
        // Create a buffer for the final texture
        Ogre::Image finalImage;
        finalImage.loadDynamicImage(
            new Ogre::uchar[texSize * texSize * 4],  // Allocate buffer
            texSize, texSize, 1, 
            Ogre::PF_R8G8B8A8, true  // Auto-delete buffer
        );
        
        // Clear to transparent
        std::memset(finalImage.getData(), 0, texSize * texSize * 4);
        
        // Generate each rectangle using OgreProcedural::Solid and composite into final image
        for (int i = 0; i < rectCount; ++i) {
            int row = i / gridSize;
            int col = i % gridSize;
            
            // Create a small texture buffer for this rectangle
            Procedural::TextureBufferPtr rectBuffer(new Procedural::TextureBuffer(rectSize));
            
            // Get color for this rectangle
            Ogre::ColourValue color = component.getColor(i);
            
            // Generate solid color using OgreProcedural
            Procedural::Solid(rectBuffer).setColour(color).process();
            
            // Get the image data from the buffer
            Ogre::Image& rectImage = rectBuffer->getImage();
            
            // Copy rectangle into final image
            int destX = col * rectSize;
            int destY = row * rectSize;
            
            for (int y = 0; y < rectSize; ++y) {
                for (int x = 0; x < rectSize; ++x) {
                    Ogre::ColourValue pixel = rectImage.getColourAt(x, y, 0);
                    finalImage.setColourAt(pixel, destX + x, destY + y, 0);
                }
            }
        }
        
        // Destroy old texture if exists
        if (component.ogreTexture) {
            Ogre::TextureManager::getSingleton().remove(component.ogreTexture);
            component.ogreTexture.reset();
        }
        
        // Create Ogre texture from image
        component.ogreTexture = Ogre::TextureManager::getSingleton().loadImage(
            component.textureName,
            Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
            finalImage,
            Ogre::TEX_TYPE_2D,
            0,  // numMipmaps
            1.0f  // gamma
        );
        
        component.generated = true;
        component.dirty = false;
        component.version++;
        
        Ogre::LogManager::getSingleton().logMessage(
            "ProceduralTexture: Generated '" + component.textureName + 
            "' (" + std::to_string(texSize) + "x" + std::to_string(texSize) + ")");
            
    } catch (const Ogre::Exception& e) {
        Ogre::LogManager::getSingleton().logMessage(
            "ProceduralTexture ERROR: " + e.getDescription());
        component.dirty = true;  // Will retry
    }
}

void ProceduralTextureSystem::regenerateTexture(flecs::entity entity)
{
    if (!entity.is_alive() || !entity.has<ProceduralTextureComponent>()) {
        return;
    }
    
    entity.get_mut<ProceduralTextureComponent>().markDirty();
}

std::string ProceduralTextureSystem::getTextureName(flecs::entity entity)
{
    if (!entity.is_alive() || !entity.has<ProceduralTextureComponent>()) {
        return "";
    }
    
    return entity.get<ProceduralTextureComponent>().textureName;
}
