/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/

#ifndef plMaxNodeData_inc
#define plMaxNodeData_inc

#include "HeadSpin.h"
#include "hsTemplates.h"
#include "hsColorRGBA.h"
#include "plRenderLevel.h"
#include "plPhysicalProps.h"
#include "hsBitVector.h"
#include "pnKeyedObject/plKey.h"
#include "plLoadMask.h"

class plMaxNodeBase;
class plMaxNode;
class plLocation;
class plSceneObject;
class hsGMesh;
class plGeometrySpan;
class plMaxBoneMap;
class plSharedMesh;

class plMaxNodeBaseTab : public Tab<plMaxNodeBase*>
{
};


//
//      Created hoping to trim some fat so to speak for all the bools that
//          we are tossing around.
//

class DataBF
{
public:
    enum DatBit
    {
        kCanConvert     = 0x0,
        kForceLocal,
        kDrawable,          // Absence of this prop forbids making this drawable
        kBlendToFB,
        kItinerant,
        kRunTimeLight,
        kMovable,
        kIsBarney,
        kForceMatCopy,
        kNoPreShade,
        kIsInstanced,
        kParticleRelated,
        kNoSpanReSort,
        kHasFade,
        kNoFaceSort,
        kNoSpanSort,
        kNoDeferDraw,
        kBLevelSet,
        kOLevelSet,
        kDup2Sided,
        kRadiateNorms,
        kGeoDice,
        kForcePreShade,
        kIsGUI,
        kSwappableGeomTarget,
        kNoShadow,
        kForceShadow,
        kFilterInherit,
        kReverseSort,
        kVS,
        kConcave,
        kCalcEdgeLens,
        kEnviron,
        kEnvironOnly,
        kPhysical,          // Absence of this prop forbids making this physical
        kSmoothAll,
        kForceSortable,
        kOverrideHighLevelSDL,
        kDisableNormal,
        kHasLoadMask,
        kWaterDecEnv,
        kWaterHeight,
        kAlphaTestHigh,
        kForceMatShade,
        kUnBounded,
        kForceVisLOS,
        kSortAsOpaque,
    };

    hsBitVector*    fBitVector;

    DataBF()                                            
    { 
        fBitVector = new hsBitVector;   
        fBitVector->SetBit(kDrawable); 
        fBitVector->SetBit(kPhysical); 
    }
    virtual ~DataBF()                                   { delete fBitVector; }

    DataBF& operator=(const DataBF& ot)
    {
        *fBitVector = *ot.fBitVector;
        return *this;
    }
    DataBF(const DataBF& ot)
    {
        fBitVector = new hsBitVector;
        *fBitVector = *ot.fBitVector;
    }

    void Init()                                         
    { 
        fBitVector = new hsBitVector; 
        fBitVector->SetBit(kDrawable); 
        fBitVector->SetBit(kPhysical); 
    }
    void DeInit()                                       { delete fBitVector; fBitVector = nil; }

    hsBool  CanBF(DatBit bitChoice)                 { return fBitVector->IsBitSet(bitChoice);   }
    void    SetBF(hsBool b, DatBit bitChoice)       { fBitVector->SetBit(bitChoice, b); }
};


class plMaxNodeData
{
public:
    plMaxNodeData() : 
        fpKey(nil), 
        fpSO(nil) , 
        fDecalLevel(0),
        fpMesh(nil), 
        fpRoomKey(nil), 
        fSoundIdxCounter( 0 ), 
        fAvatarSO(nil), 
        fFade(Point3(0,0,0), Point3(0,0,0)),
        fNormalChan(0),
        fWaterHeight(0),
        fGDMaxFaces(0), fGDMaxSize(0), fGDMinFaces(0),
        fSwapMesh(nil),
        fSwapTargetID((uint32_t)-1),
        fCachedAlphaHackLayerCounts(nil),
        fBoneMap(nil),
        fAnimCompression(1), // Should be plAnimCompressComp::kCompressionLow,
                             // but I don't want to include the entire header.
        fKeyReduceThreshold(0.0002)
    { }
    ~plMaxNodeData() 
    { 
        fpKey = nil; 
        fpRoomKey = nil;
        fpSO = 0; 
        fAvatarSO = nil; 
        delete fCachedAlphaHackLayerCounts; 
        MaxDatBF.DeInit();
    }

    // Call init on MaxNodeData whose constructor was never called, e.g. if it was malloc'd.
    plMaxNodeData&  Init() 
    {
        memset( &fpKey, 0, sizeof( plKey ) ); 
        memset( &fpRoomKey, 0, sizeof( plKey ) ); 
        fRenderDependencies.Init(); 
        fBones.Init(); 
        fCachedAlphaHackLayerCounts = nil; 
        MaxDatBF.Init();
        return *this; 
    }
    // Ditto
    void            DeInit( void ) 
    { 
        fpKey = nil; 
        fpRoomKey = nil;
        fpSO = 0; fAvatarSO = nil; 
        delete fCachedAlphaHackLayerCounts; 
        fCachedAlphaHackLayerCounts = nil; 
        MaxDatBF.DeInit();
    }

    plKey           GetKey()                            { return fpKey;     }
    void            SetKey(plKey p )                    { fpKey = p;        }
    plSceneObject * GetSceneObject()                    { return fpSO;      }
    void            SetSceneObject(plSceneObject *p)    { fpSO = p;         }

    hsBool          CanConvert()                        { return MaxDatBF.CanBF(MaxDatBF.kCanConvert);  }
    void            SetCanConvert(hsBool b)             { MaxDatBF.SetBF(b, MaxDatBF.kCanConvert);      }

    hsBool          GetForceLocal()                     { return MaxDatBF.CanBF(MaxDatBF.kForceLocal);  }
    void            SetForceLocal(hsBool b)             { MaxDatBF.SetBF(b, MaxDatBF.kForceLocal);      }

    void *          GetMesh()                           { return fpMesh;    }   // void pointer for header simplicity
    void            SetMesh(hsGMesh *p)                 { fpMesh = p;       }       

    hsBool          GetDrawable()                       {return MaxDatBF.CanBF(MaxDatBF.kDrawable);     }
    void            SetDrawable(hsBool b)               { MaxDatBF.SetBF(b, MaxDatBF.kDrawable);        }

    hsBool          GetPhysical()                       {return MaxDatBF.CanBF(MaxDatBF.kPhysical);     }
    void            SetPhysical(hsBool b)               { MaxDatBF.SetBF(b, MaxDatBF.kPhysical);        }

    hsBool          GetRunTimeLight()                   {return MaxDatBF.CanBF(MaxDatBF.kRunTimeLight); }
    void            SetRunTimeLight(hsBool b)           { MaxDatBF.SetBF(b, MaxDatBF.kRunTimeLight);    }

    hsBool          GetForceMatShade()                  {return MaxDatBF.CanBF(MaxDatBF.kForceMatShade);    }
    void            SetForceMatShade(hsBool b)          { MaxDatBF.SetBF(b, MaxDatBF.kForceMatShade);   }

    hsBool          GetForceVisLOS()                    {return MaxDatBF.CanBF(MaxDatBF.kForceVisLOS);  }
    void            SetForceVisLOS(hsBool b)            { MaxDatBF.SetBF(b, MaxDatBF.kForceVisLOS); }

    hsBool          GetEnviron()                        {return MaxDatBF.CanBF(MaxDatBF.kEnviron);  }
    void            SetEnviron(hsBool b)                { MaxDatBF.SetBF(b, MaxDatBF.kEnviron); }

    hsBool          GetEnvironOnly()                    {return MaxDatBF.CanBF(MaxDatBF.kEnvironOnly);  }
    void            SetEnvironOnly(hsBool b)            { MaxDatBF.SetBF(b, MaxDatBF.kEnvironOnly); }

    hsBool          GetWaterDecEnv()                    { return MaxDatBF.CanBF(MaxDatBF.kWaterDecEnv); }
    void            SetWaterDecEnv(hsBool b)            { MaxDatBF.SetBF(b, MaxDatBF.kWaterDecEnv);     }

    hsBool          GetItinerant()                      {return MaxDatBF.CanBF(MaxDatBF.kItinerant);    }
    void            SetItinerant(hsBool b)              { MaxDatBF.SetBF(b, MaxDatBF.kItinerant);       }

    hsBool          GetUnBounded()                      {return MaxDatBF.CanBF(MaxDatBF.kUnBounded);    }
    void            SetUnBounded(hsBool b)              { MaxDatBF.SetBF(b, MaxDatBF.kUnBounded);       }

    hsBool          GetDisableNormal()                  {return MaxDatBF.CanBF(MaxDatBF.kDisableNormal);    }
    void            SetDisableNormal(hsBool b)          { MaxDatBF.SetBF(b, MaxDatBF.kDisableNormal);       }

    hsBool          GetMovable()                        {return MaxDatBF.CanBF(MaxDatBF.kMovable);      }
    void            SetMovable(hsBool b)                { MaxDatBF.SetBF(b, MaxDatBF.kMovable);         }

    hsBool          GetNoPreShade()                     {return MaxDatBF.CanBF(MaxDatBF.kNoPreShade);   }
    void            SetNoPreShade(hsBool b)             { MaxDatBF.SetBF(b, MaxDatBF.kNoPreShade);      }

    hsBool          GetForcePreShade()                  {return MaxDatBF.CanBF(MaxDatBF.kForcePreShade);    }
    void            SetForcePreShade(hsBool b)          { MaxDatBF.SetBF(b, MaxDatBF.kForcePreShade);       }

    hsBool          GetNoShadow()                       {return MaxDatBF.CanBF(MaxDatBF.kNoShadow);     }
    void            SetNoShadow(hsBool b)               { MaxDatBF.SetBF(b, MaxDatBF.kNoShadow);        }

    hsBool          GetForceShadow()                    {return MaxDatBF.CanBF(MaxDatBF.kForceShadow);  }
    void            SetForceShadow(hsBool b)            { MaxDatBF.SetBF(b, MaxDatBF.kForceShadow);     }

    hsBool          GetAlphaTestHigh()                  {return MaxDatBF.CanBF(MaxDatBF.kAlphaTestHigh);    }
    void            SetAlphaTestHigh(hsBool b)          { MaxDatBF.SetBF(b, MaxDatBF.kAlphaTestHigh);       }

    hsBool          GetFilterInherit()                  {return MaxDatBF.CanBF(MaxDatBF.kFilterInherit);    }
    void            SetFilterInherit(hsBool b)          { MaxDatBF.SetBF(b, MaxDatBF.kFilterInherit);       }

    hsBool          GetIsBarney()                       {return MaxDatBF.CanBF(MaxDatBF.kIsBarney);     }
    void            SetIsBarney(hsBool b)               { MaxDatBF.SetBF(b, MaxDatBF.kIsBarney);        }

    hsBool          GetNoSpanSort()                     {return MaxDatBF.CanBF(MaxDatBF.kNoSpanSort);   }
    void            SetNoSpanSort(hsBool b)             { MaxDatBF.SetBF(b, MaxDatBF.kNoSpanSort);      }

    hsBool          GetNoSpanReSort()                   {return MaxDatBF.CanBF(MaxDatBF.kNoSpanReSort); }
    void            SetNoSpanReSort(hsBool b)           { MaxDatBF.SetBF(b, MaxDatBF.kNoSpanReSort);    }

    hsBool          GetNoFaceSort()                     {return MaxDatBF.CanBF(MaxDatBF.kNoFaceSort);   }
    void            SetNoFaceSort(hsBool b)             { MaxDatBF.SetBF(b, MaxDatBF.kNoFaceSort);      }

    hsBool          GetReverseSort()                    {return MaxDatBF.CanBF(MaxDatBF.kReverseSort);  }
    void            SetReverseSort(hsBool b)            { MaxDatBF.SetBF(b, MaxDatBF.kReverseSort);     }

    hsBool          GetSortAsOpaque()                   {return MaxDatBF.CanBF(MaxDatBF.kSortAsOpaque); }
    void            SetSortAsOpaque(hsBool b)           { MaxDatBF.SetBF(b, MaxDatBF.kSortAsOpaque);        }

    hsBool          GetVS()                             {return MaxDatBF.CanBF(MaxDatBF.kVS);   }
    void            SetVS(hsBool b)                     { MaxDatBF.SetBF(b, MaxDatBF.kVS);      }

    hsBool          GetHasWaterHeight()                 { return MaxDatBF.CanBF(MaxDatBF.kWaterHeight); }
    void            SetHasWaterHeight(hsBool b)         { MaxDatBF.SetBF(b, MaxDatBF.kWaterHeight); }
    float        GetWaterHeight()                    { return fWaterHeight; }
    void            SetWaterHeight(float f)          { SetHasWaterHeight(true); fWaterHeight = f; }

    hsBool          GetSmoothAll()                      {return MaxDatBF.CanBF(MaxDatBF.kSmoothAll);    }
    void            SetSmoothAll(hsBool b)              { MaxDatBF.SetBF(b, MaxDatBF.kSmoothAll);       }

    hsBool          GetForceSortable()                  {return MaxDatBF.CanBF(MaxDatBF.kForceSortable);    }
    void            SetForceSortable(hsBool b)          { MaxDatBF.SetBF(b, MaxDatBF.kForceSortable);       }

    hsBool          GetConcave()                        {return MaxDatBF.CanBF(MaxDatBF.kConcave);  }
    void            SetConcave(hsBool b)                { MaxDatBF.SetBF(b, MaxDatBF.kConcave);     }

    hsBool          GetCalcEdgeLens()                   {return MaxDatBF.CanBF(MaxDatBF.kCalcEdgeLens); }
    void            SetCalcEdgeLens(hsBool b)           { MaxDatBF.SetBF(b, MaxDatBF.kCalcEdgeLens);    }

    hsBool          GetNoDeferDraw()                    {return MaxDatBF.CanBF(MaxDatBF.kNoDeferDraw);  }
    void            SetNoDeferDraw(hsBool b)            { MaxDatBF.SetBF(b, MaxDatBF.kNoDeferDraw);     }

    hsBool          GetBlendToFB()                      {return MaxDatBF.CanBF(MaxDatBF.kBlendToFB);    }
    void            SetBlendToFB(hsBool b)              { MaxDatBF.SetBF(b, MaxDatBF.kBlendToFB);       }

    hsBool          GetForceMaterialCopy()              {return MaxDatBF.CanBF(MaxDatBF.kForceMatCopy); }
    void            SetForceMaterialCopy(hsBool b)      { MaxDatBF.SetBF(b, MaxDatBF.kForceMatCopy);    }

    hsBool          GetInstanced()                      {return MaxDatBF.CanBF(MaxDatBF.kIsInstanced);  }
    void            SetInstanced(hsBool b)              { MaxDatBF.SetBF(b, MaxDatBF.kIsInstanced);     }

    hsBool          GetParticleRelated()                {return MaxDatBF.CanBF(MaxDatBF.kParticleRelated);  }
    void            SetParticleRelated(hsBool b)        { MaxDatBF.SetBF(b, MaxDatBF.kParticleRelated);     }

    hsBool          GetDup2Sided()                      {return MaxDatBF.CanBF(MaxDatBF.kDup2Sided);    }
    void            SetDup2Sided(hsBool b)              { MaxDatBF.SetBF(b, MaxDatBF.kDup2Sided);       }

    hsBool          GetRadiateNorms()                   {return MaxDatBF.CanBF(MaxDatBF.kRadiateNorms); }
    void            SetRadiateNorms(hsBool b)           { MaxDatBF.SetBF(b, MaxDatBF.kRadiateNorms);    }

    hsBool          GetGeoDice()                        {return MaxDatBF.CanBF(MaxDatBF.kGeoDice);      }
    void            SetGeoDice(hsBool b)                { MaxDatBF.SetBF(b, MaxDatBF.kGeoDice);         }

    hsBool          GetIsGUI()                          {return MaxDatBF.CanBF(MaxDatBF.kIsGUI);        }
    void            SetIsGUI(hsBool b)                  { MaxDatBF.SetBF(b, MaxDatBF.kIsGUI);           }

    plSharedMesh*   GetSwappableGeom()                  { return fSwapMesh;}
    void            SetSwappableGeom(plSharedMesh *sm)  { fSwapMesh = sm;   }

    uint32_t          GetSwappableGeomTarget()            { return fSwapTargetID;}
    void            SetSwappableGeomTarget(uint32_t id)   { fSwapTargetID = id;}

    plMaxBoneMap*   GetBoneMap()                        { return fBoneMap;}
    void            SetBoneMap(plMaxBoneMap *bones)     { fBoneMap = bones;}

    int             GetGeoDiceMaxFaces()                { return fGDMaxFaces; }
    void            SetGeoDiceMaxFaces(int f)           { fGDMaxFaces = f; }

    float           GetGeoDiceMaxSize()                 { return fGDMaxSize; }
    void            SetGeoDiceMaxSize(float f)          { fGDMaxSize = f; }

    int             GetGeoDiceMinFaces()                { return fGDMinFaces; }
    void            SetGeoDiceMinFaces(int f)           { fGDMinFaces = f; }

    plKey           GetRoomKey()                        { return fpRoomKey; }
    void            SetRoomKey(plKey p)                 { fpRoomKey = p; }
    
    uint32_t          GetDecalLevel()                     { return fDecalLevel; }
    void            SetDecalLevel(uint32_t i)             { fDecalLevel = i; }

    uint32_t          GetSoundIdxCounter()                { return fSoundIdxCounter; }
    void            SetSoundIdxCounter( uint32_t i )      { fSoundIdxCounter = i; }

    plSceneObject * GetAvatarSO()                       { return fAvatarSO; }
    void            SetAvatarSO(plSceneObject *so)      { fAvatarSO = so; }
    
    int             NumRenderDependencies()             { return fRenderDependencies.Count(); }
    plMaxNodeBase*  GetRenderDependency(int i)          { return fRenderDependencies[i]; }
    void            AddRenderDependency(plMaxNodeBase* m)   { fRenderDependencies.Append(1, &m); }
    void            ClearRenderDependencies()           { fRenderDependencies.ZeroCount(); }

    int             NumBones()                          { return fBones.Count(); }
    plMaxNodeBase*  GetBone(int i)                      { return fBones[i]; }
    void            AddBone(plMaxNodeBase* m)           { fBones.Append(1, &m); }
    void            ClearBones()                        { fBones.ZeroCount(); }

    hsBool          HasFade()                           { return MaxDatBF.CanBF(MaxDatBF.kHasFade); }
    void            SetFade(const Box3& b)              { MaxDatBF.SetBF((b.Min()[2] != 0)||(b.Max()[2] != 0), MaxDatBF.kHasFade); fFade = b; }
    Box3            GetFade()                           { return fFade; }

    hsBool          HasLoadMask()                       { return MaxDatBF.CanBF(MaxDatBF.kHasLoadMask); }
    plLoadMask      GetLoadMask()                       { return HasLoadMask() ? fLoadMask : plLoadMask::kAlways; }
    void            AddLoadMask(const plLoadMask& m)    { if( !HasLoadMask() ) { fLoadMask = m; MaxDatBF.SetBF(true, MaxDatBF.kHasLoadMask); }else{ fLoadMask |= m; } }

    hsBool          HasNormalChan()                     { return fNormalChan > 0; }
    void            SetNormalChan(int n)                { fNormalChan = n; }
    int             GetNormalChan()                     { return fNormalChan; }

    hsBool          BlendLevelSet()                     { return MaxDatBF.CanBF(MaxDatBF.kBLevelSet); }
    void            SetBlendLevel(const plRenderLevel& l) { fBlendLevel = l; MaxDatBF.SetBF(true, MaxDatBF.kBLevelSet); }
    const plRenderLevel&    GetBlendLevel()             { return fBlendLevel; }
    hsBool          OpaqueLevelSet()                    { return MaxDatBF.CanBF(MaxDatBF.kOLevelSet); }
    void            SetOpaqueLevel(const plRenderLevel& l) { fOpaqueLevel = l; MaxDatBF.SetBF(true, MaxDatBF.kOLevelSet); }
    const plRenderLevel&    GetOpaqueLevel()            { return fOpaqueLevel; }

    plPhysicalProps* GetPhysicalProps()                 { return &fPhysicalProps; }

    hsTArray<int>   *GetAlphaHackLayersCache( void )                        { return fCachedAlphaHackLayerCounts; }
    void            SetAlphaHackLayersCache( hsTArray<int> *cache )         { fCachedAlphaHackLayerCounts = cache; }
    hsBool          GetOverrideHighLevelSDL()           { return MaxDatBF.CanBF(MaxDatBF.kOverrideHighLevelSDL); }
    void            SetOverrideHighLevelSDL(hsBool b)   { MaxDatBF.SetBF(b, MaxDatBF.kOverrideHighLevelSDL); }
    uint8_t           GetAnimCompress()                   { return fAnimCompression; }
    void            SetAnimCompress(uint8_t v)            { fAnimCompression = v; }
    float        GetKeyReduceThreshold()             { return fKeyReduceThreshold; }
    void            SetKeyReduceThreshold(float v)   { fKeyReduceThreshold = v; }

protected:
    plKey           fpKey;
    plSceneObject * fpSO;
    // hacking this in here temporarily because everything's about to change in the mesh world...
    hsGMesh*        fpMesh;
    plKey           fpRoomKey;
    uint32_t          fDecalLevel;
    int32_t           fNormalChan;
    uint32_t          fSoundIdxCounter;
    plSceneObject * fAvatarSO;
    plMaxNodeBaseTab    fRenderDependencies;
    plMaxNodeBaseTab    fBones;
    Box3            fFade;
    int             fGDMaxFaces;
    float           fGDMaxSize;
    int             fGDMinFaces;
    plRenderLevel   fBlendLevel;
    plRenderLevel   fOpaqueLevel;
    plPhysicalProps fPhysicalProps;
    uint32_t          fSwapTargetID;
    hsTArray<int>   *fCachedAlphaHackLayerCounts;
    plSharedMesh    *fSwapMesh;
    plMaxBoneMap    *fBoneMap;
    plLoadMask      fLoadMask;
    float        fWaterHeight;
    uint8_t           fAnimCompression;
    float        fKeyReduceThreshold;
    DataBF  MaxDatBF;
};


#endif // plSimpleConvert_inc