/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NMATERIAL__
#define	__NMATERIAL__


#include "core/material_channel.h"
#include "core/physic_material.h"
#include "math/matrix4.h"
#include "color/color.h"
#include "memory/nshared_ptr.h"
#include "memory/nauto_ptr.h"
#include "nstring/nstring.h"


namespace GS {
    namespace Core {
        /// Basic material definition.
        struct BasicMaterial {
            Color diffuse; ///< Diffuse color.
            Color specular; ///< Specular color.
            Color self; ///< Self color.
            Color ambient; ///< Ambient color.

            float glossiness; ///< Glossiness.
            float opacity; ///< Opacity (eg. 1 - transparency or alpha).
            float reflection; ///< Reflection [0;1]
            float irefraction; ///< Index of refraction.
            float athreshold; ///< Alpha threshold [0;1].

            float depth_bias; ///< Depth bias.

            uint renderword; ///< Render word.
            uint blendop; ///< Blending operator.
        };

        /*!
            @short	Material.

            A material describe the rendering properties of a surface.
            It usually embeds several textures which can have a very different use
            depending on whether they are used by a fixed function hardware renderer or
            a programmable renderer or even the raytracer.

            @note	A material can have up to nTextureStageLimit texture levels.
            @author Emmanuel Julien (ejulien@gsworks.fr)
        */
        struct Material : public BasicMaterial, public PhysicMaterial, public SharedObject {
            NPLACEMENT_NEW(Material)

            /*!
                @short	Maximum texture stage count.
                @note	1 is the lowest acceptable value as C++ does not allow 0 sized
                        array declaration. (Default: 6)
            */
            static const int max_texture_stage = 6;

            /// Material render word (bit mask).
            enum RenderWord {
                Render_None = 0,

                Render_Unlit = (1 << 0),
                Render_Smooth = (1 << 1),
                Render_NormalTangent = (1 << 2),
                Render_NoFog = (1 << 3),

                Render_DoubleSided = (1 << 4),
                Render_Wire = (1 << 5),
                Render_VertexColor = (1 << 6),
                Render_ParralaxDisp = (1 << 7),
                Render_Toon = (1 << 8),

                Render_NoZWrite = (1 << 9),
                Render_NoZTest = (1 << 10),
                Render_AlphaTest = (1 << 11),

                Render_AlphaSoftZ = (1 << 12), ///< Soft particle.
                Render_AlphaInShadow = (1 << 13), ///< Render alpha/opacity in shadow maps.

                Render_UseFramebuffer = (1 << 14),
                Render_Skinned = (1 << 15)
            };

            /// Blending operator.
            enum BlendOperator {
                Blend_None = 0,
                Blend_Alpha,
                Blend_Add
            };

            /// Filtering type (exclusive).
            enum FilterType {
                Filter_None = 0,
                Filter_Point = 1,
                Filter_Linear = 2,

                Filter_Default = Filter_Point
            };

            enum UVMode {
                UV_UV = 0, ///< Geometry standard UV buffer.
                UV_LSN, ///< Geometry local space transformed normal component.
                UV_FrontMap, ///< Front mapping.
                UV_SphericalEnvironment ///< Spherical Environment mapping.
            };

            enum Operator {
                Operator_Default = 0, ///< Default operator for a given renderer (usually a function of channel member).
                Operator_Multiply, ///< Multiply stage.
                Operator_Add ///< Add stage.
            };

            /*!
                @short	Texture stage.

                A texture stage describes a texture layer in a material.
                It links a texture object together with the material layer specific
                attributes such as UV wrapping, texture sampling method (point, linear,
                etc...). A texture stage is also associated with the channel it modify
                (diffuse, color, specular, etc... see StageChannel). It is the
                renderer's responsibility to render a given material as accurately as
                possible and to provide acceptable fall back in the case a material is
                too complex to be rendered.

                @author Emmanuel Julien (ejulien@gsworks.fr)
            */
            struct TextureStage {
                MaterialChannel channel; ///< Texture channel.
                String t; ///< Shared texture object.

                uchar uv_index; ///< UV map index in geometry UV list.
                UVMode uv_mode; ///< UV mode.
                Matrix4 uv_matrix; ///< UV matrix.

                Operator op; ///< Stage combine operator.

                /// Reset the texture stage.
                void Reset();

                TextureStage() { Reset(); }
            };

            String name;
            String shader;

            uint texstage_count;
            Array<TextureStage> texstage;

            /// Return the texture stage for a given channel (if any).
            TextureStage *GetChannelStage(MaterialChannel) const;

            /*!
                @short	Create a new texture stage.
                @note	If a stage is already assigned to the material channel
                        it will be returned and no new stage will be allocated.
            */
            TextureStage *NewStage(MaterialChannel, const char *uri = 0, UVMode = UV_UV, uchar uv_index = 0);

            void Reset();

            /*!
                @name	Serialization.
                @{
            */
            bool FromMetaTag(NML::Tag &);

            NML::Tag *AsMetaTag() const;

            /// @}

            Material();
        };

        typedef SharedPtr<Material> sMaterial;
        typedef List<Material *> MaterialList;
    } // Core
} // GS


#endif	// __NMATERIAL__
