#include "SceneContext.h"
#include "SceneCache.h"
#include "SetCamera.h"
#include "DrawScene.h"
#include "DrawText.h"
#include "targa.h"
#include "../Common/Common.h"
namespace
{
    
    const char * SAMPLE_FILENAME = "humanoid.fbx";
    
    const int LEFT_BUTTON = 0;
    const int MIDDLE_BUTTON = 1;
    const int RIGHT_BUTTON = 2;
    const int BUTTON_DOWN = 0;
    const int BUTTON_UP = 1;
    
    {
        if (pNode)
        {
            {
                {
                }
            }
            for (int i = 0; i < lCount; i++)
            {
                FillCameraArrayRecursive(pNode->
GetChild(i), pCameraArray);
 
            }
        }
    }
    
    {
        FillCameraArrayRecursive(pScene->
GetRootNode(), pCameraArray);
 
    }
    
    {
        for (int i=0; i < lPoseCount; ++i)
        {
        }
    }
    {
        
        for (int lIndex=0; lIndex<lNodeCount; lIndex++)
        {
            {
                
                lVertexCacheDeformerCount = lVertexCacheDeformerCount > 0 ? 1 : 0;
                for (i=0; i<lVertexCacheDeformerCount; ++i )
                {
                    
                    if( !lDeformer ) continue;
                    if( !lCache ) continue;
                    
                    {
                        {
                            
                            
#if 0 
                            {
                                
                                FbxString lTheErrorIs = lCache->GetStaus().GetErrorString();
 
                            }
#endif
                        }
                        {
                            
                            
                            
                            {
                                
                                FbxString lTheErrorIs = lStatus.GetErrorString();
 
                            }
                            
                        }
                        
                        
                        {
                            
                            FbxString lTheErrorIs = lStatus.GetErrorString();
 
                            
                        }
                        else
                        {
                            
                            {
                                
                                if(lChannel_Start < pCache_Start) pCache_Start = lChannel_Start;
                                
                                if(lChannel_Stop  > pCache_Stop)  pCache_Stop  = lChannel_Stop;
                            }
                        }
                    }
                }
            }
        }
    }
    
    bool LoadTextureFromFile(
const FbxString & pFilePath, 
unsigned int & pTextureObject)
 
    {
        {
            tga_image lTGAImage;
            if (tga_read(&lTGAImage, pFilePath.
Buffer()) == TGA_NOERR)
 
            {
                
                if (tga_is_right_to_left(&lTGAImage))
                    tga_flip_horiz(&lTGAImage);
                
                if (tga_is_top_to_bottom(&lTGAImage))
                    tga_flip_vert(&lTGAImage);
                
                tga_convert_depth(&lTGAImage, 24);
                
                glGenTextures(1, &pTextureObject);
                glBindTexture(GL_TEXTURE_2D, pTextureObject);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
                glTexImage2D(GL_TEXTURE_2D, 0, 3, lTGAImage.width, lTGAImage.height, 0, GL_BGR,
                    GL_UNSIGNED_BYTE, lTGAImage.image_data);
                glBindTexture(GL_TEXTURE_2D, 0);
                tga_free_buffers(&lTGAImage);
                return true;
            }
        }
        return false;
    }
    
    
    {
        
        for (int lMaterialIndex = 0; lMaterialIndex < lMaterialCount; ++lMaterialIndex)
        {
            {
                if (lMaterialCache->Initialize(lMaterial))
                {
                }
            }
        }
        if (lNodeAttribute)
        {
            
            {
                {
                    if (lMeshCache->Initialize(lMesh))
                    {
                    }
                }
            }
            
            {
                {
                    if (lLightCache->Initialize(lLight, pAnimLayer))
                    {
                    }
                }
            }
        }
        for (int lChildIndex = 0; lChildIndex < lChildCount; ++lChildIndex)
        {
            LoadCacheRecursive(pNode->
GetChild(lChildIndex), pAnimLayer, pSupportVBO);
 
        }
    }
    
    void UnloadCacheRecursive(
FbxNode * pNode)
 
    {
        
        for (int lMaterialIndex = 0; lMaterialIndex < lMaterialCount; ++lMaterialIndex)
        {
            {
                MaterialCache * lMaterialCache = 
static_cast<MaterialCache *
>(lMaterial->
GetUserDataPtr());
 
                delete lMaterialCache;
            }
        }
        if (lNodeAttribute)
        {
            
            {
                {
                    VBOMesh * lMeshCache = 
static_cast<VBOMesh *
>(lMesh->
GetUserDataPtr());
 
                    delete lMeshCache;
                }
            }
            
            {
                {
                    LightCache * lLightCache = 
static_cast<LightCache *
>(lLight->
GetUserDataPtr());
 
                    delete lLightCache;
                }
            }
        }
        for (int lChildIndex = 0; lChildIndex < lChildCount; ++lChildIndex)
        {
            UnloadCacheRecursive(pNode->
GetChild(lChildIndex));
 
        }
    }
    
    void LoadCacheRecursive(
FbxScene * pScene, 
FbxAnimLayer * pAnimLayer, 
const char * pFbxFileName, 
bool pSupportVBO)
 
    {
        
        for (int lTextureIndex = 0; lTextureIndex < lTextureCount; ++lTextureIndex)
        {
            {
                
                
                
                {
                    FBXSDK_printf(
"Only TGA textures are supported now: %s\n", lFileName.
Buffer());
 
                    continue;
                }
                GLuint lTextureObject = 0;
                bool lStatus = LoadTextureFromFile(lFileName, lTextureObject);
                if (!lStatus)
                {
                    
                    lStatus = LoadTextureFromFile(lResolvedFileName, lTextureObject);
                }
                if (!lStatus)
                {
                    
                    lStatus = LoadTextureFromFile(lResolvedFileName, lTextureObject);
                }
                if (!lStatus)
                {
                    FBXSDK_printf(
"Failed to load texture file: %s\n", lFileName.
Buffer());
 
                    continue;
                }
                if (lStatus)
                {
                    GLuint * lTextureName = new GLuint(lTextureObject);
                }
            }
        }
        LoadCacheRecursive(pScene->
GetRootNode(), pAnimLayer, pSupportVBO);
 
    }
    
    void UnloadCacheRecursive(
FbxScene * pScene)
 
    {
        for (int lTextureIndex = 0; lTextureIndex < lTextureCount; ++lTextureIndex)
        {
            {
                GLuint * lTextureName = 
static_cast<GLuint *
>(lFileTexture->
GetUserDataPtr());
 
                glDeleteTextures(1, lTextureName);
                delete lTextureName;
            }
        }
    }
}
bool InitializeOpenGL()
{
    
    GLenum lError = glewInit();
    if (lError != GLEW_OK)
    {
        FBXSDK_printf("GLEW Error: %s\n", glewGetErrorString(lError));
        return false;
    }
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    
    if (!GLEW_VERSION_1_5)
    {
        FBXSDK_printf("The OpenGL version should be at least 1.5 to display shaded scene!\n");
        return false;
    }
    return true;
}
SceneContext::SceneContext(const char * pFileName, int pWindowWidth, int pWindowHeight, bool pSupportVBO)
: mFileName(pFileName), mStatus(UNLOADED),
mPoseIndex(-1), mCameraStatus(CAMERA_NOTHING), mPause(false), mShadingMode(SHADING_MODE_SHADED),
mSupportVBO(pSupportVBO), mCameraZoomMode(ZOOM_FOCAL_LENGTH),
mWindowWidth(pWindowWidth), mWindowHeight(pWindowHeight), mDrawText(new DrawText)
{
        mFileName = SAMPLE_FILENAME;
    
   
   
   InitializeSdkObjects(mSdkManager, mScene);
   if (mSdkManager)
   {
       
       int lFileFormat = -1;
       if (!mSdkManager->GetIOPluginRegistry()->DetectReaderFileFormat(mFileName, lFileFormat) )
       {
           
           lFileFormat = mSdkManager->GetIOPluginRegistry()->FindReaderIDByDescription( "FBX binary (*.fbx)" );;
       }
       
       if(mImporter->Initialize(mFileName, lFileFormat) == true)
       {
           
           
           mWindowMessage = "Importing file ";
           mWindowMessage += mFileName;
           mWindowMessage += "\nPlease wait!";
           
           mStatus = MUST_BE_LOADED;
       }
       else
       {
           mWindowMessage = "Unable to open file ";
           mWindowMessage += mFileName;
           mWindowMessage += "\nError reported: ";
           mWindowMessage += mImporter->GetStatus().GetErrorString();
           mWindowMessage += "\nEsc to exit";
       }
   }
   else
   {
       mWindowMessage = "Unable to create the FBX SDK manager";
       mWindowMessage += "\nEsc to exit";
   }
}
SceneContext::~SceneContext()
{
    delete mDrawText;
    
    if (mScene)
    {
        UnloadCacheRecursive(mScene);
    }
    
    
    
    DestroySdkObjects(mSdkManager, true);
}
bool SceneContext::LoadFile()
{
    bool lResult = false;
    
    if (mStatus == MUST_BE_LOADED)
    {
        if (mImporter->Import(mScene) == true)
        {
            
            
            mStatus = MUST_BE_REFRESHED;
            
            FbxAxisSystem SceneAxisSystem = mScene->GetGlobalSettings().GetAxisSystem();
 
            if( SceneAxisSystem != OurAxisSystem )
            {
                OurAxisSystem.ConvertScene(mScene);
            }
            
            FbxSystemUnit SceneSystemUnit = mScene->GetGlobalSettings().GetSystemUnit();
 
            {
                
            }
            
            mScene->FillAnimStackNameArray(mAnimStackNameArray);
            
            FillCameraArray(mScene, mCameraArray);
            
            lGeomConverter.Triangulate(mScene, true);
            
            LoadCacheRecursive(mScene, mCurrentAnimLayer, mFileName, mSupportVBO);
            
            
            PreparePointCacheData(mScene, mCache_Start, mCache_Stop);
            
            FillPoseArray(mScene, mPoseArray);
            
            mWindowMessage = "File ";
            mWindowMessage += mFileName;
            mWindowMessage += "\nClick on the right mouse button to enter menu.";
            mWindowMessage += "\nEsc to exit.";
            
            mFrameTime.SetTime(0, 0, 0, 1, 0, mScene->GetGlobalSettings().GetTimeMode());
            
            FBXSDK_printf("Play/Pause Animation: Space Bar.\n");
            FBXSDK_printf("Camera Rotate: Left Mouse Button.\n");
            FBXSDK_printf("Camera Pan: Left Mouse Button + Middle Mouse Button.\n");
            FBXSDK_printf("Camera Zoom: Middle Mouse Button.\n");
            lResult = true;
        }
        else
        {
            
            mStatus = UNLOADED;
            mWindowMessage = "Unable to import file ";
            mWindowMessage += mFileName;
            mWindowMessage += "\nError reported: ";
            mWindowMessage += mImporter->GetStatus().GetErrorString();
        }
        
        mImporter->Destroy();
    }
    return lResult;
}
bool SceneContext::SetCurrentAnimStack(int pIndex)
{
    const int lAnimStackCount = mAnimStackNameArray.GetCount();
    if (!lAnimStackCount || pIndex >= lAnimStackCount)
    {
        return false;
    }
    
   if (lCurrentAnimationStack == 
NULL)
 
   {
       
       return false;
   }
   
   
   mScene->SetCurrentAnimationStack(lCurrentAnimationStack);
   FbxTakeInfo* lCurrentTakeInfo = mScene->GetTakeInfo(*(mAnimStackNameArray[pIndex]));
 
   if (lCurrentTakeInfo)
   {
   }
   else
   {
       
       mScene->GetGlobalSettings().GetTimelineDefaultTimeSpan(lTimeLineTimeSpan);
       mStop  = lTimeLineTimeSpan.
GetStop();
 
   }
   
   if(mCache_Start < mStart)
       mStart = mCache_Start;
   
   if(mCache_Stop  > mStop)  
       mStop  = mCache_Stop;
   
   mCurrentTime = mStart;
   
   
   mStatus = MUST_BE_REFRESHED;
   return true;
}
bool SceneContext::SetCurrentCamera(const char * pCameraName)
{
    if (!pCameraName)
    {
        return false;
    }
    mStatus = MUST_BE_REFRESHED;
    return true;
}
bool SceneContext::SetCurrentPoseIndex(int pPoseIndex)
{
    mPoseIndex = pPoseIndex;
    mStatus = MUST_BE_REFRESHED;
    return true;
}
void SceneContext::OnTimerClick() const
{
    
    if (mStop > mStart && !mPause)
    {
        
        
        mStatus = MUST_BE_REFRESHED;
        mCurrentTime += mFrameTime;
        if (mCurrentTime > mStop)
        {
            mCurrentTime = mStart;
        }
    }
    
    
    else
    {
        
        
        mStatus = REFRESHED;
    }
}
bool SceneContext::OnDisplay()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    if (mStatus != UNLOADED && mStatus != MUST_BE_LOADED)
    {
        glPushAttrib(GL_ENABLE_BIT);
        glPushAttrib(GL_LIGHTING_BIT);
        glEnable(GL_DEPTH_TEST);
        
        glEnable(GL_CULL_FACE);
        
        SetCamera(mScene, mCurrentTime, mCurrentAnimLayer, mCameraArray,
            mWindowWidth, mWindowHeight);
        if (mPoseIndex != -1)
        {
            lPose = mScene->GetPose(mPoseIndex);
        }
        
        
        if (mSelectedNode)
        {
            
            InitializeLights(mScene, mCurrentTime, lPose);
            DrawNodeRecursive(mSelectedNode, mCurrentTime, mCurrentAnimLayer, lDummyGlobalPosition, lPose, mShadingMode);
            DisplayGrid(lDummyGlobalPosition);
        }
        
        else
        {
            InitializeLights(mScene, mCurrentTime, lPose);
            DrawNodeRecursive(mScene->GetRootNode(), mCurrentTime, mCurrentAnimLayer, lDummyGlobalPosition, lPose, mShadingMode);
            DisplayGrid(lDummyGlobalPosition);
        }
        glPopAttrib();
        glPopAttrib();
    }
    DisplayWindowMessage();
    return true;
}
void SceneContext::OnReshape(int pWidth, int pHeight)
{
    glViewport(0, 0, (GLsizei)pWidth, (GLsizei)pHeight);
    mWindowWidth = pWidth;
    mWindowHeight = pHeight;
}
void SceneContext::OnKeyboard(unsigned char pKey)
{
    
    if (pKey == 43 || pKey == 61)
    {
        FbxCamera* lCamera = GetCurrentCamera(mScene);
 
        if(lCamera)
        {
            
            CameraZoom(mScene, 10, mCameraZoomMode);
            mStatus = MUST_BE_REFRESHED;
        }
    }
    
    if (pKey == 45 || pKey == 95)
    {
        FbxCamera* lCamera = GetCurrentCamera(mScene);
 
        if(lCamera)
        {
            
            CameraZoom(mScene, 0 - 10, mCameraZoomMode);
            mStatus = MUST_BE_REFRESHED;
        }
    }
    
    if (pKey == 'N' || pKey == 'n')
    {
        
    }
    
    if (pKey == ' ')
    {
        SetPause(!GetPause());
    }
}
void SceneContext::OnMouse(int pButton, int pState, int pX, int pY)
{
    
    FbxCamera* lCamera = GetCurrentCamera(mScene);
 
    if (lCamera)
    {
    }
    mLastX = pX;
    mLastY = pY;
    switch (pButton)
    {
    case LEFT_BUTTON:
        
        switch (pState)
        {
        case BUTTON_DOWN:
            if (mCameraStatus == CAMERA_ZOOM)
            {
                mCameraStatus = CAMERA_PAN;
            }
            else
            {
                mCameraStatus = CAMERA_ORBIT;
            }
            break;
        default:
            if (mCameraStatus == CAMERA_PAN)
            {
                mCameraStatus = CAMERA_ZOOM;
            }
            else
            {
                mCameraStatus = CAMERA_NOTHING;
            }
            break;
        }
        break;
    case MIDDLE_BUTTON:
        
        switch (pState)
        {
        case BUTTON_DOWN:
            if (mCameraStatus == CAMERA_ORBIT)
            {
                mCameraStatus = CAMERA_PAN;
            }
            else
            {
                mCameraStatus = CAMERA_ZOOM;
            }
            break;
        
        default:
            if (mCameraStatus == CAMERA_PAN)
            {
                mCameraStatus = CAMERA_ORBIT;
            }
            else
            {
                mCameraStatus = CAMERA_NOTHING;
            }
            break;
        }
        break;
    }
}
void SceneContext::OnMouseMotion(int pX, int pY)
{
    int motion;
    switch (mCameraStatus)
    {
    default:
        break;
    case CAMERA_ORBIT:
        CameraOrbit(mScene, mCamPosition, mRoll, pX-mLastX, mLastY-pY);
        mStatus = MUST_BE_REFRESHED;
        break;
    case CAMERA_ZOOM:
        motion = mLastY-pY;
        CameraZoom(mScene, motion, mCameraZoomMode);
        mLastY = pY;
        mStatus = MUST_BE_REFRESHED;
        break;
    case CAMERA_PAN:
        CameraPan(mScene, mCamPosition, mCamCenter, mRoll, pX-mLastX, mLastY-pY);
        mStatus = MUST_BE_REFRESHED;
        break;
    }
}
void SceneContext::SetSelectedNode(
FbxNode * pSelectedNode)
 
{
    mSelectedNode = pSelectedNode;
    mStatus = MUST_BE_REFRESHED;
}
void SceneContext::SetShadingMode(ShadingMode pMode)
{
    mShadingMode = pMode;
    mStatus = MUST_BE_REFRESHED;
}
void SceneContext::DisplayWindowMessage()
{
    glColor3f(1.0, 1.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0, mWindowWidth, 0, mWindowHeight);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    
    const float lX = 5;
    const float lY = static_cast<float>(mWindowHeight) - 20;
    glTranslatef(lX, lY, 0);
    mDrawText->SetPointSize(15.f);
    mDrawText->Display(mWindowMessage.Buffer());
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
}
void SceneContext::DisplayGrid(
const FbxAMatrix & pTransform)
 
{
    glPushMatrix();
    glMultMatrixd(pTransform);
    
    glColor3f(0.3f, 0.3f, 0.3f);
    glLineWidth(1.0);
    const int hw = 500;
    const int step = 20;
    const int bigstep = 100;
    int       i;
    
    for (i = -hw; i <= hw; i+=step) {
        if (i % bigstep == 0) {
            glLineWidth(2.0);
        } else {
            glLineWidth(1.0);
        }
        glBegin(GL_LINES);
        glVertex3i(i,0,-hw);
        glVertex3i(i,0,hw);
        glEnd();
        glBegin(GL_LINES);
        glVertex3i(-hw,0,i);
        glVertex3i(hw,0,i);
        glEnd();
    }
    
    const GLfloat zoffset = -2.f;
    const GLfloat xoffset = 1.f;
    mDrawText->SetPointSize(4.f);
    for (i = -hw; i <= hw; i+=bigstep)
    {
        int lCount;
        
        
        if (i == 0) {
            scoord = "0";
            lCount = (int)scoord.
GetLen();
 
            glPushMatrix();
            glVertex3f(i+xoffset,0,zoffset);
            glRotatef(-90,1,0,0);
            
            mDrawText->Display(scoord.
Buffer());
 
            glPopMatrix();
            continue;
        }
        
        scoord = "X: ";
        scoord += i;
        lCount = (int)scoord.
GetLen();
 
        glPushMatrix();
        glTranslatef(i+xoffset,0,zoffset);
        glRotatef(-90,1,0,0);
        mDrawText->Display(scoord.
Buffer());
 
        glPopMatrix();
        
        scoord = "Z: ";
        scoord += i;
        lCount = (int)scoord.
GetLen();
 
        glPushMatrix();
        glTranslatef(xoffset,0,i+zoffset);
        glRotatef(-90,1,0,0);
        mDrawText->Display(scoord.
Buffer());
 
        glPopMatrix();
    }
    glPopMatrix();
}
void SceneContext::SetZoomMode( CameraZoomMode pZoomMode)
{
    if( pZoomMode == ZOOM_POSITION)
    {
        mCameraZoomMode = ZOOM_POSITION;
    }
    else
    {
        mCameraZoomMode =  ZOOM_FOCAL_LENGTH;
    }
    
}