// PlaceSpheresProc.cpp
  
#include <ri.h>
#include <RixInterfaces.h>
#include <RiTypesHelper.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
  
// A RiProcedural must implement these functions. This is a fixed requirement.
extern "C"
{
    PRMANEXPORT RtPointer ConvertParameters ( RtString paramStr              );
    PRMANEXPORT RtVoid    Subdivide         ( RtPointer data, RtFloat detail );
    PRMANEXPORT RtVoid    Free              ( RtPointer data                 );
}
  
// A custom data structure for defining an arbitrary number of spheres
// positioned at locations defined by the "coords" array.
typedef struct {
    RtFloat radius;
    RtFloat *coords;        // an array of x,y,z coordinates
    RtInt    num_coords;
    } SpheresData;
    
RtFloat randBetween(RtFloat min, RtFloat max);
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
//
RtPointer ConvertParameters(RtString paramStr) {
    // The strtok() function cannot be used on the paramStr directly because
    // it modifies the string. 
    long len = strlen(paramStr);
    
    // We could directly create a copy of the input paramStr as an array and
    // use the strcpy(), string copy, function.
    //char copyStr[len];
    //strcpy(copyStr, paramStr);
    
    // However, because the paramStr can be very large we allocate memory
    // from the main memory pool (the "heap") and then perform a block 
    // copy of the contents of paramStr.
    char *copyStr = (char*)calloc(len + 1, sizeof(char));
    memcpy(copyStr, paramStr, len + 1);
    
    // Allocate a block of memory to store one instance of SpheresData.
    SpheresData *dataPtr = (SpheresData*)calloc(1, sizeof(SpheresData));
    
    // Irrespective of how many values are specified by the paramStr we
    // know the first two values will specify the radius of the spheres
    // and the number of coordinates that define their 3D locations.
    sscanf(copyStr, "%f %d", &dataPtr->radius, &dataPtr->num_coords);
    
    // Allocate memory to store an array of coordinates
    dataPtr->coords = (RtFloat*)malloc(sizeof(RtFloat) * dataPtr->num_coords);
    
    char *strPtr = strtok(copyStr, " ");
    strPtr = strtok(NULL, " "); // eat the radius value
    strPtr = strtok(NULL, " "); // eat the num vertices value
    long count = 0;
    while(strPtr) {
        // Convert each string to a double precision floating point number
        dataPtr->coords[count] = strtod(strPtr, NULL);
        count++;
        strPtr = strtok(NULL, " ");
        }
    // Don't forget to free the allocated memory.
    free(copyStr);
    return (RtPointer)dataPtr;
}
  
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Subdivide(RtPointer data, RtFloat detail) {
    RtFloat radius = ((SpheresData*)data)->radius;
    RtInt     num_coords = ((SpheresData*)data)->num_coords;
    RtFloat *coords =  ((SpheresData*)data)->coords;
  
    for(int n = 0; n < num_coords; n = n + 3) {
        RtFloat x = coords[n];
        RtFloat y = coords[n + 1];
        RtFloat z = coords[n + 2];
        RiTransformBegin();
            RiTranslate(x,y,z);
            // To assign a color to each sphere un-comment the next two lines
            // and comment line 73
            RtColor cs[1] = { {randBetween(0,1),randBetween(0,1),randBetween(0,1) } } ;
            RiSphere(radius, -radius, radius, 360, "constant color Cs", (RtPointer)cs, RI_NULL);
            //RiSphere(radius, -radius, radius, 360, RI_NULL);
        RiTransformEnd();
        }
    }
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Free(RtPointer data) {
    free(((SpheresData*)data)->coords);
    free(data);
    }
    
// ----------------------------------------------------
// Our utility functions begin here 
// ----------------------------------------------------
RtFloat randBetween(RtFloat min, RtFloat max) {
    return ((RtFloat)rand()/RAND_MAX) * (max - min) + min;
    }