/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSG_DisplaySettings
#define OSG_DisplaySettings 1

#include <osg/Referenced>
#include <osg/Matrixd>
#include <osg/ref_ptr>

#include <string>
#include <vector>
#include <map>

namespace osg {

// forward declare
class ArgumentParser;
class ApplicationUsage;

/** DisplaySettings class for encapsulating what visuals are required and
  * have been set up, and the status of stereo viewing.*/
class OSG_EXPORT DisplaySettings : public osg::Referenced
{

    public:

        /** Maintain a DisplaySettings singleton for objects to query at runtime.*/
        static ref_ptr<DisplaySettings>& instance();

        DisplaySettings():
            Referenced(true)
        {
            setDefaults();
            readEnvironmentalVariables();
        }

        DisplaySettings(ArgumentParser& arguments):
            Referenced(true)
        {
            setDefaults();
            readEnvironmentalVariables();
            readCommandLine(arguments);
        }

        DisplaySettings(const DisplaySettings& vs);


        DisplaySettings& operator = (const DisplaySettings& vs);

        void setDisplaySettings(const DisplaySettings& vs);

        void merge(const DisplaySettings& vs);

        void setDefaults();

        /** read the environmental variables.*/
        void readEnvironmentalVariables();

        /** read the commandline arguments.*/
        void readCommandLine(ArgumentParser& arguments);


        enum DisplayType
        {
            MONITOR,
            POWERWALL,
            REALITY_CENTER,
            HEAD_MOUNTED_DISPLAY
        };

        void setDisplayType(DisplayType type) { _displayType = type; }

        DisplayType getDisplayType() const { return _displayType; }


        void setStereo(bool on) { _stereo = on; }
        bool getStereo() const { return _stereo; }

        enum StereoMode
        {
            QUAD_BUFFER,
            ANAGLYPHIC,
            HORIZONTAL_SPLIT,
            VERTICAL_SPLIT,
            LEFT_EYE,
            RIGHT_EYE,
            HORIZONTAL_INTERLACE,
            VERTICAL_INTERLACE,
            CHECKERBOARD
        };

        void setStereoMode(StereoMode mode) { _stereoMode = mode; }
        StereoMode getStereoMode() const { return _stereoMode; }

        void setEyeSeparation(float eyeSeparation) { _eyeSeparation = eyeSeparation; }
        float getEyeSeparation() const { return _eyeSeparation; }

        enum SplitStereoHorizontalEyeMapping
        {
            LEFT_EYE_LEFT_VIEWPORT,
            LEFT_EYE_RIGHT_VIEWPORT
        };

        void setSplitStereoHorizontalEyeMapping(SplitStereoHorizontalEyeMapping m) { _splitStereoHorizontalEyeMapping = m; }
        SplitStereoHorizontalEyeMapping getSplitStereoHorizontalEyeMapping() const { return _splitStereoHorizontalEyeMapping; }

        void setSplitStereoHorizontalSeparation(int s) { _splitStereoHorizontalSeparation = s; }
        int getSplitStereoHorizontalSeparation() const { return _splitStereoHorizontalSeparation; }

        enum SplitStereoVerticalEyeMapping
        {
            LEFT_EYE_TOP_VIEWPORT,
            LEFT_EYE_BOTTOM_VIEWPORT
        };

        void setSplitStereoVerticalEyeMapping(SplitStereoVerticalEyeMapping m) { _splitStereoVerticalEyeMapping = m; }
        SplitStereoVerticalEyeMapping getSplitStereoVerticalEyeMapping() const { return _splitStereoVerticalEyeMapping; }

        void setSplitStereoVerticalSeparation(int s) { _splitStereoVerticalSeparation = s; }
        int getSplitStereoVerticalSeparation() const { return _splitStereoVerticalSeparation; }

        void setSplitStereoAutoAdjustAspectRatio(bool flag) { _splitStereoAutoAdjustAspectRatio=flag; }
        bool getSplitStereoAutoAdjustAspectRatio() const { return _splitStereoAutoAdjustAspectRatio; }


        void setScreenWidth(float width) { _screenWidth = width; }
        float getScreenWidth() const { return _screenWidth; }

        void setScreenHeight(float height) { _screenHeight = height; }
        float getScreenHeight() const { return _screenHeight; }

        void setScreenDistance(float distance) { _screenDistance = distance; }
        float getScreenDistance() const { return _screenDistance; }



        void setDoubleBuffer(bool flag) { _doubleBuffer = flag; }
        bool getDoubleBuffer() const { return _doubleBuffer; }


        void setRGB(bool flag) { _RGB = flag; }
        bool getRGB() const { return _RGB; }


        void setDepthBuffer(bool flag) { _depthBuffer = flag; }
        bool getDepthBuffer() const { return _depthBuffer; }


        void setMinimumNumAlphaBits(unsigned int bits) { _minimumNumberAlphaBits = bits; }
        unsigned int getMinimumNumAlphaBits() const { return _minimumNumberAlphaBits; }
        bool getAlphaBuffer() const { return _minimumNumberAlphaBits!=0; }

        void setMinimumNumStencilBits(unsigned int bits) { _minimumNumberStencilBits = bits; }
        unsigned int getMinimumNumStencilBits() const { return _minimumNumberStencilBits; }
        bool getStencilBuffer() const { return _minimumNumberStencilBits!=0; }

        void setMinimumNumAccumBits(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha);
        unsigned int getMinimumNumAccumRedBits() const { return _minimumNumberAccumRedBits; }
        unsigned int getMinimumNumAccumGreenBits() const { return _minimumNumberAccumGreenBits; }
        unsigned int getMinimumNumAccumBlueBits() const { return _minimumNumberAccumBlueBits; }
        unsigned int getMinimumNumAccumAlphaBits() const { return _minimumNumberAccumAlphaBits; }
        bool getAccumBuffer() const { return (_minimumNumberAccumRedBits+_minimumNumberAccumGreenBits+_minimumNumberAccumBlueBits+_minimumNumberAccumAlphaBits)!=0; }


        void setMaxNumberOfGraphicsContexts(unsigned int num);
        unsigned int getMaxNumberOfGraphicsContexts() const;

        void setNumMultiSamples(unsigned int samples) { _numMultiSamples = samples; }
        unsigned int getNumMultiSamples() const { return _numMultiSamples; }
        bool getMultiSamples() const { return _numMultiSamples!=0; }

        void setCompileContextsHint(bool useCompileContexts) { _compileContextsHint = useCompileContexts; }
        bool getCompileContextsHint() const { return _compileContextsHint; }

        void setSerializeDrawDispatch(bool serializeDrawDispatch) { _serializeDrawDispatch = serializeDrawDispatch; }
        bool getSerializeDrawDispatch() const { return _serializeDrawDispatch; }


        void setUseSceneViewForStereoHint(bool hint) { _useSceneViewForStereoHint = hint; }
        bool getUseSceneViewForStereoHint() const { return _useSceneViewForStereoHint; }


        /** Set the hint for the total number of threads in the DatbasePager set up, inclusive of the number of http dedicated threads.*/
        void setNumOfDatabaseThreadsHint(unsigned int numThreads) { _numDatabaseThreadsHint = numThreads; }

        /** Get the hint for total number of threads in the DatbasePager set up, inclusive of the number of http dedicated threads.*/
        unsigned int getNumOfDatabaseThreadsHint() const { return _numDatabaseThreadsHint; }

        /** Set the hint for number of threads in the DatbasePager to dedicate to reading http requests.*/
        void setNumOfHttpDatabaseThreadsHint(unsigned int numThreads) { _numHttpDatabaseThreadsHint = numThreads; }

        /** Get the hint for number of threads in the DatbasePager dedicated to reading http requests.*/
        unsigned int getNumOfHttpDatabaseThreadsHint() const { return _numHttpDatabaseThreadsHint; }

        void setApplication(const std::string& application) { _application = application; }
        const std::string& getApplication() { return _application; }


        void setMaxTexturePoolSize(unsigned int size) { _maxTexturePoolSize = size; }
        unsigned int getMaxTexturePoolSize() const { return _maxTexturePoolSize; }

        void setMaxBufferObjectPoolSize(unsigned int size) { _maxBufferObjectPoolSize = size; }
        unsigned int getMaxBufferObjectPoolSize() const { return _maxBufferObjectPoolSize; }

        /**
         Methods used to set and get defaults for Cameras implicit buffer attachments.
         For more info: See description of Camera::setImplicitBufferAttachment method

         DisplaySettings implicit buffer attachment selection defaults to: DEPTH and COLOR
         for both primary (Render) FBO and secondary Multisample (Resolve) FBO
         ie: IMPLICIT_DEPTH_BUFFER_ATTACHMENT | IMPLICIT_COLOR_BUFFER_ATTACHMENT
        **/
        enum ImplicitBufferAttachment
        {
            IMPLICIT_DEPTH_BUFFER_ATTACHMENT = (1 << 0),
            IMPLICIT_STENCIL_BUFFER_ATTACHMENT = (1 << 1),
            IMPLICIT_COLOR_BUFFER_ATTACHMENT = (1 << 2),
            DEFAULT_IMPLICIT_BUFFER_ATTACHMENT = IMPLICIT_COLOR_BUFFER_ATTACHMENT | IMPLICIT_DEPTH_BUFFER_ATTACHMENT
        };

        typedef int ImplicitBufferAttachmentMask;

        void setImplicitBufferAttachmentMask(ImplicitBufferAttachmentMask renderMask = DisplaySettings::DEFAULT_IMPLICIT_BUFFER_ATTACHMENT, ImplicitBufferAttachmentMask resolveMask = DisplaySettings::DEFAULT_IMPLICIT_BUFFER_ATTACHMENT )
        {
            _implicitBufferAttachmentRenderMask = renderMask;
            _implicitBufferAttachmentResolveMask = resolveMask;
        }

        void setImplicitBufferAttachmentRenderMask(ImplicitBufferAttachmentMask implicitBufferAttachmentRenderMask)
        {
            _implicitBufferAttachmentRenderMask = implicitBufferAttachmentRenderMask;
        }

        void setImplicitBufferAttachmentResolveMask(ImplicitBufferAttachmentMask implicitBufferAttachmentResolveMask)
        {
            _implicitBufferAttachmentResolveMask = implicitBufferAttachmentResolveMask;
        }

        /** Get mask selecting default implicit buffer attachments for Cameras primary FBOs. */
        ImplicitBufferAttachmentMask getImplicitBufferAttachmentRenderMask() const {  return _implicitBufferAttachmentRenderMask; }

        /** Get mask selecting default implicit buffer attachments for Cameras secondary MULTISAMPLE FBOs. */
        ImplicitBufferAttachmentMask getImplicitBufferAttachmentResolveMask() const { return _implicitBufferAttachmentResolveMask;}

        enum SwapMethod
        {
            SWAP_DEFAULT,   // Leave swap method at default returned by choose Pixel Format.
            SWAP_EXCHANGE,  // Flip front / back buffer.
            SWAP_COPY,      // Copy back to front buffer.
            SWAP_UNDEFINED  // Move back to front buffer leaving contents of back buffer undefined.
        };

        /** Select preferred swap method */
        void setSwapMethod( SwapMethod swapMethod ) { _swapMethod = swapMethod; }

        /** Get preferred swap method */
        SwapMethod getSwapMethod( void ) { return _swapMethod; }


        /** Set whether Arb Sync should be used to manage the swaps buffers, 0 disables the use of the sync, greater than zero enables sync based on number of frames specified.*/
        void setSyncSwapBuffers(unsigned int numFrames=0) { _syncSwapBuffers = numFrames; }

        /** Set whether Arb Sync should be used to manage the swaps buffers.*/
        unsigned int getSyncSwapBuffers() const { return _syncSwapBuffers; }



        /** Set the hint of which OpenGL version to attempt to create a graphics context for.*/
        void setGLContextVersion(const std::string& version) { _glContextVersion = version; }

        /** Get the hint of which OpenGL version to attempt to create a graphics context for.*/
        const std::string getGLContextVersion() const { return _glContextVersion; }

        /** Set the hint of the flags to use in when creating graphic contexts.*/
        void setGLContextFlags(unsigned int flags) { _glContextFlags = flags; }

        /** Get the hint of the flags to use in when creating graphic contexts.*/
        unsigned int getGLContextFlags() const { return _glContextFlags; }

        /** Set the hint of the profile mask to use in when creating graphic contexts.*/
        void setGLContextProfileMask(unsigned int mask) { _glContextProfileMask = mask; }

        /** Get the hint of the profile mask to use in when creating graphic contexts.*/
        unsigned int getGLContextProfileMask() const { return _glContextProfileMask; }

        /** Set the NvOptimusEnablement value. Default can be set using OSG_NvOptimusEnablement env var.*/
        void setNvOptimusEnablement(int value);
        /** Get the NvOptimusEnablement value. */
        int getNvOptimusEnablement() const;


        enum VertexBufferHint
        {
            NO_PREFERENCE,
            VERTEX_BUFFER_OBJECT,
            VERTEX_ARRAY_OBJECT
        };

        void setVertexBufferHint(VertexBufferHint gi) { _vertexBufferHint = gi; }
        VertexBufferHint getVertexBufferHint() const { return _vertexBufferHint; }


        enum ShaderHint
        {
            SHADER_NONE,
            SHADER_GL2,
            SHADER_GLES2,
            SHADER_GL3,
            SHADER_GLES3
        };

        /** set the ShaderHint to tells shader generating cdoes  version to create.
         * By default also OSG_GLSL_VERSION and OSG_PRECISION_FLOAT values that can get use directly in shaders using $OSG_GLSL_VERSION and $OSG_PRECISION_FLOAT respectively.*/
        void setShaderHint(ShaderHint hint, bool setShaderValues=true);
        ShaderHint getShaderHint() const { return _shaderHint; }

        /** Set the TextShaderTechnique that is used in the Text default constructor to choose which osgText::ShaderTechnique to use.*/
        void setTextShaderTechnique(const std::string& str) { _textShaderTechnique = str; }
        const std::string& getTextShaderTechnique() const { return _textShaderTechnique; }


        void setKeystoneHint(bool enabled) { _keystoneHint = enabled; }
        bool getKeystoneHint() const { return _keystoneHint; }

        typedef std::vector<std::string> FileNames;
        void setKeystoneFileNames(const FileNames& filenames) { _keystoneFileNames = filenames; }
        FileNames& getKeystoneFileNames() { return _keystoneFileNames; }
        const FileNames& getKeystoneFileNames() const { return _keystoneFileNames; }

        typedef std::vector< osg::ref_ptr<osg::Object> > Objects;
        void setKeystones(const Objects& objects) { _keystones = objects; }
        Objects& getKeystones() { return _keystones; }
        const Objects& getKeystones() const { return _keystones; }

        enum OSXMenubarBehavior
        {
            MENUBAR_AUTO_HIDE,
            MENUBAR_FORCE_HIDE,
            MENUBAR_FORCE_SHOW
        };

        OSXMenubarBehavior getOSXMenubarBehavior() const { return _OSXMenubarBehavior; }
        void setOSXMenubarBehavior(OSXMenubarBehavior hint) { _OSXMenubarBehavior = hint; }

        /** helper function for computing the left eye projection matrix.*/
        virtual osg::Matrixd computeLeftEyeProjectionImplementation(const osg::Matrixd& projection) const;

        /** helper function for computing the left eye view matrix.*/
        virtual osg::Matrixd computeLeftEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale=1.0) const;

        /** helper function for computing the right eye view matrix.*/
        virtual osg::Matrixd computeRightEyeProjectionImplementation(const osg::Matrixd& projection) const;

        /** helper function for computing the right eye view matrix.*/
        virtual osg::Matrixd computeRightEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale=1.0) const;


        void setValue(const std::string& name, const std::string& value);

        bool getValue(const std::string& name, std::string& value, bool use_getenv_fallback=true) const;

    protected:

        virtual ~DisplaySettings();


        DisplayType                     _displayType;
        bool                            _stereo;
        StereoMode                      _stereoMode;
        float                           _eyeSeparation;
        float                           _screenWidth;
        float                           _screenHeight;
        float                           _screenDistance;

        SplitStereoHorizontalEyeMapping _splitStereoHorizontalEyeMapping;
        int                             _splitStereoHorizontalSeparation;
        SplitStereoVerticalEyeMapping   _splitStereoVerticalEyeMapping;
        int                             _splitStereoVerticalSeparation;
        bool                            _splitStereoAutoAdjustAspectRatio;

        bool                            _doubleBuffer;
        bool                            _RGB;
        bool                            _depthBuffer;
        unsigned int                    _minimumNumberAlphaBits;
        unsigned int                    _minimumNumberStencilBits;
        unsigned int                    _minimumNumberAccumRedBits;
        unsigned int                    _minimumNumberAccumGreenBits;
        unsigned int                    _minimumNumberAccumBlueBits;
        unsigned int                    _minimumNumberAccumAlphaBits;

        unsigned int                    _maxNumOfGraphicsContexts;

        unsigned int                    _numMultiSamples;

        bool                            _compileContextsHint;
        bool                            _serializeDrawDispatch;
        bool                            _useSceneViewForStereoHint;

        unsigned int                    _numDatabaseThreadsHint;
        unsigned int                    _numHttpDatabaseThreadsHint;

        std::string                     _application;

        unsigned int                    _maxTexturePoolSize;
        unsigned int                    _maxBufferObjectPoolSize;

        ImplicitBufferAttachmentMask    _implicitBufferAttachmentRenderMask;
        ImplicitBufferAttachmentMask    _implicitBufferAttachmentResolveMask;

        std::string                     _glContextVersion;
        unsigned int                    _glContextFlags;
        unsigned int                    _glContextProfileMask;

        SwapMethod                      _swapMethod;
        unsigned int                    _syncSwapBuffers;

        VertexBufferHint                _vertexBufferHint;
        ShaderHint                      _shaderHint;
        std::string                     _textShaderTechnique;

        bool                            _keystoneHint;
        FileNames                       _keystoneFileNames;
        Objects                         _keystones;

        OSXMenubarBehavior              _OSXMenubarBehavior;

        typedef std::map<std::string, std::string> ValueMap;

        mutable OpenThreads::Mutex      _valueMapMutex;
        mutable ValueMap                _valueMap;

};

}

# endif