#ifdef WIN32
#pragma warning( disable : 4786 )
#endif
#include <maya/MIOStream.h>
#include <maya/MDagPath.h>
#include <maya/MDoubleArray.h>
#include <maya/MFloatVector.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnLightDataAttribute.h>
#include <maya/MFnMesh.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MString.h>
#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#include "blindDataShader.h"
void mergeSort( int startIndex, int sortingOffset,
                            MIntArray& sortingArray, int* tempSortingArray,
                                MDoubleArray& otherArray, double* tempOtherArray)
{
        if (sortingOffset == 2)
        {
                if (sortingArray[startIndex] > sortingArray[startIndex+1])
                {
                        int temp = sortingArray[startIndex+1];
                        sortingArray[startIndex+1] = sortingArray[startIndex];
                        sortingArray[startIndex] = temp;
                        double ftemp = otherArray[startIndex+1];
                        otherArray[startIndex+1] = otherArray[startIndex];
                        otherArray[startIndex] = ftemp;
                }
        }
        else if (sortingOffset > 2)
        {
                
                
                int leftCount = sortingOffset / 2;
                int rightCount = sortingOffset - leftCount;
                int leftIndex = startIndex;
                int rightIndex = startIndex + leftCount;
                mergeSort(leftIndex, leftCount, sortingArray, tempSortingArray,
                        otherArray, tempOtherArray);
                mergeSort(rightIndex, rightCount, sortingArray, tempSortingArray,
                        otherArray, tempOtherArray);
                
                
                int index = startIndex;
                while (leftIndex < (startIndex + leftCount)
                        && rightIndex < (startIndex + sortingOffset) )
                {
                        if (sortingArray[leftIndex] < sortingArray[rightIndex])
                        {
                                tempSortingArray[index] = sortingArray[leftIndex];
                                tempOtherArray[index++] = otherArray[leftIndex++];
                        }
                        else
                        {
                                tempSortingArray[index] = sortingArray[rightIndex];
                                tempOtherArray[index++] = otherArray[rightIndex++];
                        }
                }
                
                
                while (leftIndex < (startIndex + leftCount) )
                {
                        tempSortingArray[index] = sortingArray[leftIndex];
                        tempOtherArray[index++] = otherArray[leftIndex++];
                }
                while (rightIndex < (startIndex + sortingOffset) )
                {
                        tempSortingArray[index] = sortingArray[rightIndex];
                        tempOtherArray[index++] = otherArray[rightIndex++];
                }
                
                
                for (index = startIndex; index < (startIndex + sortingOffset); ++index)
                {
                        sortingArray[index] = tempSortingArray[index];
                        otherArray[index] = tempOtherArray[index];
                }
        }
}
int binarySearch(int searchValue, MIntArray& searchArray)
{
        int max = searchArray.length();
        int min = 0;
        while (max - min > 1)
        {
                int currentIndex = ((max - min) / 2) + min;
                int currentValue = searchArray[currentIndex];
                if (currentValue == searchValue) return currentIndex;
                else if (currentValue < searchValue) min = currentIndex;
                else max = currentIndex;
        }
        if (searchArray[min] == searchValue) return min;
        else return -1;
}
MTypeId blindDataShader::id( 0x00086000 );
void* blindDataShader::creator()
{
    return new blindDataShader();
}
MStatus blindDataShader::initialize()
{
        return MS::kSuccess;
}
MStatus blindDataShader::compute(
const MPlug&      plug,
      MDataBlock& block ) 
{ 
    bool k = false;
    k |= (plug==outColor);
    k |= (plug==outColorR);
    k |= (plug==outColorG);
    k |= (plug==outColorB);
    if( !k ) return MS::kUnknownParameter;
        
    MFloatVector resultColor(0.0,0.0,0.0);
    
    MDataHandle outColorHandle = block.outputValue( outColor );
    MFloatVector& outColor = outColorHandle.asFloatVector();
    outColor = resultColor;
    outColorHandle.setClean();
    return MS::kSuccess;
}
MStatus blindDataShader::bind(const MDrawRequest& request, M3dView& view)
{
        view.beginGL();
        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
        view.endGL();
        return MS::kSuccess;
}
MStatus blindDataShader::unbind(const MDrawRequest& request,
                           M3dView& view)
{
        view.beginGL(); 
        glPopClientAttrib();
        glPopAttrib();
        view.endGL();
        return MS::kSuccess;
}
MStatus blindDataShader::geometry( const MDrawRequest& request,
                                                                M3dView& view,
                                                            int prim,
                                                                unsigned int writable,
                                                                int indexCount,
                                                                const unsigned int * indexArray,
                                                                int vertexCount,
                                                            const int * vertexIDs,
                                                                const float * vertexArray,
                                                                int normalCount,
                                                                const float ** normalArrays,
                                                                int colorCount,
                                                                const float ** colorArrays,
                                                                int texCoordCount,
                                                                const float ** texCoordArrays)
{
        const int blindDataUniqueID = 60;
        MStatus stat;
        int i;
        
        
        
        double* colours = new double[vertexCount * 3];
        float defaultColour[3] = { 0.0f, 0.0f, 0.0f };
        MPlug plug( thisMObject(), outColor );
        MObject colorObject;
        plug.getValue(colorObject);
        MFnNumericData data(colorObject);
        data.getData(defaultColour[0], defaultColour[1], defaultColour[2]);
        
        
        MFnMesh mesh(request.multiPath().node(), &stat);
        if (stat && mesh.hasBlindData(MFn::kMeshVertComponent, &stat)
                && mesh.hasBlindData(vertexIDs[0], MFn::kMeshVertComponent,
                blindDataUniqueID, &stat))
        {
                MIntArray redComponentIDs, greenComponentIDs, blueComponentIDs;
                MDoubleArray redColour, greenColour, blueColour;
                stat = mesh.getDoubleBlindData( MFn::kMeshVertComponent,
                        blindDataUniqueID, "red", redComponentIDs, redColour);
                if (stat) stat = mesh.getDoubleBlindData( MFn::kMeshVertComponent,
                        blindDataUniqueID, "green", greenComponentIDs, greenColour);
                if (stat) stat = mesh.getDoubleBlindData( MFn::kMeshVertComponent,
                        blindDataUniqueID, "blue", blueComponentIDs, blueColour);
                if (stat && redComponentIDs.length() == redColour.length()
                        && greenComponentIDs.length() == greenColour.length()
                        && blueComponentIDs.length() == blueColour.length())
                {
                        
                        
                        
                        
                        
                        
                        
                        int maxArrayLength =
                                (redComponentIDs.length() > greenComponentIDs.length()) ?
                                ((redComponentIDs.length() > blueComponentIDs.length()) ?
                                        redComponentIDs.length() : blueComponentIDs.length()) :
                                ((greenComponentIDs.length() > blueComponentIDs.length()) ?
                                        greenComponentIDs.length() : blueComponentIDs.length());
                        int* tempIntArray = new int[maxArrayLength];
                        double* tempFloatArray = new double[maxArrayLength];
                        mergeSort(0, redComponentIDs.length(), redComponentIDs,
                                tempIntArray, redColour, tempFloatArray);
                        mergeSort(0, greenComponentIDs.length(), greenComponentIDs,
                                tempIntArray, greenColour, tempFloatArray);
                        mergeSort(0, blueComponentIDs.length(), blueComponentIDs,
                                tempIntArray, blueColour, tempFloatArray);
                        
                        
                        for (i = 0; i < vertexCount; ++i)
                        {
                                int index = binarySearch(vertexIDs[i], redComponentIDs);
                                if (index != -1) colours[3*i] = redColour[index];
                                else colours[3*i] = defaultColour[0];
                                index = binarySearch(vertexIDs[i], greenComponentIDs);
                                if (index != -1) colours[3*i+1] = greenColour[index];
                                else colours[3*i+1] = defaultColour[1];
                                index = binarySearch(vertexIDs[i], blueComponentIDs);
                                if (index != -1) colours[3*i+2] = blueColour[index];
                                else colours[3*i+2] = defaultColour[2];
                        }
                }
        }
        
        
        
        view.beginGL();
        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
        glDisable(GL_LIGHTING);
        glVertexPointer(3, GL_FLOAT, 0, vertexArray);
        glEnableClientState(GL_VERTEX_ARRAY);
        
        glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
        glColorPointer(3, GL_DOUBLE, 0, colours);
        glEnableClientState(GL_COLOR_ARRAY);
        glDrawElements(prim, indexCount, GL_UNSIGNED_INT, indexArray);
        glDisable(GL_COLOR_MATERIAL);
        glPopClientAttrib();
        glPopAttrib();
        view.endGL();
        return MS::kSuccess;
}