#include <RixPattern.h> 
#include <RixShadingUtils.h>
#include <RixShading.h>
#include <cstring>
  
class CutrColorByHeight : public RixPattern {
public:
  
    CutrColorByHeight();
    virtual ~CutrColorByHeight() { }
    virtual int Init(RixContext &, char const *pluginpath);
    virtual RixSCParamInfo const *GetParamTable();
    virtual void Finalize(RixContext &) { }
    virtual int ComputeOutputParams(RixShadingContext const *ctx,
                                    RtInt *noutputs, 
                                    OutputSpec **outputs,
                                    RtConstPointer instanceData,
                                    RixSCParamInfo const *ignored);
    private:
        RixMessages *m_msg;
        RixShadeFunctions *m_shd;  // Shading functions in RixInterfaces.h
        RtFloat        m_upperht;
        RtFloat        m_lowerht;
        RtColorRGB    m_upperRGB;
        RtColorRGB    m_lowerRGB;
        RtInt        m_mode;
    };
  
CutrColorByHeight::CutrColorByHeight():
    m_msg(NULL),
    m_shd(NULL),
    m_upperht(10),
    m_lowerht(0),
    m_upperRGB(0.8,0.2,0.0),
    m_lowerRGB(0.0,0.6,0.0),
    m_mode(1)
    { }
  
int CutrColorByHeight::Init(RixContext &ctx, char const *pluginpath) {
    m_msg = (RixMessages*)ctx.GetRixInterface(k_RixMessages);
    m_shd = (RixShadeFunctions*)ctx.GetRixInterface(k_RixShadeFunctions);
  
    // Uncomment the next three lines if a rib Option will be queried.
    //RixRenderState *rstate = (RixRenderState*)ctx.GetRixInterface(k_RixRenderState);
    //RixRenderState::Type optType;
    //RtInt optNumValues, err;
    // Example of using messaging,
    //    m_msg->Info("%f\n", a_float_value);
    return (!m_msg) ? 1 : 0;
    }
  
RixSCParamInfo const *CutrColorByHeight::GetParamTable() {
    static RixSCParamInfo s_ptable[] = {
        // Output
        RixSCParamInfo("resultRGB", k_RixSCColor, k_RixSCOutput),
        // Inputs
        RixSCParamInfo("upperht", k_RixSCFloat),
        RixSCParamInfo("lowerht", k_RixSCFloat),
        RixSCParamInfo("upperRGB", k_RixSCColor),
        RixSCParamInfo("lowerRGB", k_RixSCColor),
        RixSCParamInfo("mode", k_RixSCInteger),
        RixSCParamInfo("attrName", k_RixSCString),
        RixSCParamInfo() // end of table
        };
    return &s_ptable[0];
    }
  
enum paramIndex {
    k_resultRGB = 0,
    k_upperht,
    k_lowerht,
    k_upperRGB,
    k_lowerRGB,
    k_mode,
    k_attrName
    };
    
int CutrColorByHeight::ComputeOutputParams(RixShadingContext const *ctx,
                                RtInt *noutputs, 
                                OutputSpec **outputs,
                                RtConstPointer instanceData,
                                RixSCParamInfo const *ignored) {
  
    // Uncomment the next three lines if a rib Attribute will be queried. Note
    // that Rib Options should be queried in the init() method - not here!
    RixRenderState *rstate = (RixRenderState*)ctx->GetRixInterface(k_RixRenderState);
    RixRenderState::Type attrType;
    RtInt attrNumValues, err;
  
    // OUTPUTS BEGIN____________________________________
    // Allocate memory for the OutputSpec data structure.
    RixShadingContext::Allocator pool(ctx);
    OutputSpec *outSpec = pool.AllocForPattern<OutputSpec>(1);
    *outputs = outSpec;
  
    // Allocate memory for each output.
    RtColorRGB    *resultRGB = pool.AllocForPattern<RtColorRGB>(ctx->numPts);
  
    // Connect the output(s) to the OutputSpec.
    *noutputs = 1;
    outSpec[0].paramId = k_resultRGB;
    outSpec[0].detail = k_RixSCVarying;
    outSpec[0].value = resultRGB;
  
    // INPUTS BEGIN____________________________________
    bool varying = true;
    bool uniform = false;
    // Declare a pointer for each input then obtain their values
    // using EvalParam().
    RtFloat        const *upperht;
    RtFloat        const *lowerht;
    RtColorRGB    const *upperRGB;
    RtColorRGB    const *lowerRGB;
    RtInt        const *modePtr;
    RtConstString     *attrNamePtr;
    ctx->EvalParam(k_upperht, -1, &upperht, &m_upperht, varying);
    ctx->EvalParam(k_lowerht, -1, &lowerht, &m_lowerht, varying);
    ctx->EvalParam(k_upperRGB, -1, &upperRGB, &m_upperRGB, varying);
    ctx->EvalParam(k_lowerRGB, -1, &lowerRGB, &m_lowerRGB, varying);
    ctx->EvalParam(k_mode, -1, &modePtr, &m_mode, uniform);
    ctx->EvalParam(k_attrName, -1, &attrNamePtr);
    RtInt mode = *modePtr;
         
//m_msg->Info("attrNamePtr %s\n", *attrNamePtr);
    RtPoint3 *xform; // an array of points either transformed or not
    RtPoint3 pAttrValue;
     if(mode == 0) {
        RtPoint3 const  *P;
        ctx->GetBuiltinVar(RixShadingContext::k_P, &P);
        xform = pool.AllocForPattern<RtPoint3>(ctx->numPts);
        memcpy(xform, P, sizeof(RtPoint3) * ctx->numPts);
        ctx->Transform(RixShadingContext::k_AsPoints, NULL, "world", xform, NULL);
        }
    else if(mode == 1) {
        xform = pool.AllocForPattern<RtPoint3>(ctx->numPts);
        for(int i = 0; i < ctx->numPts; i++) {
            xform[i].x = xform[i].y = xform[i].z = 0;
            }
        ctx->Transform(RixShadingContext::k_AsPoints, "object", "world", xform, NULL);
        }
    else
        {
        //  Attribute "user" "point position" [-0.847 1.061 0.578] 
        
        err = rstate->GetAttribute(*attrNamePtr, &pAttrValue, sizeof(RtPoint3),
                                    &attrType, &attrNumValues);
        if(err != 0 || attrType != RixRenderState::k_Point)
            pAttrValue = RtPoint3(0,0,0); // default value ?
    //    m_msg->Info("pAttrValue %1.3f\n", pAttrValue[1]);
        }    
    RtFloat   blend;
    for(int i = 0; i < ctx->numPts; i++) {
        if(mode == 0 || mode == 1)
            blend = RixSmoothStep(lowerht[i], upperht[i], xform[i].y);
        else
            blend = RixSmoothStep(lowerht[i], upperht[i], pAttrValue[1]);
            
         resultRGB[i] = RixMix(lowerRGB[i], upperRGB[i], blend);
        }
    return 0;
    }
RIX_PATTERNCREATE {
    return new CutrColorByHeight();
    }
RIX_PATTERNDESTROY {
    delete((CutrColorByHeight*)pattern);
    }