#include <math.h>
#include <maya/MIOStream.h>
#include "apiMeshCreator.h"           
#include "apiMeshData.h"
#include <api_macros.h>
#include <maya/MFnMesh.h>
#include <maya/MFnPluginData.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnNumericAttribute.h>
#ifndef M_PI
#define M_PI        3.14159265358979323846
#endif
#ifndef M_PI_2
#define M_PI_2      1.57079632679489661923
#endif
MObject apiMeshCreator::outputSurface;
 
MTypeId apiMeshCreator::id( 0x80089 );
 
apiMeshCreator::apiMeshCreator() {}
apiMeshCreator::~apiMeshCreator() {}
{ 
    if ( plug == outputSurface ) {
        
        
        
        
        fnDataCreator.
create( tmpid, &stat );
        MCHECKERROR( stat, "compute : error creating apiMeshData")
        apiMeshData * newData = (apiMeshData*)fnDataCreator.data( &stat );
        MCHECKERROR( stat, "compute : error gettin at proxy apiMeshData object")
        apiMeshGeom * geomPtr = newData->fGeometry;
        
        
        
        bool hasHistory = computeInputMesh( plug, datablock,
                                            geomPtr->vertices,
                                            geomPtr->face_counts,
                                            geomPtr->face_connects,
                                            geomPtr->normals, 
                                            geomPtr->uvcoords
            );
                                            
        
        
        
        if ( !hasHistory ) {
            double shape_size = sizeHandle.
asDouble();
 
            MDataHandle typeHandle = datablock.inputValue( shapeType );
 
            short shape_type = typeHandle.
asShort();
 
            switch( shape_type )
            {
                case 0 : 
                    buildCube( shape_size,
                               geomPtr->vertices,
                               geomPtr->face_counts,
                               geomPtr->face_connects,
                               geomPtr->normals, 
                               geomPtr->uvcoords
                        );
                    break;
            
                case 1 : 
                    buildSphere( shape_size,
                                 32,
                                 geomPtr->vertices,
                                 geomPtr->face_counts,
                                 geomPtr->face_connects,
                                 geomPtr->normals, 
                                 geomPtr->uvcoords
                        );
                    break;
            } 
        }
        geomPtr->faceCount = geomPtr->face_counts.length();
        
        
        MDataHandle outHandle = datablock.outputValue( outputSurface );
 
        outHandle.
set( newData );
        datablock.setClean( plug );
    }
    else {
    }
}
void apiMeshCreator::buildCube( 
    double cube_size, 
    apiMeshGeomUV& uvs
)
{
    const int num_faces         = 6;
    const int num_face_connects = 24;
    const double normal_value   = 0.5775;
    const int uv_count          = 14; 
    
    uvs.reset(); 
    normals.
append( 
MVector( -normal_value, -normal_value, -normal_value ) );
    normals.
append( 
MVector(  normal_value, -normal_value, -normal_value ) );
    normals.
append( 
MVector(  normal_value, -normal_value,  normal_value ) );
    normals.
append( 
MVector( -normal_value, -normal_value,  normal_value ) );
    normals.
append( 
MVector( -normal_value,  normal_value, -normal_value ) );
    normals.
append( 
MVector( -normal_value,  normal_value,  normal_value ) );
    normals.
append( 
MVector(  normal_value,  normal_value,  normal_value ) );
    normals.
append( 
MVector(  normal_value,  normal_value, -normal_value ) );
    
    
    float uv_pts[uv_count * 2] = { 0.375, 0.0, 
                                   0.625, 0.0,
                                   0.625, 0.25, 
                                   0.375, 0.25,
                                   0.625, 0.5,
                                   0.375, 0.5,
                                   0.625, 0.75,
                                   0.375, 0.75,
                                   0.625, 1.0,
                                   0.375, 1.0,
                                   0.875, 0.0,
                                   0.875, 0.25,
                                   0.125, 0.0,
                                   0.125, 0.25 
    }; 
    
    
    
    int uv_fvid [ num_face_connects ] = {  0, 1, 2, 3, 
                                           3, 2, 4, 5, 
                                           5, 4, 6, 7, 
                                           7, 6, 8, 9, 
                                           1, 10, 11, 2, 
                                           12, 0, 3, 13 };
    int i;
    for ( i = 0; i < uv_count; i ++ ) {
        uvs.append_uv( uv_pts[i*2], uv_pts[i*2 + 1] ); 
    }
    
    for ( i = 0; i < num_face_connects; i ++ ) { 
        uvs.faceVertexIndex.append( uv_fvid[i] ); 
    }
    
    
    
    int face_counts[num_faces] = { 4, 4, 4, 4, 4, 4 };
    for ( i=0; i<num_faces; i++ )
    {
        faceCounts.
append( face_counts[i] );
    }
    
    
    int face_connects[ num_face_connects ] = {  0, 1, 2, 3,
                                                4, 5, 6, 7,
                                                3, 2, 6, 5,
                                                0, 3, 5, 4,
                                                0, 4, 7, 1,
                                                1, 7, 6, 2  };
    for ( i=0; i<num_face_connects; i++ )
    {
        faceConnects.
append( face_connects[i] );
    }
}
void apiMeshCreator::buildSphere( 
    double              rad, 
    int                 div, 
    apiMeshGeomUV &     uvs
)
{
    double u = -M_PI_2;
    double v = -M_PI;
    double u_delta = M_PI / ((double)div); 
    double v_delta = 2 * M_PI / ((double)div); 
    MPoint topPole( 0.0, rad, 0.0 );
 
    MPoint botPole( 0.0, -rad, 0.0 );
 
    
    
    int i;
    for ( i=0; i<(div-1); i++ )
    {
        u += u_delta;
        v = -M_PI;
        for ( int j=0; j<div; j++ )
        {
            double x = rad * cos(u) * cos(v);
            double y = rad * sin(u);
            double z = rad * cos(u) * sin(v) ;
            v += v_delta;
        }
    }
    
    
    int vid = 1;
    int numV = 0;
    for ( i=0; i<div; i++ )
    {
        for ( int j=0; j<div; j++ )
        {
            if ( i==0 ) {
                connects.
append( (j==(div-1)) ? vid : j+vid+1 );
            }
            else if ( i==(div-1) ) {
                connects.
append( j+vid+1-div );
                connects.
append( j==(div-1) ? vid+1-div : j+vid+2-div );
            }
            else {
                connects.
append( j + vid+1-div );
                connects.
append( j == (div-1) ? vid+1 : j+vid+2 );
                connects.
append( j == (div-1) ? vid+1-div : j+vid+2-div );
            }
            numV++;
        }
        vid = numV;
    }
    
    
}
MStatus apiMeshCreator::computeInputMesh( 
 
    apiMeshGeomUV&      uvs
)
{
    
    
    MCHECKERROR( stat, "compute get inputMesh")
    
    
    MPlug surfPlug( thisObj, inputMesh );
 
    if ( !surfPlug.isConnected() ) {
        MCHECKERROR( stat, "compute setClean" )
    }
    
    
    MCHECKERROR( stat, "compute - 
MFnMesh error" );
    stat = surfFn.getPoints( vertices, 
MSpace::kObject );
    MCHECKERROR( stat, "compute getPoints"); 
    
    
    bool hasUVs = surfFn.numUVs() > 0;  
    surfFn.getUVs( uvs.ucoord, uvs.vcoord ); 
    for ( int i=0; i<surfFn.numPolygons(); i++ )
    {
        surfFn.getPolygonVertices( i, polyVerts );
        int uvId; 
        for ( int v=0; v<pvc; v++ ) {
            if ( hasUVs ) {
                surfFn.getPolygonUVid( i, v, uvId );
                uvs.faceVertexIndex.append( uvId );
            }
            connects.
append( polyVerts[v] );
        }
    }
    for ( int n=0; n<(int)vertices.length(); n++ )
    {
        surfFn.getVertexNormal( n, normal );
    }
}
void* apiMeshCreator::creator()
{
    return new apiMeshCreator();
}
MStatus apiMeshCreator::initialize()
 
{ 
    
    MAKE_NUMERIC_ATTR(  size, "size", "sz",
                        false, false, true );
    shapeType = enumAttr.
create( 
"shapeType", 
"st", 0, &stat );
    MCHECKERROR( stat, "create shapeType attribute" );
    MCHECKERROR( stat, "add enum type cube" );
    stat = enumAttr.
addField( 
"sphere", 1 );
    MCHECKERROR( stat, "add enum type sphere" );
    ADD_ATTRIBUTE( shapeType );
    
    MAKE_TYPED_ATTR( inputMesh, 
"inputMesh", 
"im", 
MFnData::kMesh, NULL );
    
    outputSurface = typedAttr.
create( 
"outputSurface", 
"os",
                                      apiMeshData::id,
    MCHECKERROR( stat, "create outputSurface attribute" )
    typedAttr.setWritable( false );
    ADD_ATTRIBUTE( outputSurface );
    
    
    ATTRIBUTE_AFFECTS( inputMesh, outputSurface );
    ATTRIBUTE_AFFECTS( size, outputSurface );
    ATTRIBUTE_AFFECTS( shapeType, outputSurface );
}