//BSD License  
//DXGfx® - http://www.eteractions.com
//Copyright (c) 2005
//by Guillaume Randon
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software
//and associated documentation files (the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
//and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all copies or substantial
//portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
//AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;

namespace DXGfxLib
{
    /// <summary>
    /// We use this enumeration to know to which group our effect might be related to.
    /// </summary>
    public enum EffectCategory
    {
        RenderScene,
        RenderLight,
        RenderEnvMap
    }

    /// <summary>
    /// This class is a convenient class to package everything related to the effects that might be needed to render a geometry.
    /// At the moment this class will act as a container for various types of effects associated with various situations.
    /// This is a bit premature as in the current version of the sample associated with the library we use only the default
    /// effect of an effect group.
    /// </summary>
    public class EffectGroup : IComparable, IDisposable
    {
        /// <summary>
        /// Effect to render the scene.
        /// </summary>
        protected Effect renderScene = null;

        /// <summary>
        /// Effect to render the scene accessor.
        /// </summary>
        public Effect RenderScene
        {
            get { return renderScene; }
        }

        /// <summary>
        /// Default effect of the effect group, at the moment this is the only one really used.
        /// It is the same as the RenderScene effect.
        /// </summary>
        public Effect Default
        {
            get { return renderScene; }
        }

        /// <summary>
        /// We want to keep the file name of the effect file.
        /// </summary>
        protected string renderSceneEffectFile = "";

        /// <summary>
        /// Effect file accessor.
        /// </summary>
        public string RenderSceneEffectFile
        {
            get { return renderSceneEffectFile; }
        }

        /// <summary>
        /// Default effect file accessor.
        /// </summary>
        public string DefaultFile
        {
            get { return renderSceneEffectFile; }
        }

        /// <summary>
        /// This is the name of the effect group.
        /// </summary>
        public string EffectGroupName
        {
            get { return renderSceneEffectFile; }
        }

        /// <summary>
        /// Effect for light
        /// </summary>
        protected Effect renderLight = null;

        /// <summary>
        /// Effect for light
        /// </summary>
        public Effect RenderLight
        {
            get { return renderLight; }
        }

        /// <summary>
        /// Effect file for light
        /// </summary>
        protected string renderLightEffectFile = "";

        /// <summary>
        /// Effect file for light
        /// </summary>
        public string RenderLightEffectFile
        {
            get { return renderLightEffectFile; }
        }

        /// <summary>
        /// Effect to render env map 
        /// </summary>
        protected Effect renderEnvMap = null;

        /// <summary>
        /// Effect to render env map 
        /// </summary>
        public Effect RenderEnvMap
        {
            get { return renderEnvMap; }
        }

        /// <summary>
        /// Effect file to render env map 
        /// </summary>
        protected string renderEnvMapEffectFile = "";

        /// <summary>
        /// Effect file to render env map 
        /// </summary>
        public string RenderEnvMapEffectFile
        {
            get { return renderEnvMapEffectFile; }
        }

        /// <summary>
        /// Effects need to be disposed.
        /// </summary>
        public void Dispose()
        {
            if (renderScene != null)
            {
                renderScene.Dispose();
            }
            if (renderLight != null)
            {
                renderLight.Dispose();
            }
            if (renderEnvMap != null)
            {
                renderEnvMap.Dispose();
            }
        }

        /// <summary>
        /// This method sets the effect used to render the scene (which is also the default one for the effect group).
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="fileName"></param>
        public void Set(Device d3ddevice, string fileName)
        {
            Set(d3ddevice, fileName, EffectCategory.RenderScene);
        }

        /// <summary>
        /// This will set the effect of the specified category.
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="fileName"></param>
        /// <param name="category"></param>
        public void Set(Device d3ddevice, string fileName, EffectCategory category)
        {
            if (!File.Exists(fileName))
            {
                // Let's try to found it
                fileName = Utility.FindMediaFile(StringUtil.GetName(fileName));
            }

            string strErr = "No Problem!";
            Effect effect = null;
            String effectFileName = "";

            try
            {
                effect = ResourceCache.GetGlobalInstance().CreateEffectFromFile(d3ddevice, fileName, null, null, ShaderFlags.None, null, out strErr);
                effect.Technique = "Default";
                effectFileName = fileName;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(strErr);
                throw new EffectException("Exception while creating effect", fileName, strErr, ex);
            }

            switch (category)
            {
                case EffectCategory.RenderScene:
                    renderScene = effect;
                    renderSceneEffectFile = effectFileName;
                    break;
                case EffectCategory.RenderLight:
                    renderLight = effect;
                    renderLightEffectFile = effectFileName;
                    break;
                case EffectCategory.RenderEnvMap:
                    renderEnvMap = effect;
                    renderEnvMapEffectFile = effectFileName;
                    break;
            }
        }

        /// <summary>
        /// A different method to set the effect that will be used to render the scene. Use this method when you already
        /// have the effect and don't need to load it from a .fx file.
        /// </summary>
        /// <param name="effect"></param>
        /// <param name="fileName"></param>
        public void Set(Effect effect, string fileName)
        {
            Set(effect, fileName, EffectCategory.RenderScene);
        }

        /// <summary>
        /// A different method to set the effect of the desired category. Use this method when the effect already exist and that
        /// you don't need to load it from a file.
        /// </summary>
        /// <param name="effect"></param>
        /// <param name="fileName"></param>
        /// <param name="category"></param>
        public void Set(Effect effect, string fileName, EffectCategory category)
        {
            if (effect.Technique == string.Empty)
            {
                effect.Technique = "Default";
            }

            switch (category)
            {
                case EffectCategory.RenderScene:
                    renderScene = effect;
                    renderSceneEffectFile = fileName;
                    break;
                case EffectCategory.RenderLight:
                    renderLight = effect;
                    renderLightEffectFile = fileName;
                    break;
                case EffectCategory.RenderEnvMap:
                    renderEnvMap = effect;
                    renderEnvMapEffectFile = fileName;
                    break;
            }
        }

        /// <summary>
        /// Our objects are sorted before beiing rendered. One of the main criterion to choose which one will be first in the
        /// list is the effect group that he is using. Basically when we sort objects before rendering, we do in fact compare
        /// their effect group between themselves. We do call the below method (in fact the one which is passed an effectgroup
        /// and not an object) to sort these. Sorting will be performed based on the name of the effectgroup (why not) :).
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(Object obj)
        {
            int ret = 1;
            EffectGroup effectGroup = obj as EffectGroup;

            if (effectGroup != null)
            {
                ret = renderSceneEffectFile.CompareTo(effectGroup.RenderSceneEffectFile);
            }
            return ret;
        }

        /// <summary>
        /// Our objects are sorted before beiing rendered. One of the main criterion to choose which one will be first in the
        /// list is the effect group that he is using. Basically when we sort objects before rendering, we do in fact compare
        /// their effect group between themselves. We do call the below method (in fact the one which is passed an effectgroup
        /// and not an object) to sort these. Sorting will be performed based on the name of the effectgroup (why not) :).
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(EffectGroup effectGroup)
        {
            return renderSceneEffectFile.CompareTo(effectGroup.RenderSceneEffectFile);
        }
    }
}
