/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 * For complete copyright and license terms please see the LICENSE at the root of this distribution.
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 *
 */


#ifndef CRYINCLUDE_EDITOR_LIGHTMAPCOMPILER_SIMPLETRIANGLERASTERIZER_H
#define CRYINCLUDE_EDITOR_LIGHTMAPCOMPILER_SIMPLETRIANGLERASTERIZER_H
#pragma once

class CSimpleTriangleRasterizer
{
public:

    class IRasterizeSink
    {
    public:

        //! is called once per triangel for the first possible visible line
        //! /param iniStartY
        virtual void Triangle([[maybe_unused]] const int iniStartY)
        {
        }

        //! callback function
        //! /param infXLeft included - not clipped against left and reight border
        //! /param infXRight excluded - not clipped against left and reight border
        //! /param iniXLeft included
        //! /param iniXRight excluded
        //! /param iniY
        virtual void Line(const float infXLeft, const float infXRight,
            const int iniXLeft, const int iniXRight, const int iniY) = 0;
    };

    typedef unsigned long DWORD;

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

    //! implementation sink sample
    class CDWORDFlatFill
        : public IRasterizeSink
    {
    public:

        //! constructor
        CDWORDFlatFill(DWORD* inpBuffer, const DWORD indwPitchInPixels, DWORD indwValue)
        {
            m_dwValue = indwValue;
            m_pBuffer = inpBuffer;
            m_dwPitchInPixels = indwPitchInPixels;
        }

        virtual void Triangle(const int iniY)
        {
            m_pBufferLine = &m_pBuffer[iniY * m_dwPitchInPixels];
        }

        virtual void Line([[maybe_unused]] const float infXLeft, [[maybe_unused]] const float infXRight,
            const int iniLeft, const int iniRight, [[maybe_unused]] const int iniY)
        {
            DWORD* mem = &m_pBufferLine[iniLeft];

            for (int x = iniLeft; x < iniRight; x++)
            {
                *mem++ = m_dwValue;
            }

            m_pBufferLine += m_dwPitchInPixels;
        }

    private:
        DWORD m_dwValue;                    //!< fill value
        DWORD* m_pBufferLine;           //!< to get rid of the multiplication per line

        DWORD m_dwPitchInPixels;    //!< in DWORDS, not in Bytes
        DWORD* m_pBuffer;                   //!< pointer to the buffer
    };

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

    //! constructor
    //! /param iniWidth excluded
    //! /param iniHeight excluded
    CSimpleTriangleRasterizer(const int iniWidth, const int iniHeight)
    {
        m_iMinX = 0;
        m_iMinY = 0;
        m_iMaxX = iniWidth - 1;
        m_iMaxY = iniHeight - 1;
    }
    /*
        //! constructor
        //! /param iniMinX included
        //! /param iniMinY included
        //! /param iniMaxX included
        //! /param iniMaxY included
        CSimpleTriangleRasterizer( const int iniMinX, const int iniMinY, const int iniMaxX, const int iniMaxY )
        {
            m_iMinX=iniMinX;
            m_iMinY=iniMinY;
            m_iMaxX=iniMaxX;
            m_iMaxY=iniMaxY;
        }
    */
    //! simple triangle filler with clipping (optimizable), not subpixel correct
    //! /param pBuffer pointer o the color buffer
    //! /param indwWidth width of the color buffer
    //! /param indwHeight height of the color buffer
    //! /param x array of the x coordiantes of the three vertices
    //! /param y array of the x coordiantes of the three vertices
    //! /param indwValue value of the triangle
    void DWORDFlatFill(DWORD* inpBuffer, const DWORD indwPitchInPixels, float x[3], float y[3], DWORD indwValue, bool inbConservative)
    {
        CDWORDFlatFill pix(inpBuffer, indwPitchInPixels, indwValue);

        if (inbConservative)
        {
            CallbackFillConservative(x, y, &pix);
        }
        else
        {
            CallbackFillSubpixelCorrect(x, y, &pix);
        }
    }

    // Rectangle around triangle - more stable - use for debugging purpose
    void CallbackFillRectConservative(float x[3], float y[3], IRasterizeSink * inpSink);


    //! subpixel correct triangle filler (conservative or not conservative)
    //! \param pBuffer pointe to the DWORD
    //! \param indwWidth width of the buffer pBuffer pointes to
    //! \param indwHeight height of the buffer pBuffer pointes to
    //! \param x array of the x coordiantes of the three vertices
    //! \param y array of the x coordiantes of the three vertices
    //! \param inpSink pointer to the sink interface (is called per triangle and per triangle line)
    void CallbackFillConservative(float x[3], float y[3], IRasterizeSink * inpSink);

    //! subpixel correct triangle filler (conservative or not conservative)
    //! \param pBuffer pointe to the DWORD
    //! \param indwWidth width of the buffer pBuffer pointes to
    //! \param indwHeight height of the buffer pBuffer pointes to
    //! \param x array of the x coordiantes of the three vertices
    //! \param y array of the x coordiantes of the three vertices
    //! \param inpSink pointer to the sink interface (is called per triangle and per triangle line)
    void CallbackFillSubpixelCorrect(float x[3], float y[3], IRasterizeSink * inpSink);

    //!
    //! /param inoutfX
    //! /param inoutfY
    //! /param infAmount could be positive or negative
    static void ShrinkTriangle(float inoutfX[3], float inoutfY[3], float infAmount);

private:

    // Clipping Rect;

    int     m_iMinX;        //!< minimum x value included
    int     m_iMinY;        //!< minimum y value included
    int     m_iMaxX;        //!< maximum x value included
    int     m_iMaxY;        //!< maximum x value included

    void lambertHorizlineConservative(float fx1, float fx2, int y, IRasterizeSink* inpSink);
    void lambertHorizlineSubpixelCorrect(float fx1, float fx2, int y, IRasterizeSink* inpSink);
    void CopyAndSortY(const float infX[3], const float infY[3], float outfX[3], float outfY[3]);
};


// extension ideas:
// * callback with coverage mask (possible non ordered sampling)
// * z-buffer behaviour
// * gouraud shading
// * texture mapping with nearest/bicubic/bilinear filter
// * further primitives: thick line, ellipse
// * build a template version
// *

#endif // CRYINCLUDE_EDITOR_LIGHTMAPCOMPILER_SIMPLETRIANGLERASTERIZER_H
