/* osgEarth
 * Copyright 2025 Pelican Mapping
 * MIT License
 */
#include <osgEarth/GeometryRasterizer>
#include <osgEarth/PointSymbol>
#include <osgEarth/LineSymbol>
#include <osgEarth/PolygonSymbol>
#include <osgEarth/AGG.h>

using namespace osgEarth;

#define LC "[GeometryRasterizer] "

// --------------------------------------------------------------------------

namespace
{
    struct AggState : public osg::Referenced
    {
        AggState( osg::Image* image )
            : _rbuf( image->data(), image->s(), image->t(), image->s()*4 ),
              _ren( _rbuf )
        {
            _ras.gamma( 1.3 );
            _ras.filling_rule( agg::fill_even_odd );

            // pre-clear the buffer....
            _ren.clear(agg::rgba8(0,0,0,0));
        }

        agg::rendering_buffer           _rbuf;
        agg::renderer<agg::span_abgr32, agg::rgba8> _ren;
        agg::rasterizer                 _ras;
    };
}

// --------------------------------------------------------------------------

GeometryRasterizer::GeometryRasterizer( int width, int height, const Style& style ) :
_style( style )
{
    _image = new osg::Image();
    _image->allocateImage( width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE );
    _image->setAllocationMode( osg::Image::USE_NEW_DELETE );
    _state = new AggState( _image.get() );
}

GeometryRasterizer::GeometryRasterizer( osg::Image* image, const Style& style ) :
_image( image ),
_style( style )
{
    _state = new AggState( _image.get() );
}

GeometryRasterizer::~GeometryRasterizer()
{
}

osg::Image*
GeometryRasterizer::finalize()
{
    //convert from ABGR to RGBA
    unsigned char* pixel = _image->data();
    for(int i=0; i<_image->s()*_image->t()*4; i+=4, pixel+=4)
    {
        std::swap( pixel[0], pixel[3] );
        std::swap( pixel[1], pixel[2] );
    }
    osg::Image* result = _image.release();
    _image = 0L;
    return result;
}

void
GeometryRasterizer::draw( const Geometry* geom, const osg::Vec4f& c )
{
    if ( !_image.valid() ) return;

    AggState* state = static_cast<AggState*>( _state.get() );

    osg::Vec4f color = c;
    osg::ref_ptr<const Geometry> geomToRender = geom;

    if ( _style.has<PolygonSymbol>() )
    {
        color = _style.get<const PolygonSymbol>()->fill()->color();
    }
    else
    {
        const LineSymbol* ls = _style.getSymbol<const LineSymbol>();
        Distance distance(1.0, Units::METERS);
        if (ls && ls->stroke()->width().isSet())
            distance = ls->stroke()->width()->literal();

        osg::ref_ptr<Geometry> bufferedGeom;
        if ( !geom->buffer( distance.as(Units::METERS) * 0.5, bufferedGeom ) )
        {
            OE_WARN << LC << "Failed to draw line; buffer op not available" << std::endl;
            return;
        }
        geomToRender = bufferedGeom.get();
        if ( ls )
            color = ls->stroke()->color();
    }

    float a = 127+(color.a()*255)/2; // scale alpha up
    agg::rgba8 fgColor = agg::rgba8( (unsigned int)(color.r()*255), (unsigned int)(color.g()*255), (unsigned int)(color.b()*255), (unsigned int)a );

    ConstGeometryIterator gi( geomToRender.get() );
    while( gi.hasMore() )
    {
        const Vec3dVector& g = gi.next()->asVector();

        for( Vec3dVector::const_iterator p = g.begin(); p != g.end(); p++ )
        {
            const osg::Vec3d& p0 = *p;

            if ( p == g.begin() )
                state->_ras.move_to_d( p0.x(), p0.y() );
            else
                state->_ras.line_to_d( p0.x(), p0.y() );
        }
    }
    state->_ras.render( state->_ren, fgColor );
    state->_ras.reset();
}

