#ifndef _RRSHADINGCONTEXT_HPP
#define _RRSHADINGCONTEXT_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Reference Renderer
 * -----------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Shading context
 *//*--------------------------------------------------------------------*/

#include "rrDefs.hpp"
#include "rrGenericVector.hpp"
#include "rrFragmentPacket.hpp"

namespace rr
{

/*--------------------------------------------------------------------*//*!
 * \brief Fragment shading context
 *
 * Contains per-primitive information used in fragment shading
 *//*--------------------------------------------------------------------*/
struct FragmentShadingContext
{
    FragmentShadingContext(const GenericVec4 *varying0, const GenericVec4 *varying1, const GenericVec4 *varying2,
                           GenericVec4 *outputArray, GenericVec4 *outputArraySrc1, float *fragmentDepths,
                           int primitiveID, int numFragmentOutputs, int numSamples, FaceType visibleFace_);

    const GenericVec4 *varyings[3];     //!< Vertex shader outputs. Pointer will be NULL if there is no such vertex.
    GenericVec4 *const outputArray;     //!< Fragment output array
    GenericVec4 *const outputArraySrc1; //!< Fragment output array for source 1.
    const int primitiveID;              //!< Geometry shader output
    const int numFragmentOutputs;       //!< Fragment output count
    const int numSamples;               //!< Number of samples
    float *
        fragmentDepths; //!< Fragment packet depths. Pointer will be NULL if there is no depth buffer. Each sample has per-sample depth values
    FaceType visibleFace; //!< Which face (front or back) is visible
};

// Write output

template <typename T>
void writeFragmentOutput(const FragmentShadingContext &context, int packetNdx, int fragNdx, int outputNdx,
                         const T &value)
{
    DE_ASSERT(packetNdx >= 0);
    DE_ASSERT(fragNdx >= 0 && fragNdx < 4);
    DE_ASSERT(outputNdx >= 0 && outputNdx < context.numFragmentOutputs);

    context.outputArray[outputNdx + context.numFragmentOutputs * (fragNdx + packetNdx * 4)] = value;
}

template <typename T>
void writeFragmentOutputDualSource(const FragmentShadingContext &context, int packetNdx, int fragNdx, int outputNdx,
                                   const T &value, const T &value1)
{
    DE_ASSERT(packetNdx >= 0);
    DE_ASSERT(fragNdx >= 0 && fragNdx < 4);
    DE_ASSERT(outputNdx >= 0 && outputNdx < context.numFragmentOutputs);

    context.outputArray[outputNdx + context.numFragmentOutputs * (fragNdx + packetNdx * 4)]     = value;
    context.outputArraySrc1[outputNdx + context.numFragmentOutputs * (fragNdx + packetNdx * 4)] = value1;
}

// Read Varying

template <typename T>
tcu::Vector<T, 4> readPointVarying(const FragmentPacket &packet, const FragmentShadingContext &context, int varyingLoc,
                                   int fragNdx)
{
    DE_UNREF(fragNdx);
    DE_UNREF(packet);

    return context.varyings[0][varyingLoc].get<T>();
}

template <typename T>
tcu::Vector<T, 4> readLineVarying(const FragmentPacket &packet, const FragmentShadingContext &context, int varyingLoc,
                                  int fragNdx)
{
    return packet.barycentric[0][fragNdx] * context.varyings[0][varyingLoc].get<T>() +
           packet.barycentric[1][fragNdx] * context.varyings[1][varyingLoc].get<T>();
}

template <typename T>
tcu::Vector<T, 4> readTriangleVarying(const FragmentPacket &packet, const FragmentShadingContext &context,
                                      int varyingLoc, int fragNdx)
{
    return packet.barycentric[0][fragNdx] * context.varyings[0][varyingLoc].get<T>() +
           packet.barycentric[1][fragNdx] * context.varyings[1][varyingLoc].get<T>() +
           packet.barycentric[2][fragNdx] * context.varyings[2][varyingLoc].get<T>();
}

template <typename T>
tcu::Vector<T, 4> readVarying(const FragmentPacket &packet, const FragmentShadingContext &context, int varyingLoc,
                              int fragNdx)
{
    if (context.varyings[1] == DE_NULL)
        return readPointVarying<T>(packet, context, varyingLoc, fragNdx);
    if (context.varyings[2] == DE_NULL)
        return readLineVarying<T>(packet, context, varyingLoc, fragNdx);
    return readTriangleVarying<T>(packet, context, varyingLoc, fragNdx);
}

// Derivative

template <typename T, int Size>
void dFdxLocal(tcu::Vector<T, Size> outFragmentdFdx[4], const tcu::Vector<T, Size> func[4])
{
    const tcu::Vector<T, Size> dFdx[2] = {func[1] - func[0], func[3] - func[2]};

    outFragmentdFdx[0] = dFdx[0];
    outFragmentdFdx[1] = dFdx[0];
    outFragmentdFdx[2] = dFdx[1];
    outFragmentdFdx[3] = dFdx[1];
}

template <typename T, int Size>
void dFdyLocal(tcu::Vector<T, Size> outFragmentdFdy[4], const tcu::Vector<T, Size> func[4])
{
    const tcu::Vector<T, Size> dFdy[2] = {func[2] - func[0], func[3] - func[1]};

    outFragmentdFdy[0] = dFdy[0];
    outFragmentdFdy[1] = dFdy[1];
    outFragmentdFdy[2] = dFdy[0];
    outFragmentdFdy[3] = dFdy[1];
}

template <typename T>
inline void dFdxVarying(tcu::Vector<T, 4> outFragmentdFdx[4], const FragmentPacket &packet,
                        const FragmentShadingContext &context, int varyingLoc)
{
    const tcu::Vector<T, 4> func[4] = {
        readVarying<T>(packet, context, varyingLoc, 0),
        readVarying<T>(packet, context, varyingLoc, 1),
        readVarying<T>(packet, context, varyingLoc, 2),
        readVarying<T>(packet, context, varyingLoc, 3),
    };

    dFdxLocal(outFragmentdFdx, func);
}

template <typename T>
inline void dFdyVarying(tcu::Vector<T, 4> outFragmentdFdy[4], const FragmentPacket &packet,
                        const FragmentShadingContext &context, int varyingLoc)
{
    const tcu::Vector<T, 4> func[4] = {
        readVarying<T>(packet, context, varyingLoc, 0),
        readVarying<T>(packet, context, varyingLoc, 1),
        readVarying<T>(packet, context, varyingLoc, 2),
        readVarying<T>(packet, context, varyingLoc, 3),
    };

    dFdyLocal(outFragmentdFdy, func);
}

// Fragent depth

inline float readFragmentDepth(const FragmentShadingContext &context, int packetNdx, int fragNdx, int sampleNdx)
{
    // Reading or writing to fragment depth values while there is no depth buffer is legal but not supported by rr
    DE_ASSERT(context.fragmentDepths);
    return context.fragmentDepths[(packetNdx * 4 + fragNdx) * context.numSamples + sampleNdx];
}

inline void writeFragmentDepth(const FragmentShadingContext &context, int packetNdx, int fragNdx, int sampleNdx,
                               float depthValue)
{
    // Reading or writing to fragment depth values while there is no depth buffer is legal but not supported by rr
    DE_ASSERT(context.fragmentDepths);
    context.fragmentDepths[(packetNdx * 4 + fragNdx) * context.numSamples + sampleNdx] = depthValue;
}

} // namespace rr

#endif // _RRSHADINGCONTEXT_HPP
