#include <fbxsdk.h>
#ifdef KARCH_ENV_WIN
#include <malloc.h>
#endif
#include "../MyOwnWriterReader/MyOwnWriterReader.h"
#include "../Common/Common.h"
#define SAMPLE_FILENAME "ExportScene05.fbx"
bool CreateScene(KFbxScene* pScene);
void SetGlobalDefaultPosition(KFbxNode* pNode, KFbxXMatrix pGlobalPosition);
KFbxXMatrix GetGlobalDefaultPosition(KFbxNode* pNode);
class MyMemoryAllocator : public KFbxMemoryAllocator
{
public:
    MyMemoryAllocator() 
        : KFbxMemoryAllocator(MyMalloc, MyCalloc, MyRealloc, MyFree, MyMsize)
    {
    }
    ~MyMemoryAllocator()  
    {
    }
    static void* MyMalloc(size_t pSize)       
    {
        return malloc(pSize);
    }
    static void* MyCalloc(size_t pCount,size_t pSize)
    {
        return calloc(pCount, pSize);
    }
    static void* MyRealloc(void* pData, size_t pSize)
    {
        return realloc(pData, pSize);
    }
    static void  MyFree(void* pData)
    {
        free(pData);
    }
    static size_t MyMsize(void* pData)
    {
        return _msize(pData);
    }
};
int main(int argc, char** argv)
{
    KFbxSdkManager* lSdkManager = NULL;
    MyMemoryAllocator lMyMemoryAllocator;
    KFbxScene* lScene = NULL;
    bool lResult;
    
    KFbxSdkManager::SetMemoryAllocator(&lMyMemoryAllocator);
    
    InitializeSdkObjects(lSdkManager, lScene);
    
    lResult = CreateScene(lScene);
    if(lResult == false)
    {
        printf("\n\nAn error occurred while creating the scene...\n");
        DestroySdkObjects(lSdkManager);
        return 1;
    }
    
    
    if(argc > 1)
    {
        lResult = SaveScene(lSdkManager, lScene, argv[1]);
    }
    
    else
    {
        lResult = SaveScene(lSdkManager, lScene, SAMPLE_FILENAME);
    }
    if(lResult == false)
    {
        printf("\n\nAn error occurred while saving the scene...\n");
        DestroySdkObjects(lSdkManager);
        return 1;
    }
    
    printf("Writing to file with custom writer\n");
    int lRegisteredCount;
    int lPluginId;
    
    lSdkManager->GetIOPluginRegistry()->RegisterWriter(CreateMyOwnWriter, GetMyOwnWriterInfo,
        lPluginId, lRegisteredCount, FillOwnWriterIOSettings);
    
    const char* lFileName = "CustomWriter.ABC";
    
    KFbxExporter* lExporter = KFbxExporter::Create(lSdkManager, "");
    
    int lFileFormat = lSdkManager->GetIOPluginRegistry()->FindWriterIDByExtension("ABC");
    
    
    if(lExporter->Initialize(lFileName, lFileFormat, lSdkManager->GetIOSettings()) == false)
    {
        printf("Call to KFbxExporter::Initialize() failed.\n");
        printf("Error returned: %s\n\n", lExporter->GetLastErrorString());
        
        DestroySdkObjects(lSdkManager);
        return 1;
    }
    
    lResult = lExporter->Export(lScene); 
    if(lResult == false)
    {
        printf("Error in write of our custom writer\n");
        
        DestroySdkObjects(lSdkManager);
        return 1;
    }
    
    
    lSdkManager->GetIOPluginRegistry()->RegisterReader(CreateMyOwnReader, GetMyOwnReaderInfo,
        lPluginId, lRegisteredCount, FillOwnReaderIOSettings);
    
    KFbxImporter* lImporter = KFbxImporter::Create(lSdkManager,"");
    
    
    lResult = lImporter->Initialize(lFileName, -1, lSdkManager->GetIOSettings() );
    
    lResult = lImporter->Import(lScene);
    if(lResult == false)
    {
        printf("There was a problem in the read of our custom reader\n");
        
        DestroySdkObjects(lSdkManager);
        return 1;
    }
    
    DestroySdkObjects(lSdkManager);
    return 0;
}
bool CreateScene(KFbxScene* pScene)
{
    KFbxVector4 lT, lR, lS;
    KFbxXMatrix lGM;
    
    KFbxNode* pNodeA = KFbxNode::Create(pScene,"A");
    KFbxNode* pNodeB = KFbxNode::Create(pScene,"B");
    KFbxNode* pNodeC = KFbxNode::Create(pScene,"C");
    KFbxNode* pNodeD = KFbxNode::Create(pScene,"D");
    
    KFbxSkeleton* lSkeletonA = KFbxSkeleton::Create(pScene,"");
    lSkeletonA->SetSkeletonType(KFbxSkeleton::eROOT);
    pNodeA->SetNodeAttribute(lSkeletonA);
    KFbxSkeleton* lSkeletonB = KFbxSkeleton::Create(pScene,"");
    lSkeletonB->SetSkeletonType(KFbxSkeleton::eLIMB_NODE);
    pNodeB->SetNodeAttribute(lSkeletonB);
    KFbxSkeleton* lSkeletonC = KFbxSkeleton::Create(pScene,"");
    lSkeletonC->SetSkeletonType(KFbxSkeleton::eLIMB_NODE);
    pNodeC->SetNodeAttribute(lSkeletonC);
    KFbxSkeleton* lSkeletonD = KFbxSkeleton::Create(pScene,"");
    lSkeletonD->SetSkeletonType(KFbxSkeleton::eLIMB_NODE);
    pNodeD->SetNodeAttribute(lSkeletonD);
    
    pNodeA->GetLimits().SetTranslationLimitActive(true);
    pNodeA->GetLimits().mTranslationLimits.SetLimitMinActive(true, true, true);
    pNodeA->GetLimits().mTranslationLimits.SetLimitMin(KFbxVector4(0.1, 0.2, 0.3));
    pNodeA->GetLimits().mTranslationLimits.SetLimitMaxActive(true, true, true);
    pNodeA->GetLimits().mTranslationLimits.SetLimitMax(KFbxVector4(5.0, 1.0, 0.0));
    
    
    pNodeB->SetRotationActive(true);
    
    
    
    pNodeB->GetLimits().mRotationLimits.SetLimitMaxActive(true, false, false);
    pNodeB->GetLimits().mRotationLimits.SetLimitMax(KFbxVector4(33.3, 0.0, 0.0));
    pNodeB->SetPivotState(KFbxNode::eSOURCE_SET, KFbxNode::ePIVOT_STATE_ACTIVE);
    pNodeB->SetRotationOrder(KFbxNode::eSOURCE_SET, eSPHERIC_XYZ);
    pNodeB->SetUseRotationSpaceForLimitOnly(KFbxNode::eSOURCE_SET, false);
    pNodeB->SetQuaternionInterpolation(KFbxNode::eSOURCE_SET, eQUATINTERP_CLASSIC);
    pNodeB->SetRotationPivot(KFbxNode::eSOURCE_SET, KFbxVector4(11.1, 22.2, 33.3));
    pNodeB->SetPreRotation(KFbxNode::eSOURCE_SET, KFbxVector4(15.0, 30.0, 45.0));
    pNodeB->SetPostRotation(KFbxNode::eSOURCE_SET, KFbxVector4(-45.0, -30.0, -15.0));
    
    pScene->GetRootNode()->AddChild(pNodeA);
    pNodeA->AddChild(pNodeB);
    pNodeB->AddChild(pNodeC);
    pNodeC->AddChild(pNodeD);
    
    lT.Set(0.0, 0.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, 45.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeA, lGM);
    
    lT.Set(30.0, 20.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, 0.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeB, lGM);
    
    lT.Set(55.0, 20.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, -40.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeC, lGM);
    
    lT.Set(70.0, 10.0, 0.0); lGM.SetT(lT);
    lR.Set(0.0, 0.0, 0.0); lGM.SetR(lR);
    SetGlobalDefaultPosition(pNodeD, lGM);
    
    
    
    
    
    
    
    
    
    
    
    KFbxObjectMetaData* pFamilyMetaData = KFbxObjectMetaData::Create(pScene, "Family");
    KFbxProperty::Create(pFamilyMetaData, DTString, "Level", "Level").Set(KString("Family"));
    KFbxProperty::Create(pFamilyMetaData, DTString, "Type", "Type").Set(KString("Wall"));
    KFbxProperty::Create(pFamilyMetaData, DTFloat, "Width", "Width").Set(10.0f);
    KFbxProperty::Create(pFamilyMetaData, DTDouble, "Weight", "Weight").Set(25.0);
    KFbxProperty::Create(pFamilyMetaData, DTDouble, "Cost", "Cost").Set(1.25);
    KFbxObjectMetaData* pTypeMetaData = KFbxCast<KFbxObjectMetaData>(pFamilyMetaData->Clone(KFbxObject::eREFERENCE_CLONE, pScene));
    pTypeMetaData->SetName("Type");
    
    pTypeMetaData->FindProperty("Cost").Set(2500.0);
    pTypeMetaData->FindProperty("Level").Set(KString("Type"));
    KFbxObjectMetaData* pInstanceMetaData = KFbxCast<KFbxObjectMetaData>(pTypeMetaData->Clone(KFbxObject::eREFERENCE_CLONE, pScene));
    pInstanceMetaData->SetName("Instance");
    
    KFbxProperty::Create(pInstanceMetaData, DTString, "Sku", "Sku#").Set(KString("143914-10"));
    pInstanceMetaData->FindProperty("Width").Set(1100.50f);
    pInstanceMetaData->FindProperty("Type").Set(KString("Super Heavy Duty Wall"));
    pInstanceMetaData->FindProperty("Level").Set(KString("Instance"));
    
    pNodeA->ConnectSrcObject(pInstanceMetaData);
    pNodeC->ConnectSrcObject(pInstanceMetaData);    
    pNodeD->ConnectSrcObject(pTypeMetaData);
    return true;
}
void SetGlobalDefaultPosition(KFbxNode* pNode, KFbxXMatrix pGlobalPosition)
{
    KFbxXMatrix lLocalPosition;
    KFbxXMatrix lParentGlobalPosition;
    if (pNode->GetParent())
    {
        lParentGlobalPosition = GetGlobalDefaultPosition(pNode->GetParent());
        lLocalPosition = lParentGlobalPosition.Inverse() * pGlobalPosition;
    }
    else
    {
        lLocalPosition = pGlobalPosition;
    }
    pNode->LclTranslation.Set(lLocalPosition.GetT());
    pNode->LclRotation.Set(lLocalPosition.GetR());
    pNode->LclScaling.Set(lLocalPosition.GetS());
}
KFbxXMatrix GetGlobalDefaultPosition(KFbxNode* pNode)
{
    KFbxXMatrix lLocalPosition;
    KFbxXMatrix lGlobalPosition;
    KFbxXMatrix lParentGlobalPosition;
    lLocalPosition.SetT(pNode->LclTranslation.Get());
    lLocalPosition.SetR(pNode->LclRotation.Get());
    lLocalPosition.SetS(pNode->LclScaling.Get());
    if (pNode->GetParent())
    {
        lParentGlobalPosition = GetGlobalDefaultPosition(pNode->GetParent());
        lGlobalPosition = lParentGlobalPosition * lLocalPosition;
    }
    else
    {
        lGlobalPosition = lLocalPosition;
    }
    return lGlobalPosition;
}