/*==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/>.

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==*/
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  hsG3DDeviceSelector Class Header                                         //
//  Generic device enumeration (D3D, OpenGL, etc)                            //
//  Cyan, Inc.                                                               //
//                                                                           //
//// Version History //////////////////////////////////////////////////////////
//                                                                           //
//  5.21.2001 mcn - Cleaned out all the old Glide stuff, since Plasma2 will  //
//                  not support Glide :(                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#ifndef hsG3DDeviceSelector_inc
#define hsG3DDeviceSelector_inc

#include "hsWinRef.h"

#include "hsTemplates.h"
#include "hsBitVector.h"

#ifdef HS_BUILD_FOR_WIN32
#define HS_SELECT_DIRECT3D // not supported on the Mac.
#endif // HS_BUILD_FOR_WIN32

#ifdef HS_BUILD_FOR_WIN32
#define __MSC__
#define DYNAHEADER 1
#endif // HS_BUILD_FOR_WIN32

/// #define the following to allow selection of the D3D reference driver
#define HS_ALLOW_D3D_REF_DRIVER 1


class hsStream;
struct D3DEnum_DeviceInfo;
struct D3DEnum_DriverInfo;
struct D3DEnum_DeviceInfo;
struct D3DEnum_DriverInfo;

class hsG3DDeviceMode
{
    enum {
        kNone           = 0x0,
        kDiscarded      = 0x1
    };
protected:
    UInt32              fFlags;

    UInt32              fWidth;
    UInt32              fHeight;
    UInt32              fDepth;

    hsTArray<UInt16>    fZStencilDepths;    // Array of supported depth/stencil buffer formats.
                                            // Each entry is of the form: ( stencil bit count << 8 ) | ( depth bit count )
    hsTArray<UInt8>     fFSAATypes;         // Array of multisample types supported (each one 2-16)

    hsBool              fCanRenderToCubics;

public:
    hsG3DDeviceMode();
    ~hsG3DDeviceMode();

    hsBool operator< (const hsG3DDeviceMode &mode) const;

    void Clear();

    hsBool GetDiscarded() const { return 0 != (fFlags & kDiscarded); }
    UInt32 GetWidth() const { return fWidth; }
    UInt32 GetHeight() const { return fHeight; }
    UInt32 GetColorDepth() const { return fDepth; }
    UInt8   GetNumZStencilDepths( void ) const { return fZStencilDepths.GetCount(); }
    UInt16  GetZStencilDepth( UInt8 i ) const { return fZStencilDepths[ i ]; }
    UInt8   GetNumFSAATypes( void ) const { return fFSAATypes.GetCount(); }
    UInt8   GetFSAAType( UInt8 i ) const { return fFSAATypes[ i ]; }
    hsBool  GetCanRenderToCubics( void ) const { return fCanRenderToCubics; }

    void SetDiscarded(hsBool on=true) { if(on) fFlags |= kDiscarded; else fFlags &= ~kDiscarded; }
    void SetWidth(UInt32 w) { fWidth = w; }
    void SetHeight(UInt32 h) { fHeight = h; }
    void SetColorDepth(UInt32 d) { fDepth = d; }
    void    ClearZStencilDepths( void ) { fZStencilDepths.Reset(); }
    void    AddZStencilDepth( UInt16 depth ) { fZStencilDepths.Append( depth ); }

    void    ClearFSAATypes( void ) { fFSAATypes.Reset(); }
    void    AddFSAAType( UInt8 type ) { fFSAATypes.Append( type ); }

    void    SetCanRenderToCubics( hsBool can ) { fCanRenderToCubics = can; }

    void Read(hsStream* s);
    void Write(hsStream* s) const;
};

class hsG3DDeviceRecord
{
public:
    enum {
        kNone           = 0x0,
        kDiscarded      = 0x1,
        kInvalid        = 0x2
    };

    enum FogTypes {
        kFogExp = 0,
        kFogExp2,
        kNumFogTypes
    };

protected:

    UInt32          fRecordVersion;     /// Version starts at 2 (see .cpp for explanation)
    enum {
        kCurrRecordVersion = 0x0b
        /// Version history:
        ///     1 - Initial version (had no version #)
        ///     2 - Added Z and LOD bias
        ///     3 - Changed Z and LOD bias to floats, added fog tweaks
        ///     4 - Changed values for fog tweaks; force reload through version #
        ///     5 - Same as #4, updated fog end bias to be based solely on fog quantization/bit depth
        ///     6 - Updated values for the ATI boards, Matrox, and i810
        ///     7 - Added fog knee tweaks
        ///     8 - Added support for multiple depth/stencil formats per mode
        ///     9 - Added multisample types to the mode record
        ///     A - Added anisotropic sample field
        ///     B - Added flag for cubic textures support
    };

    /// Version < 2 Data
    UInt32          fFlags;

    UInt32          fG3DDeviceType;
    UInt32          fG3DHALorHEL;


    char*           fG3DDriverDesc;
    char*           fG3DDriverName;
    char*           fG3DDriverVersion;
    char*           fG3DDeviceDesc;

    hsBitVector         fCaps;
    UInt32              fLayersAtOnce;
    UInt32              fMemoryBytes;

    hsTArray<hsG3DDeviceMode> fModes;

    /// New to Version 3
    float   fZBiasRating;
    float   fLODBiasRating;
    float   fFogExpApproxStart;
    float   fFogExp2ApproxStart;
    float   fFogEndBias;    // As a percentage of the max value for fog
                            // (i.e. for Z fog, it's a percentage of 1 to add on,
                            // for W fog, it's a percentage of the yon)

    /// Version 7 - Fog Knee values
    float   fFogKnees[ kNumFogTypes ];
    float   fFogKneeVals[ kNumFogTypes ];

    /// Version 9 - The actual AA setting we use
    UInt8   fAASetting;

    /// Version A - the anisotropic level we use
    UInt8   fMaxAnisotropicSamples; // 1 to disable, up to max allowed in hardware
    int fPixelShaderMajorVer;
    int fPixelShaderMinorVer;

public:
    hsG3DDeviceRecord();
    virtual ~hsG3DDeviceRecord();

    hsG3DDeviceRecord(const hsG3DDeviceRecord& src);
    hsG3DDeviceRecord& operator=(const hsG3DDeviceRecord& src);

    UInt32  GetG3DDeviceType() const { return fG3DDeviceType; }
    const char* GetG3DDeviceTypeName() const;
    UInt32  GetG3DHALorHEL() const { return fG3DHALorHEL; }

    UInt32 GetMemoryBytes() const { return fMemoryBytes; }

    const char* GetDriverDesc() const { return fG3DDriverDesc; }
    const char* GetDriverName() const { return fG3DDriverName; }
    const char* GetDriverVersion() const { return fG3DDriverVersion; }
    const char* GetDeviceDesc() const { return fG3DDeviceDesc; }

    void SetG3DDeviceType(UInt32 t) { fG3DDeviceType = t; }
    void SetG3DHALorHEL(UInt32 h) { fG3DHALorHEL = h; }
    void SetMemoryBytes(UInt32 b) { fMemoryBytes = b; }

    void SetDriverDesc(const char* s);
    void SetDriverName(const char* s);
    void SetDriverVersion(const char* s);
    void SetDeviceDesc(const char* s);

    hsBool  GetCap(UInt32 cap) const { return fCaps.IsBitSet(cap); }
    void    SetCap(UInt32 cap, hsBool on=true) { fCaps.SetBit(cap, on); }

    float   GetZBiasRating( void ) const { return fZBiasRating; }
    void    SetZBiasRating( float rating ) { fZBiasRating = rating; }

    float   GetLODBiasRating( void ) const { return fLODBiasRating; }
    void    SetLODBiasRating( float rating ) { fLODBiasRating = rating; }

    void    GetFogApproxStarts( float &expApprox, float &exp2Approx ) const { expApprox = fFogExpApproxStart;
                                                                            exp2Approx = fFogExp2ApproxStart; }
    void    SetFogApproxStarts( float exp, float exp2 ) { fFogExpApproxStart = exp; 
                                                                fFogExp2ApproxStart = exp2; }

    float   GetFogEndBias( void ) const { return fFogEndBias; }
    void    SetFogEndBias( float rating ) { fFogEndBias = rating; }

    void    GetFogKneeParams( UInt8 type, float &knee, float &kneeVal ) const { knee = fFogKnees[ type ]; kneeVal = fFogKneeVals[ type ]; }
    void    SetFogKneeParams( UInt8 type, float knee, float kneeVal ) { fFogKnees[ type ] = knee; fFogKneeVals[ type ] = kneeVal; }

    UInt32  GetLayersAtOnce() const { return fLayersAtOnce; }
    void    SetLayersAtOnce(UInt32 n) { fLayersAtOnce = n; }

    UInt8   GetAASetting() const { return fAASetting; }
    void    SetAASetting( UInt8 s ) { fAASetting = s; }

    UInt8   GetMaxAnisotropicSamples( void ) const { return fMaxAnisotropicSamples; }
    void    SetMaxAnisotropicSamples( UInt8 num ) { fMaxAnisotropicSamples = num; }

    void SetDiscarded(hsBool on=true) { if(on)fFlags |= kDiscarded; else fFlags &= ~kDiscarded; }
    hsBool GetDiscarded() const { return 0 != (fFlags & kDiscarded); }

    void    SetInvalid( hsBool on = true ) { if( on ) fFlags |= kInvalid; else fFlags &= ~kInvalid; }
    hsBool  IsInvalid() const { return 0 != ( fFlags & kInvalid ); }

    hsTArray<hsG3DDeviceMode>& GetModes() { return fModes; }

    hsG3DDeviceMode* GetMode(int i) const { return &fModes[i]; }

    void ClearModes();
    void Clear();
    void RemoveDiscarded();

    // PlaceHolder - Whether a mode can window is restricted by the current setup
    // of the PC. E.g. if the user changes from 16 bit to TrueColor, the Modes that
    // can window are pretty much flipped. So we'll have to pass in enough info (like
    // the hWnd?) to find out what the current setup is to make sure it's compatible.
    hsBool ModeCanWindow(void* ctx, hsG3DDeviceMode* mode) { return false; } 
    void SetPixelShaderVersion(int major, int minor) { fPixelShaderMajorVer = major; fPixelShaderMinorVer = minor; }
    void GetPixelShaderVersion(int &major, int &minor) { major = fPixelShaderMajorVer; minor = fPixelShaderMinorVer; }

    void Read(hsStream* s);
    void Write(hsStream* s) const;
};

class hsG3DDeviceModeRecord
{
protected:
    hsG3DDeviceRecord       fDevice;
    hsG3DDeviceMode         fMode;
public:
    hsG3DDeviceModeRecord();
    hsG3DDeviceModeRecord(const hsG3DDeviceRecord& devRec, const hsG3DDeviceMode& devMode);
    ~hsG3DDeviceModeRecord();

    hsG3DDeviceModeRecord(const hsG3DDeviceModeRecord& src);
    hsG3DDeviceModeRecord& operator=(const hsG3DDeviceModeRecord& src);

    const hsG3DDeviceRecord*    GetDevice() const { return &fDevice; }
    const hsG3DDeviceMode*      GetMode() const { return &fMode; }
};

class hsG3DDeviceSelector : public hsRefCnt
{
public:
    enum {
        kDevTypeUnknown     = 0,
        kDevTypeGlide,
        kDevTypeDirect3D,
        kDevTypeOpenGL,
        kDevTypeDirect3DTnL,

        kNumDevTypes
    };
    enum {
        kHHTypeUnknown      = 0,
        kHHD3DNullDev,
        kHHD3DRampDev,
        kHHD3DRGBDev,
        kHHD3DHALDev,
        kHHD3DMMXDev,
        kHHD3DTnLHalDev,
        kHHD3DRefDev,
        kHHD3D3dfxDev,
        kHHD3D3dfxVoodoo5Dev,

        kNumHHTypes
    };
    enum {
        kCapsNone           = 0,
        kCapsNoWindow,
        kCapsMipmap,
        kCapsPerspective,
        kCapsHardware,
        kCapsWBuffer,
        kCapsCompressTextures,
        kCapsHWTransform,
        kCapsDither,
        kCapsFogLinear,
        kCapsFogExp,
        kCapsFogExp2,
        kCapsFogRange,
        kCapsLODWatch,
        kCapsUNUSED,
        kCapsDoesSmallTextures,
        kCapsPixelFog,
        kCapsBadYonStuff,
        kCapsNoKindaSmallTexs,
        kCapsCubicTextures,
        kCapsCubicMipmap,
        kCapsZBias,
        kCapsPixelShader,
        kCapsNoAA,
        kCapsDoubleFlush,
        kCapsSingleFlush,
        kCapsCantShadow,
        kCapsMaxUVWSrc2,
        kCapsCantProj,
        kCapsLimitedProj,
        kCapsShareDepth,
        kCapsBadManaged,
        kCapsNoAniso,
        // etc.

        kNumCaps
    };
    enum
    {
        kDefaultWidth   = 800,
        kDefaultHeight  = 600,
        kDefaultDepth   = 32
    };

protected:
    hsTArray<hsG3DDeviceRecord>     fRecords;
    char fTempWinClass[ 128 ];

    char    fErrorString[ 128 ];

    void ITryDirect3DTnLDevice(D3DEnum_DeviceInfo* devInfo, hsG3DDeviceRecord& srcDevRec);
    void ITryDirect3DTnLDriver(D3DEnum_DriverInfo* drivInfo);
    void ITryDirect3DTnL(hsWinRef winRef);
    hsBool  IInitDirect3D( void );

#ifdef HS_SELECT_DX7
    void ITryDirect3DDevice(D3DEnum_DeviceInfo* devInfo, hsG3DDeviceRecord& srcDevRec);
    void ITryDirect3DDriver(D3DEnum_DriverInfo* drivInfo);
    void ITryDirect3D(hsWinRef winRef);
#endif // HS_SELECT_DX7
    void IFudgeDirectXDevice( hsG3DDeviceRecord &record,
                                D3DEnum_DriverInfo *driverInfo, D3DEnum_DeviceInfo *deviceInfo );
    UInt32  IAdjustDirectXMemory( UInt32 cardMem );

    hsBool  IGetD3DCardInfo( hsG3DDeviceRecord &record, void *driverInfo, void *deviceInfo,
                                DWORD *vendorID, DWORD *deviceID, char **driverString, char **descString );
#ifdef HS_SELECT_DX7
    hsBool  IGetD3D7CardInfo( hsG3DDeviceRecord &record, void *driverInfo, void *deviceInfo,
                                DWORD *vendorID, DWORD *deviceID, char **driverString, char **descString );
#endif // HS_SELECT_DX7

    void        ITryOpenGL( hsWinRef winRef );
    void        IGetExtOpenGLInfo( hsG3DDeviceRecord &devRec );
    void        IGetOpenGLModes( hsG3DDeviceRecord &devRec, char *driverName );
    hsBool      ITestOpenGLRes( int width, int height, int bitDepth, 
                                hsG3DDeviceRecord &devRec, char *driverName );
#ifdef HS_OPEN_GL
#if HS_BUILD_FOR_WIN32
    UInt32      ICreateTempOpenGLContext( HDC hDC, hsBool makeItFull );
#endif
#endif

    void    ISetFudgeFactors( UInt8 chipsetID, hsG3DDeviceRecord &record );

public:
    hsG3DDeviceSelector();
    virtual ~hsG3DDeviceSelector();

    void Clear();
    void RemoveDiscarded();
    void RemoveUnusableDevModes(hsBool bTough); // Removes modes and devices not allowed supported in release

    hsBool  Init( void );   // Returns false if couldn't init
    const char  *GetErrorString( void ) { return fErrorString; }

    void Enumerate(hsWinRef winRef);
    hsTArray<hsG3DDeviceRecord>& GetDeviceRecords() { return fRecords; }

    hsBool GetDefault(hsG3DDeviceModeRecord *dmr);

    hsG3DDeviceRecord* GetRecord(int i) { return &fRecords[i]; }

    void Read(hsStream* s);
    void Write(hsStream* s);
};


#define M3DDEMOINFO 1       /// Always compiled now, but only enabled if
                            /// WIN_INIT has DemoInfoOutput in it
///////////////////////////////////////////////////////////////////////////////
//
//  Demo Debug File header file stuff
//  Created 10.10.2000 by Mathew Burrack @ Cyan, Inc.
//
///////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include "headspin.h"

class plDemoDebugFile
{
    public:
        plDemoDebugFile() { fDemoDebugFP = nil; fIsOpen = false; fEnabled = false; }
        ~plDemoDebugFile() { IDDFClose(); }

        // Static function to write a string to the DDF
        static void Write( char *string );

        // Static function to write two strings to the DDF
        static void Write( char *string1, char *string2 );

        // Static function to write a string and a signed integer value to the DDF
        static void Write( char *string1, Int32 value );

        // Enables or disables the DDF class
        static void Enable( hsBool yes ) { fEnabled = yes; }

    protected:
        static hsBool   fIsOpen;
        static FILE     *fDemoDebugFP;
        static hsBool   fEnabled;

        // Opens the DDF for writing
        static hsBool   IDDFOpen( void );

        // Closes the DDF
        static void     IDDFClose( void );
};


#endif // hsG3DDeviceSelector_inc