// PlaceBlobbyProc.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                 );
}
  
typedef struct {
    RtFloat *coords;
    RtInt   num_blobs;
    RtInt    total_num_codes; 
    RtInt    *codes;
    RtInt    num_matrix_values; // 16 x num_blobs
    RtFloat *matrix;
    } BlobbyData;
    
// Declare our utility functions...    
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);
  
    // Allocate a block of memory to store one instance of BlobbyData.
    BlobbyData *blobbyDataPtr = (BlobbyData*)malloc(sizeof(BlobbyData));
    // An error has occurred...
    if(len == 0) {
        blobbyDataPtr->num_blobs = 0;
        return (RtPointer)blobbyDataPtr;
        }
            
    // We could directly create a copy of the input paramStr as an array and
    // use strcpy() to copy the string. For example,
    //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);
  
    // Irrespective of how many values are specified by the paramStr we
    // know the first two values provide the scale of the blobs
    // and the number of coordinates that define their 3D locations.
    double  scale;
    RtInt     num_coords;
    sscanf(copyStr, "%lf %d", &scale, &num_coords);
    
    blobbyDataPtr->num_blobs = num_coords/3;
    RtInt num_ellipsoid_codes = blobbyDataPtr->num_blobs * 2;
    RtInt num_blending_codes = 2 + blobbyDataPtr->num_blobs;
    blobbyDataPtr->total_num_codes = num_ellipsoid_codes + num_blending_codes;
        
    // Allocate a block of memory for the array of coordinates.
    blobbyDataPtr->coords = (RtFloat*)calloc(num_coords, sizeof(RtFloat));
        
    // Tokenize the input string but ignore the first two values because
    // they were read by the ssanf() funtion.
    char *strPtr = strtok(copyStr, " ");
    strtok(NULL, " "); // eat the scale value
    strtok(NULL, " "); // eat the num_coords value
    RtInt     count = 0;
    while(strPtr) {
        blobbyDataPtr->coords[count++] = strtod(strPtr, NULL);
        strPtr = strtok(NULL, " ");
        }
    
    // Allocate the memory for the array of codes.
    blobbyDataPtr->codes = (RtInt*)calloc(blobbyDataPtr->total_num_codes, sizeof(RtInt));
    
    // Begin assigning the codes_______________________________________________
    RtInt n = 0, matrix_index_counter = 0;
    while(n < num_ellipsoid_codes) {
        blobbyDataPtr->codes[n] = 1001;
        blobbyDataPtr->codes[n+1] = matrix_index_counter;
        matrix_index_counter += 16;
        n += 2;
        }
    blobbyDataPtr->codes[n++] = 0;    // code to additively blend the blobs
    blobbyDataPtr->codes[n++] = blobbyDataPtr->num_blobs;    
    RtInt     blob_index = 0;
    while(n < blobbyDataPtr->total_num_codes)
        blobbyDataPtr->codes[n++] = blob_index++;
    // End assigning the codes________________________________________________
        
    // Allocate the memory for an array of matrix elements.        
    blobbyDataPtr->num_matrix_values = blobbyDataPtr->num_blobs * 16;
    blobbyDataPtr->matrix = (RtFloat*)calloc(blobbyDataPtr->num_matrix_values, sizeof(RtFloat));
    
    // Assign the matrix values_______________________________________________    
    RtInt     coord_counter = 0;
    for(int n = 0; n < blobbyDataPtr->num_matrix_values; n += 16) {
        blobbyDataPtr->matrix[n] = blobbyDataPtr->matrix[n+5] = blobbyDataPtr->matrix[n+10] = scale;
        blobbyDataPtr->matrix[n+12] = blobbyDataPtr->coords[coord_counter++];
        blobbyDataPtr->matrix[n+13] = blobbyDataPtr->coords[coord_counter++];
        blobbyDataPtr->matrix[n+14] = blobbyDataPtr->coords[coord_counter++];
        blobbyDataPtr->matrix[n+15] = 1.0;
        }
    free(copyStr);
    return (RtPointer)blobbyDataPtr;
    }
  
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Subdivide(RtPointer data, RtFloat detail) {
    if(((BlobbyData*)data)->num_blobs == 0)
        return;
    RtToken token[1] = { "\0" };
    RiBlobby(((BlobbyData*)data)->num_blobs, 
             ((BlobbyData*)data)->total_num_codes, 
             ((BlobbyData*)data)->codes, 
             ((BlobbyData*)data)->num_matrix_values, 
             ((BlobbyData*)data)->matrix, 1, token, RI_NULL );
    }
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Free(RtPointer data) {
    if( ((BlobbyData*)data)->num_blobs > 0) {
        free(((BlobbyData*)data)->codes);
        free(((BlobbyData*)data)->matrix);
        free(((BlobbyData*)data)->coords);
        }
    free(data);
    }
    
// ----------------------------------------------------
// Our utility functions begin here 
// ----------------------------------------------------
RtFloat randBetween(RtFloat min, RtFloat max) {
    return ((RtFloat)rand()/RAND_MAX) * (max - min) + min;
    }