#include "SceneContext.h"
#include "GL/glut.h"
void ExitFunction();
void CreateMenus();
void CameraSelectionCallback(int pItem);
void CameraZoomModeCallback(int pItem);
void AnimStackSelectionCallback(int pItem);
void MenuSelectionCallback(int pItem);
void PoseSelectionCallback(int pItem);
void ShadingModeSelectionCallback(int pItem);
void TimerCallback(int);
void DisplayCallback();
void ReshapeCallback(int pWidth, int pHeight);
void KeyboardCallback(unsigned char pKey, int, int);
void MouseCallback(int button, int state, int x, int y);
void MotionCallback(int x, int y);
SceneContext * gSceneContext;
#define PRODUCER_PERSPECTIVE_ITEM   100
#define PRODUCER_TOP_ITEM           101
#define PRODUCER_BOTTOM_ITEM        102
#define PRODUCER_FRONT_ITEM         103
#define PRODUCER_BACK_ITEM          104
#define PRODUCER_RIGHT_ITEM         105
#define PRODUCER_LEFT_ITEM          106
#define CAMERA_SWITCHER_ITEM        107
#define PLAY_ANIMATION              200
const int MENU_SHADING_MODE_WIREFRAME = 300;
const int MENU_SHADING_MODE_SHADED = 301;
const char * MENU_STRING_SHADING_MODE_WIREFRAME = "Wireframe";
const char * MENU_STRING_SHADING_MODE_SHADED = "Shaded";
const int MENU_ZOOM_FOCAL_LENGTH =          401;
const int MENU_ZOOM_POSITION     =          402;
const int MENU_EXIT = 400;
const int DEFAULT_WINDOW_WIDTH = 720;
const int DEFAULT_WINDOW_HEIGHT = 486;
class MyMemoryAllocator
{
public:
    static void* MyMalloc(size_t pSize)
    {
        char *p = (char*)malloc(pSize+1);
        *p = '#';
        return p+1;
    }
    static void* MyCalloc(size_t pCount, size_t pSize)
    {
        char *p = (char*)calloc(pCount, pSize+1);
        *p = '#';
        return p+1;
    }
    static void* MyRealloc(void* pData, size_t pSize)
    {
        if (pData)
        {
            FBX_ASSERT(*((char*)pData-1)=='#');
            if (*((char*)pData-1)=='#')
            {
                char *p = (char*)realloc((char*)pData-1, pSize+1);
                *p = '#';
                return p+1;
            }
            else
            {   
                char *p = (char*)realloc((char*)pData, pSize+1);
                *p = '#';
                return p+1;
            }
        }
        else
        {
            char *p = (
char*)realloc(
NULL, pSize+1);
 
            *p = '#';
            return p+1;
        }
    }
    static void MyFree(void* pData)
    {
            return;
        FBX_ASSERT(*((char*)pData-1)=='#');
        if (*((char*)pData-1)=='#')
        {
            free((char*)pData-1);
        }
        else
        {   
            free(pData);
        }
    }
};
static bool gAutoQuit = false;
int main(int argc, char** argv)
{
    
    atexit(ExitFunction);
    
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); 
    glutInitWindowPosition(100, 100);
    glutCreateWindow("ViewScene");
    
    const bool lSupportVBO = InitializeOpenGL();
    
    glutDisplayFunc(DisplayCallback); 
    glutReshapeFunc(ReshapeCallback);
    glutKeyboardFunc(KeyboardCallback);
    glutMouseFunc(MouseCallback);
    glutMotionFunc(MotionCallback);
    for( int i = 1, c = argc; i < c; ++i )
    {
        if( 
FbxString(argv[i]) == 
"-test" ) gAutoQuit = 
true;
 
        else if( lFilePath.IsEmpty() ) lFilePath = argv[i];
    }
    gSceneContext = 
new SceneContext(!lFilePath.IsEmpty() ? lFilePath.Buffer() : 
NULL, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, lSupportVBO);
 
    glutMainLoop();
    return 0;
}
void ExitFunction()
{
    delete gSceneContext;
}
void CreateMenus()
{
    
    int lCameraMenu = glutCreateMenu(CameraSelectionCallback);
    
    
    if (lCameraArray.GetCount() > 0)
    {
    }
    
    for (int lCameraIndex = 0; lCameraIndex < lCameraArray.GetCount(); ++lCameraIndex)
    {
        glutAddMenuEntry(lCameraArray[lCameraIndex]->GetName(), lCameraIndex);
    }   
    
    int lAnimStackMenu = glutCreateMenu(AnimStackSelectionCallback);
    int lCurrentAnimStackIndex = 0;
    
    for (int lPoseIndex = 0; lPoseIndex < lAnimStackNameArray.GetCount(); ++lPoseIndex)
    {
        glutAddMenuEntry(lAnimStackNameArray[lPoseIndex]->Buffer(), lPoseIndex);
        
        if (lAnimStackNameArray[lPoseIndex]->Compare(gSceneContext->GetScene()->ActiveAnimStackName.Get()) == 0)
        {
            lCurrentAnimStackIndex = lPoseIndex;
        }
    }
    
    
    AnimStackSelectionCallback(lCurrentAnimStackIndex);
    const int lShadingModeMenu = glutCreateMenu(ShadingModeSelectionCallback);
    glutAddMenuEntry(MENU_STRING_SHADING_MODE_WIREFRAME, MENU_SHADING_MODE_WIREFRAME);
    glutAddMenuEntry(MENU_STRING_SHADING_MODE_SHADED, MENU_SHADING_MODE_SHADED);
    int lBindPoseCount = 0;
    int lRestPoseCount = 0;
    
    int lBindPoseMenu = glutCreateMenu(PoseSelectionCallback);
    
    for (int lPoseIndex = 0; lPoseIndex < lPoseArray.GetCount(); ++lPoseIndex)
    {
        if (lPoseArray[lPoseIndex]->IsBindPose())
        {
            glutAddMenuEntry(lPoseArray[lPoseIndex]->GetName(), lPoseIndex);
            lBindPoseCount++;
        }
    }
    
    int lRestPoseMenu = glutCreateMenu(PoseSelectionCallback);
    
    for (int lPoseIndex = 0; lPoseIndex < lPoseArray.GetCount(); ++lPoseIndex)
    {
        if (lPoseArray[lPoseIndex]->IsRestPose())
        {
            glutAddMenuEntry(lPoseArray[lPoseIndex]->GetName(), lPoseIndex);
            lRestPoseCount++;
        }
    }
    
    int lPoseMenu = 0;
    if (lBindPoseCount>0 || lRestPoseCount>0)
    {
        lPoseMenu = glutCreateMenu(PoseSelectionCallback);
        if (lBindPoseCount>0)
            glutAddSubMenu("Bind Pose", lBindPoseMenu);
        if (lRestPoseCount>0)
            glutAddSubMenu("Rest Pose", lRestPoseMenu);
    }
    
    int lZoomMenu = glutCreateMenu( CameraZoomModeCallback);
    glutAddMenuEntry( "Zooming lens", MENU_ZOOM_FOCAL_LENGTH);
    glutAddMenuEntry( "Zooming Position", MENU_ZOOM_POSITION);
    
    glutCreateMenu(MenuSelectionCallback);
    glutAddSubMenu("Select Camera", lCameraMenu); 
    glutAddSubMenu("Select Animation Stack", lAnimStackMenu);
    glutAddSubMenu("Select Shading Mode", lShadingModeMenu);
    if (lBindPoseCount>0 || lRestPoseCount>0)
        glutAddSubMenu("Go to Pose", lPoseMenu);
    glutAddSubMenu( "Zoom Mode", lZoomMenu);
    glutAddMenuEntry("Play", PLAY_ANIMATION);
    glutAddMenuEntry("Exit", MENU_EXIT); 
    glutAttachMenu(GLUT_RIGHT_BUTTON);
}
void CameraSelectionCallback(int pItem)
{
    if (pItem == PRODUCER_PERSPECTIVE_ITEM)
    {
    }
    else if (pItem == PRODUCER_TOP_ITEM)
    {
    }
    else if (pItem == PRODUCER_BOTTOM_ITEM)
    {
    }
    else if (pItem == PRODUCER_FRONT_ITEM)
    {
    }
    else if (pItem == PRODUCER_BACK_ITEM)
    {
    }
    else if (pItem == PRODUCER_RIGHT_ITEM)
    {
    }
    else if (pItem == PRODUCER_LEFT_ITEM)
    {
    }
    else if (pItem == CAMERA_SWITCHER_ITEM)
    {
    }
    else if (pItem >= 0 && pItem < lCameraArray.GetCount())
    {
        gSceneContext->SetCurrentCamera(lCameraArray[pItem]->GetName());
    }
}
void CameraZoomModeCallback(int pItem)
{
    if( pItem == MENU_ZOOM_FOCAL_LENGTH)
        gSceneContext->SetZoomMode( SceneContext::ZOOM_FOCAL_LENGTH);
    else
        gSceneContext->SetZoomMode(SceneContext::ZOOM_POSITION);
}
void AnimStackSelectionCallback( int pItem )
{
    gSceneContext->SetCurrentAnimStack(pItem);
}
void PoseSelectionCallback(int pItem)
{
    gSceneContext->SetCurrentPoseIndex(pItem);
}
void ShadingModeSelectionCallback(int pItem)
{
    if (pItem == MENU_SHADING_MODE_WIREFRAME)
    {
        gSceneContext->SetShadingMode(SHADING_MODE_WIREFRAME);
    }
    else if (pItem == MENU_SHADING_MODE_SHADED)
    {
        gSceneContext->SetShadingMode(SHADING_MODE_SHADED);
    }
}
void MenuSelectionCallback(int pItem)
{
    if (pItem == PLAY_ANIMATION)
    {
        gSceneContext->SetCurrentPoseIndex(-1);
    }
    else if (pItem == MENU_EXIT)
    {
        exit(0);
    }
}
void TimerCallback(int)
{
    
    if (gSceneContext->GetStatus() == SceneContext::MUST_BE_REFRESHED)
    {
        glutPostRedisplay();
    }
    gSceneContext->OnTimerClick();
    
    glutTimerFunc((unsigned int)gSceneContext->GetFrameTime().GetMilliSeconds(), TimerCallback, 0);
}
void DisplayCallback()
{
    gSceneContext->OnDisplay();
    glutSwapBuffers();
    
    if (gSceneContext->GetStatus() == SceneContext::MUST_BE_LOADED)
    {
        
        
        
        gSceneContext->LoadFile();
        CreateMenus();
        
        glutTimerFunc((unsigned int)gSceneContext->GetFrameTime().GetMilliSeconds(), TimerCallback, 0);
    }
    if( gAutoQuit ) exit(0);
}
void ReshapeCallback(int pWidth, int pHeight)
{
    gSceneContext->OnReshape(pWidth, pHeight);
}
void KeyboardCallback(unsigned char pKey, int , int )
{
    
    if (pKey == 27)
    {
        exit(0);
    }
    gSceneContext->OnKeyboard(pKey);
}
void MouseCallback(int button, int state, int x, int y)
{
    gSceneContext->OnMouse(button, state, x, y);
}
void MotionCallback(int x, int y)
{
    gSceneContext->OnMouseMotion(x, y);
}