#include <RixPattern.h> 
#include <RixShadingUtils.h>
#include <RixShading.h>
//#include <RixInterfaces.h>
#include <cstring>
  
class CutrFaceColor : public RixPattern {
public:
  
    CutrFaceColor();
    virtual ~CutrFaceColor() { }
    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
        RtColorRGB    m_xcolor;
        RtColorRGB    m_ycolor;
        RtColorRGB    m_zcolor;
        RtColorRGB    m_base_color;
        RtFloat        m_base_amount;
    };
  
CutrFaceColor::CutrFaceColor():
    m_msg(NULL),
    m_shd(NULL),
    m_xcolor(1,0,0),
    m_ycolor(0,1,0),
    m_zcolor(0,0,1),
    m_base_color(1,1,1),
    m_base_amount(1)
    { }
  
int CutrFaceColor::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 *CutrFaceColor::GetParamTable() {
    static RixSCParamInfo s_ptable[] = {
        // Output
        RixSCParamInfo("resultRGB", k_RixSCColor, k_RixSCOutput),
        // Inputs
        RixSCParamInfo("xcolor", k_RixSCColor),
        RixSCParamInfo("ycolor", k_RixSCColor),
        RixSCParamInfo("zcolor", k_RixSCColor),
        RixSCParamInfo("coord_name", k_RixSCString),
        RixSCParamInfo("base_color", k_RixSCColor),
        RixSCParamInfo("base_amount", k_RixSCFloat),
        RixSCParamInfo() // end of table
        };
    return &s_ptable[0];
    }
  
enum paramIndex {
    k_resultRGB = 0,
    k_xcolor,
    k_ycolor,
    k_zcolor,
    k_coord_name,
    k_base_color,
    k_base_amount
    };
    
int CutrFaceColor::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().
    RtColorRGB    const *xcolor;
    RtColorRGB    const *ycolor;
    RtColorRGB    const *zcolor;
    RtConstString     *coord_namePtr;
    RtColorRGB    const *base_color;
    RtFloat        const *base_amount;
    ctx->EvalParam(k_xcolor, -1, &xcolor, &m_xcolor, varying);
    ctx->EvalParam(k_ycolor, -1, &ycolor, &m_ycolor, varying);
    ctx->EvalParam(k_zcolor, -1, &zcolor, &m_zcolor, varying);
    ctx->EvalParam(k_coord_name, -1, &coord_namePtr);
    ctx->EvalParam(k_base_color, -1, &base_color, &m_base_color, varying);
    ctx->EvalParam(k_base_amount, -1, &base_amount, &m_base_amount, varying);
  
    // Assign an array of normalized shading normals to Nn.
    // Copy the original Nn data to tformNn. Transform will 
    // over-write the original tNn data.
    RtNormal3 const  *Nn;
    ctx->GetBuiltinVar(RixShadingContext::k_Nn, &Nn);
    RtNormal3 *tNn = pool.AllocForPattern<RtNormal3>(ctx->numPts);
    memcpy(tNn, Nn, sizeof(RtNormal3) * ctx->numPts);
  
    if(strlen(*coord_namePtr) == 0)
        ctx->Transform(RixShadingContext::k_AsNormals, "current", "object", tNn, NULL);
    else
        ctx->Transform(RixShadingContext::k_AsNormals, "current", *coord_namePtr, tNn, NULL);
  
    RtVector3 vecx = RtVector3(1.0, 0, 0);
    RtVector3 vecy = RtVector3(0, 1.0, 0);
    RtVector3 vecz = RtVector3(0, 0, 1.0);
    RtColorRGB white = RtColorRGB(1.0, 1.0, 1.0);
    RtFloat dot;
    // Assign values to the output(s).
    for(int i = 0; i < ctx->numPts; i++) {
        // Ensure the length of the normal does not exceed 1.0.
        tNn[i].Normalize(); 
        
        dot = fabs(tNn[i].Dot(vecx));
        resultRGB[i] = RixMix(white, xcolor[i], dot);
    
        dot = fabs(tNn[i].Dot(vecy));
        resultRGB[i] = RixMix(resultRGB[i], ycolor[i], dot);
  
        dot = fabs(tNn[i].Dot(vecz));
        resultRGB[i] = RixMix(resultRGB[i], zcolor[i], dot);
        
        // Finally, tint the result with the base color
        RtColorRGB rgb = RixMix(white, base_color[i], base_amount[i]);
        resultRGB[i] *= rgb;
        //resultRGB[i] = RixMix(resultRGB[i], base_color[i], base_amount[i]);
        }
    return 0;
    }
RIX_PATTERNCREATE {
    return new CutrFaceColor();
    }
RIX_PATTERNDESTROY {
    delete((CutrFaceColor*)pattern);
    }