//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#pragma pack(push,8)
#include <MAX.H>
#pragma pack(pop)

// NOTE:
// if you have problems linking this exporter it is likely you are encountering a
// bug in Microsofts include\basetsd.h header.
//
// The problem is that Visual C++ defines INT_PTR to 'long' when it is
// supposed to be defined as an 'int' (on ia32 platforms). You can either
// use a supported build environment by updating to the platform SDK, or
// you can use the unsupported environment by manually fixing the problem
// in the header "On or around line 123 of include\basetsd.h change: 
//
//    typedef long INT_PTR, *PINT_PTR; 
//    typedef unsigned long UINT_PTR, *PUINT_PTR; 
// TO 
//    typedef int INT_PTR, *PINT_PTR; 
//    typedef unsigned int UINT_PTR, *PUINT_PTR;

#include "max2dtsExporter/exporter.h"
#include "max2dtsExporter/sceneEnum.h"
#include "core/filestream.h"
#include "ts/tsShapeInstance.h"
#include "max2dtsExporter/sequence.h"
#include "max2dtsExporter/exportUtil.h"
#include "max2dtsExporter/skinHelper.h"


#define DLLEXPORT __declspec(dllexport)


#if defined(TORQUE_DEBUG)
   const char * const gProgramVersion = "0.900d-beta";
#else
   const char * const gProgramVersion = "0.900r-beta";
#endif

HINSTANCE hInstance;

S32 gSupressUI = 0;
TCHAR maxFile[1024];
S32 controlsInit = FALSE;
char dllPath[256];
char globalConfig[270];

TCHAR *GetString(S32 id)
{
    static TCHAR buf[256];

    if (hInstance)
    {
        return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;
    }

    return NULL;
}
const char * days[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
const char * months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };

static S32 Alert(const char * s1, const char * s2, S32 option = MB_OK)
{
   if (gSupressUI)
   {
      FileStream fs;
      fs.open(avar("%smax2difExport.error.log",dllPath),FileStream::ReadWrite);
      if (fs.getStatus() != Stream::Ok)
      {
         fs.close();
         return -1;
      }
      fs.setPosition(fs.getStreamSize());

      // what's the time and date?
      Platform::LocalTime localTime;
      Platform::getLocalTime(localTime);
      char timeBuffer[256];
      dStrcpy(timeBuffer,avar("%i:%i on %s %s %i",localTime.hour,localTime.min,days[localTime.weekday],months[localTime.month],localTime.monthday));

      // write the error to the log file
      char buffer[1024];
      dStrcpy(buffer,avar("Error exporting file \"%s\" at %s:\r\n  --> %s\r\n\r\n",maxFile,timeBuffer,s1));
      fs.writeLine((unsigned char*) buffer);
      fs.close();
      return -1;
   }
   else
   {
      TSTR str1(s1);
      TSTR str2(s2);
      return MessageBox(GetActiveWindow(), str1, str2, option);
   }
}

static S32 Alert(S32 s1, S32 s2 = IDS_TH_DTSEXP, S32 option = MB_OK)
{
   return Alert(GetString(s1),GetString(s2),option);
}

static S32 Alert(const char * s1, S32 s2 = IDS_TH_DTSEXP, S32 option = MB_OK)
{
   return Alert(s1,GetString(s2),option);
}

const char * GetVersionString()
{
   static char versionString[128];
   dStrcpy(versionString,avar("Version %s (dts v%i.%.2i)",gProgramVersion, DTS_EXPORTER_CURRENT_VERSION/100, DTS_EXPORTER_CURRENT_VERSION%100));
   return versionString;
}

//------------------------------------------------------
// Our main plugin class:

class _Exporter : public SceneExport
{
    char extension[10]; // 'DTS' or 'DSQ'

    public:
         _Exporter(const char *);
        ~_Exporter();
        
        S32           ExtCount();         // Number of extensions supported
        const TCHAR  *Ext(S32);           // Extension #n
        const TCHAR  *LongDesc();         // Long ASCII description
        const TCHAR  *ShortDesc();        // Short ASCII description
        const TCHAR  *AuthorName();       // ASCII Author name
        const TCHAR  *CopyrightMessage(); // ASCII Copyright message
        const TCHAR  *OtherMessage1();    // Other message #1
        const TCHAR  *OtherMessage2();    // Other message #2
        U32           Version();          // Version number * 100
        void          ShowAbout(HWND);    // Show DLL's "About..." box

        S32 DoExport(const TCHAR *, ExpInterface *, Interface *, int, DWORD);
};

//------------------------------------------------------
// Jaguar interface code

extern void mInstallLibrary_C();
namespace Memory
{
extern bool gAlwaysLogLeaks;
};

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) 
{
    hInstance = hinstDLL;

    if (!controlsInit) 
    {
        controlsInit = TRUE;
        
        // 3DS custom controls
        InitCustomControls(hInstance);

        // Initialize common Windows controls
        InitCommonControls();

        // install math library (set up function pointers)
        mInstallLibrary_C();

        // set "factory" default parameter values
        SceneEnumProc::setInitialDefaults();
        
        // always log memory leaks -- some of them aren't really leaks,
        // but we want to see them and we don't want the annoying box asking us
        // about it...
        Memory::gAlwaysLogLeaks = true;

        // get DLL path
        GetModuleFileName(hInstance,dllPath,sizeof(dllPath));
        char * p  = dStrrchr(dllPath,'\\');
        char * p2 = dStrrchr(dllPath,':');
        if (p && *p=='\\')
            *(p+1) = '\0';
        else if(p2 && *p2==':')
            *(p2+1) = '\0';
        else
            dllPath[0] = '\0';

        // load the global config file if we can find it
        dStrcpy(globalConfig,dllPath);
        dStrcpy(globalConfig+dStrlen(globalConfig),"dtsGlobal.cfg");
        SceneEnumProc::readConfigFile(globalConfig);
    }
    
    switch(fdwReason) 
    {
        case DLL_PROCESS_ATTACH:
            break;
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
        case DLL_PROCESS_DETACH:
            break;
    }

    return(TRUE);
}


// defines for setting exporter type and description
#define  _EXPORTER_CLASS_NAME    "Torque Shape Exporter"
#define  _DTS_EXPORTER_CLASS_ID  Class_ID(0x296a4787, 0x2ec557fc) 
#define  _DSQ_EXPORTER_CLASS_ID  Class_ID(0x65d74e76, 0x10da4a97) 
#define  _TXT_EXPORTER_CLASS_ID  Class_ID(0x6bfb02d7, 0x13860666) 
#define  _EXPORTER_CLASS_SDESC   "Torque Shape Exporter"

// more code for interfacing with max
class _DTSClassDesc : public ClassDesc 
{
    public:
        S32         IsPublic() { return 1; }
        void        *Create(BOOL loading = FALSE) { return new _Exporter("DTS"); }
        const TCHAR *ClassName() { return _EXPORTER_CLASS_NAME; }
        SClass_ID   SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
        Class_ID    ClassID() { return _DTS_EXPORTER_CLASS_ID; }
        const TCHAR *Category() { return GetString(IDS_TH_SHAPEEXPORT);  }
};

// more code for interfacing with max
class _DSQClassDesc : public ClassDesc 
{
    public:
        S32         IsPublic() { return 1; }
        void        *Create(BOOL loading = FALSE) { return new _Exporter("DSQ"); }
        const TCHAR *ClassName() { return _EXPORTER_CLASS_NAME; }
        SClass_ID   SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
        Class_ID    ClassID() { return _DSQ_EXPORTER_CLASS_ID; }
        const TCHAR *Category() { return GetString(IDS_TH_SHAPEEXPORT);  }
};

// more code for interfacing with max
class _TXTClassDesc : public ClassDesc 
{
    public:
        S32         IsPublic() { return 1; }
        void        *Create(BOOL loading = FALSE) { return new _Exporter("TXT"); }
        const TCHAR *ClassName() { return _EXPORTER_CLASS_NAME; }
        SClass_ID   SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
        Class_ID    ClassID() { return _TXT_EXPORTER_CLASS_ID; }
        const TCHAR *Category() { return GetString(IDS_TH_SHAPEEXPORT);  }
};

static _DTSClassDesc _DTSDesc;
static _DSQClassDesc _DSQDesc;
static _TXTClassDesc _TXTDesc;

DLLEXPORT const TCHAR *LibDescription() 
{ 
    return GetString(IDS_TH_DTSEXPORTDLL); 
}

DLLEXPORT S32 LibNumberClasses() 
{ 
    return 6;
}

DLLEXPORT ClassDesc *LibClassDesc(S32 i) 
{
    switch(i) 
    {
        case 0: 
            return &_DTSDesc; 

        case 1: 
            return &_DSQDesc; 

        case 2:
            return &_TXTDesc;

        case 3:
            return GetSequenceDesc();

        case 4:
            return GetExportUtilDesc();

        case 5:
            return GetSkinHelperDesc();

        default: 
            return NULL;
    }
}

// Return version so can detect obsolete DLLs
DLLEXPORT ULONG LibVersion() 
{ 
    return VERSION_3DSMAX; 
}

//
// ._DTS export module functions follow:
//

_Exporter::_Exporter(const char * _extension) 
{
    dStrcpy(extension,_extension);
}

_Exporter::~_Exporter() 
{
}

S32 _Exporter::ExtCount() 
{
    return 1;
}

// Extensions supported for import/export modules
const TCHAR *_Exporter::Ext(S32 n) 
{
    switch(n) 
    {
        case 0:
            return _T(extension);

        default:
            return _T("");
    }
}

// Long ASCII description (e.g., "Targa 2.0 Image File")
const TCHAR *_Exporter::LongDesc() 
{
    return GetString(IDS_TH_DTSFILE_LONG);
}
    
// Short ASCII description (e.g., "Targa")
const TCHAR *_Exporter::ShortDesc() 
{
    return _EXPORTER_CLASS_SDESC;
}

// ASCII Author name
const TCHAR *_Exporter::AuthorName() 
{
    return GetString(IDS_TH_AUTHOR);
}

// ASCII Copyright message
const TCHAR *_Exporter::CopyrightMessage() 
{
    return GetString(IDS_TH_COPYRIGHT_COMPANY);
}

// Other message #1
const TCHAR *_Exporter::OtherMessage1() 
{
    return _T("");
}

// Other message #2
const TCHAR *_Exporter::OtherMessage2() 
{
    return _T("");
}

// Version number * 100 (i.e. v3.01 = 301)
U32 _Exporter::Version() 
{
    return DTS_EXPORTER_CURRENT_VERSION;
}

// Optional
void _Exporter::ShowAbout(HWND hWnd) 
{
}

//------------------------------------------------------

// be more thourough about this later...
U32  saveDumpMask;
bool saveEnableSequences;
bool saveCollapse;
bool saveAllowEmptySubtrees;
bool saveAllowCrossedDetails;
bool saveAllowUnusedMeshes;
bool saveViconNeeded;
bool saveAllowOldSequences;
F32  saveAnimationDelta;
F32  saveMaxFrameRate;
S32  saveT2AutoDetail;

void saveConfig()
{
    saveDumpMask = dumpMask;
    saveEnableSequences = enableSequences;
    saveCollapse = transformCollapse;
    saveAllowEmptySubtrees = allowEmptySubtrees;
    saveAllowCrossedDetails = allowCrossedDetails;
    saveAllowUnusedMeshes = allowUnusedMeshes;
    saveViconNeeded = viconNeeded;
    saveAllowOldSequences = allowOldSequences;
    saveAnimationDelta = animationDelta;
    saveMaxFrameRate = maxFrameRate;
    saveT2AutoDetail = t2AutoDetail;
}

void restoreConfig()
{
    dumpMask = saveDumpMask;
    enableSequences = saveEnableSequences;
    transformCollapse = saveCollapse;
    allowEmptySubtrees = saveAllowEmptySubtrees;
    allowCrossedDetails = saveAllowCrossedDetails;
    allowUnusedMeshes = saveAllowUnusedMeshes;
    viconNeeded = saveViconNeeded;
    allowOldSequences = saveAllowOldSequences;
    animationDelta = saveAnimationDelta;
    maxFrameRate = saveMaxFrameRate;
    t2AutoDetail = saveT2AutoDetail;
}

S32 cheapSemaphore = 0;

S32 _dts_save(const TCHAR *fname, ExpInterface *ei, Interface *gi) 
{
   if (cheapSemaphore)
   {
       Alert("One export at a time:  Export code not re-entrant.");
       return 1;
   }
   cheapSemaphore = 1;

   char filename[1024];
   dStrcpy(filename,fname);
   char * ch = filename;
   while (*ch!='\0')
   {
      *ch = tolower(*ch);
      ch++;
   }

   SceneEnumProc myScene;
   dStrcpy(maxFile,gi->GetCurFilePath());

   //-----------------------------------------------
   // read in the config file...
   char configFilename[256];
   dStrcpy(configFilename,maxFile);
   char * p  = dStrrchr(configFilename,'\\');
   char * p2 = dStrrchr(configFilename,':');
   if (p && *p=='\\')
      dStrcpy(p+1,"*.cfg");
   else if (p2 && *p2==':')
      dStrcpy(p2+1,"*.cfg");
   else
      dStrcpy(configFilename,"*.cfg");
   const char * error = myScene.readConfigFile(configFilename);
   if (error)
      myScene.setExportError(error);

   //-----------------------------------------------
   // Error?
   if (myScene.isExportError())
   {
       Alert(myScene.getExportError());
       cheapSemaphore = 0;
       return 1;
   }

   //-----------------------------------------------
   // create dump file...
   myScene.startDump(filename,maxFile);

   //-----------------------------------------------
   // read config file again so we have a record of it...
   myScene.readConfigFile(configFilename);

   //-----------------------------------------------
   // Error?
   if (myScene.isExportError())
   {
       Alert(myScene.getExportError());
       cheapSemaphore = 0;
       return 1;
   }

   //-----------------------------------------------
   // tweak some parameters depending on export type...
   if (dStrstr((const char*)filename,".dsq"))
   {
       // we're exporting sequences, so turn this on
       if (!enableSequences)
       {
           enableSequences = true;
           SceneEnumProc::printDump(PDAlways,"\r\nEnabling \"Param::SequenceExport\" (doing *.dsq export).\r\n");
       }
       SceneEnumProc::exportType = 's'; // sequence
   }
   if (dStrstr((const char*)filename,".txt"))
   {
       // doing a text file dump -- export everything
       if (!enableSequences)
       {
           enableSequences = true;
           SceneEnumProc::printDump(PDAlways,"\r\nEnabling \"Param::SequenceExport\" (doing *.txt export).\r\n");
        
       }
       if (transformCollapse)
       {
           transformCollapse = false;
           SceneEnumProc::printDump(PDAlways,"\r\nDisabling \"Param::CollapseTransforms\" (doing *.txt export).\r\n");
       }
       SceneEnumProc::exportType = 't'; // text
   }
   if (dStrstr((const char*)filename,".dts"))
      SceneEnumProc::exportType = 'w'; // whole shape

   //-----------------------------------------------
   // Error?
   if (myScene.isExportError())
   {
       Alert(myScene.getExportError());
       cheapSemaphore = 0;
       return 1;
   }

   //-----------------------------------------------
   // Get the nodes we're interested in!
   // We also do some checking to make sure everything
   // we need is present ... if something is missing,
   // an error will be returned.
   myScene.enumScene(ei->theScene);

   //-----------------------------------------------
   // Error?
   if (myScene.isExportError())
   {
       Alert(myScene.getExportError());
       cheapSemaphore = 0;
       return 1;
   }
    
   //-----------------------------------------------
   // Any useful nodes?
   if (myScene.isEmpty()) 
   {
       Alert("No data to export");
       cheapSemaphore = 0;
       return 1;
   }

   //-----------------------------------------------
   // make sure we get rid of target file before opening it
   // otherwise, it'll never shrink
   File zap;
   zap.open(filename,File::Write);
   zap.close();

   //-----------------------------------------------
   // open a file to save the exported shape to:
   FileStream file;
   file.open(filename,FileStream::Write);
   if( file.getStatus() != Stream::Ok )
   {
      Alert(IDS_TH_CANTCREATE);
      cheapSemaphore = 0;
      return(0);
   }

   //-----------------------------------------------
   // actually do the export:
   myScene.processScene();

   //-----------------------------------------------
   // Error?
   if (myScene.isExportError())
   {
       Alert(myScene.getExportError());
       file.close();
       cheapSemaphore = 0;
       return 1;
   }

   //-----------------------------------------------
   //  now save the shape
   TSShape * pShape = myScene.getShape();

   if (dStrstr((const char*)filename,".dts"))
       pShape->write(&file);
   else if (dStrstr((const char*)filename,".dsq"))
       pShape->exportSequences(&file);
   else if (dStrstr((const char*)filename,".txt"))
       myScene.exportTextFile(&file);

   //-----------------------------------------------
   // close the file and report any problems:
    
   if( file.getStatus() != Stream::Ok )
   {
      Alert(IDS_TH_WRITEERROR);
      file.close();
      remove(filename);
      cheapSemaphore = 0;
      return(0);
   }
   else
      file.close();

   //-----------------------------------------------
   // write the shape out to dump file
   if (PDShapeHierarchy & dumpMask && dStrstr((const char*)filename,".dts"))
   {
      // write out the structure of the newly created shape
      // but read it from the file first...
      if (!SceneEnumProc::dumpShape(filename))
      {
          Alert(avar("Error opening created file \"%s\".",filename));
          file.close();
          cheapSemaphore = 0;
          return 1;
      }
   }

   cheapSemaphore = 0;
   return 1;    
}

S32 _Exporter::DoExport(const TCHAR *filename,ExpInterface *ei,Interface *gi, S32 supressUI, DWORD)
{
   gSupressUI = supressUI;

   // so we can go back to defaults when we're done
   saveConfig();

   S32 status;
        
   status = _dts_save(filename, ei, gi);

   restoreConfig();

   if(status == 0)
      return 1;        // Dialog cancelled

   if(status < 0)
      return 0;        // Real, honest-to-goodness error

   return status;
}