/*
    Intended for use with Mach Kobayashi's OmnidirectionalStereo Projection
    plugin. For details about how to use his plugin with this Rif refer to:
        www.fundza.com/devkit/rifplugins/omni_stereo/index.html
    
    May 16 2016
    Malcolm Kesson
*/
#include <RifPlugin.h>
#include <RifFilter.h>
#include <RixInterfaces.h>
#include <ri.h>
#include <string>
#include <rx.h>
#include <stdlib.h>
#include <stdio.h>
  
#define DEFAULT_STEREO_PLUGIN_NAME "OmnidirectionalStereo"
  
class OmniProjection_Rif : public RifPlugin {
    public:
             OmniProjection_Rif(std::string plugin_path, RtFloat eye_sep);
    virtual ~OmniProjection_Rif() { }
    virtual RifFilter     &GetFilter() { return m_filter; }
    virtual std::string    join(std::string head, std::string tail);
//    static void infoMsg(const char *str);
    
    private:
        RixMessages  *m_msg;
        RifFilter    m_filter;
        std::string  m_plugin_path;
        RtFloat      m_interpupilaryDistance;
        // Callback_____________________________________________________________
        static RtVoid  projection(RtToken name, RtInt, RtToken[], RtPointer[]);
    };

//-----------------------------------------------------
// RifPluginManufacture
//-----------------------------------------------------
// For simplicity we assume that if a path to the Kobayashi plugin
// has been specified it does NOT contain spaces.
RifPlugin *RifPluginManufacture(int argc, char **argv) {
    std::string defaultPath("./");
    if(argc == 0) {
        return new OmniProjection_Rif(defaultPath, -1);
        }
    std::vector <std::string> inputs;
    if(argc == 1) {
        // Tokenize and add to a list (vector) of inputs.
        char *ptr = strtok(*argv, " ");
        while(ptr) {
            inputs.push_back(std::string(ptr));
            ptr = strtok(NULL, " ");
            }
        }
    else // multiple inputs
        for(int n = 0; n < argc; n++) 
            inputs.push_back(std::string(argv[n]));
        
    RtFloat fval = 2.0;
    char     *endPtr = NULL;
    if(inputs.size() == 1) {
        fval = strtod(inputs[0].c_str(), &endPtr);
        // Not a number, therefore, assume its the path to 
        // the Projection plugin.
        if(*endPtr)
            return new OmniProjection_Rif(inputs[0], -1.0);
        else
            return new OmniProjection_Rif(defaultPath, fval);
        }  
    else // the first input MUST be a path followed by float
        {
        fval = strtod(inputs[1].c_str(), &endPtr);
        // Not a float...
        if(*endPtr)
            return new OmniProjection_Rif(inputs[0], -1.0);
        return new OmniProjection_Rif(inputs[0], fval);
        }
    }
  
//-----------------------------------------------------
// OmniProjection_Rif
//----------------------------------------------------- 
// Constructor
OmniProjection_Rif::OmniProjection_Rif(std::string path, RtFloat eye_sep) {
    RixContext    *rixContext = RxGetRixContext();
    m_msg = (RixMessages*) rixContext->GetRixInterface(k_RixMessages);
    
    m_plugin_path = path;
    m_interpupilaryDistance = eye_sep;
    
    m_filter.ProjectionV = projection;
    m_filter.Filtering = RifFilter::k_Continue;
  
    m_plugin_path = join(m_plugin_path, DEFAULT_STEREO_PLUGIN_NAME);
    }
   
//-----------------------------------------------------
// Callback: projection
//-----------------------------------------------------
RtVoid OmniProjection_Rif::projection(RtToken name, RtInt nvals, RtToken tokens[], RtPointer params[]) {
    OmniProjection_Rif *self = static_cast<OmniProjection_Rif*> (RifGetCurrentPlugin());
  
    // Is there any sense in outputting the standard Projection rib statement?
    // RiProjectionV(name, nvals, tokens, params);
  
    // Ensure the Projection plugin uses it's default "interpupilaryDistance". 
    if(self->m_interpupilaryDistance == -1) {
        RiProjectionV( (char*)self->m_plugin_path.c_str(), 0, NULL, NULL);
        return;
        }
    RixContext      *rixContext = RxGetRixContext();
    RixTokenStorage *tok_store = static_cast<RixTokenStorage*>(rixContext->GetRixInterface(k_RixGlobalTokenData));
    RtToken       tok = (char*)tok_store->GetToken("float interpupilaryDistance");
    RtToken     token[] = { tok };
    RtPointer     param[] = { &self->m_interpupilaryDistance };
    RiProjectionV( (char*)self->m_plugin_path.c_str(), 1, token, param);
    }
  
//-----------------------------------------------------
// join file paths
//-----------------------------------------------------
std::string OmniProjection_Rif::join(std::string head, std::string tail) { 
    //std::replace(head.begin(), head.end(), '\\', '/');
    //std::replace(tail.begin(), tail.end(), '\\', '/');
    char lastChar = *head.rbegin();
    return (lastChar != '/') ? head + '/' + tail : head + tail;
    }

/*
void OmniProjection_Rif::infoMsg(const char *str) {
    RixContext    *rixContext = RxGetRixContext();
    RixMessages   *msg = (RixMessages*) rixContext->GetRixInterface(k_RixMessages);
    msg->InfoAlways(str);
    }
*/