/
etc/
lib/
src/Abilities/
src/Abilities/Skills/
src/Abilities/Spells/
src/Abilities/Spells/Enums/
src/Affects/
src/ArtheaConsole/
src/ArtheaConsole/Properties/
src/ArtheaGUI/Properties/
src/Clans/Enums/
src/Commands/Communication/
src/Commands/ItemCommands/
src/Connections/
src/Connections/Colors/
src/Connections/Enums/
src/Connections/Players/
src/Connections/Players/Enums/
src/Continents/
src/Continents/Areas/
src/Continents/Areas/Characters/
src/Continents/Areas/Characters/Enums/
src/Continents/Areas/Items/
src/Continents/Areas/Items/Enums/
src/Continents/Areas/Rooms/
src/Continents/Areas/Rooms/Enums/
src/Continents/Areas/Rooms/Exits/
src/Creation/
src/Creation/Attributes/
src/Creation/Interfaces/
src/Database/
src/Database/Interfaces/
src/Environment/
src/Properties/
src/Scripts/Enums/
src/Scripts/Interfaces/
#region Arthea License

/***********************************************************************
*  Arthea MUD by R. Jennings (2007)      http://arthea.googlecode.com/ *
*  By using this code you comply with the Artistic and GPLv2 Licenses. *
***********************************************************************/

#endregion

using System;
using System.Reflection;
using Arthea.Connections.Players;
using Arthea.Creation.Attributes;
using Arthea.Interfaces;

namespace Arthea.Creation
{
    /// <summary>
    /// Implementation of a class to edit a field.
    /// </summary>
    public class OlcField
    {
        #region [rgn] Fields (3)

        private object baseObj;
        private FieldInfo info;
        private object obj;

        #endregion [rgn]

        #region [rgn] Constructors (1)

        /// <summary>
        /// Initializes a new instance of the <see cref="OlcField"/> class.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="info">The info.</param>
        public OlcField(object obj, FieldInfo info)
        {
            this.info = info;
            baseObj = obj;
            this.obj = info.GetValue(obj);
        }

        #endregion [rgn]

        #region [rgn] Properties (4)

        /// <summary>
        /// Gets or sets the base obj.
        /// </summary>
        /// <value>The base obj.</value>
        public object BaseObj
        {
            get { return baseObj; }
            set { baseObj = value; }
        }

        /// <summary>
        /// Gets or sets the info.
        /// </summary>
        /// <value>The info.</value>
        public FieldInfo Info
        {
            get { return info; }
            set { info = value; }
        }

        /// <summary>
        /// Gets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return Olc.FieldToWords(info.Name); }
        }

        /// <summary>
        /// Gets or sets the obj.
        /// </summary>
        /// <value>The obj.</value>
        public object Obj
        {
            get { return obj; }
            set { obj = value; }
        }

        #endregion [rgn]

        #region [rgn] Methods (4)

        // [rgn] Public Methods (3)

        /// <summary>
        /// Edits this field.
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="argument">The argument.</param>
        public void Edit(Player player, String argument)
        {
            if (CustomEdit(player, argument))
                return;

            try
            {
                if (info.FieldType.IsEnum)
                {
                    Set(Enum.Parse(info.FieldType, argument, true));
                }
                else
                {
                    Set(Convert.ChangeType(argument.ToString(), info.FieldType));
                }
                player.WriteLine("{0} set to {1}.",
                                 new String(info.Name).Capitalize(), argument);
            }
            catch (Exception ex)
            {
                player.WriteLine("Invalid argument: {0}.", ex.Message);
            }
        }

        /// <summary>
        /// Gets this instance.
        /// </summary>
        public object Get()
        {
            PropertyInfo prop = baseObj.GetType().GetProperty(info.Name,
                                                              BindingFlags.IgnoreCase | BindingFlags.Instance |
                                                              BindingFlags.Public);

            // use a property of field name if it exists
            if (prop != null)
            {
                MethodInfo mi = prop.GetGetMethod();

                if (mi != null)
                {
                    return mi.Invoke(baseObj, new object[] {});
                }
                else
                {
                    Log.Bug("{0} property is write only.", prop.Name);
                }
            }


            return info.GetValue(baseObj);
        }

        /// <summary>
        /// Sets the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        public void Set(object value)
        {
            PropertyInfo prop = baseObj.GetType().GetProperty(info.Name,
                                                              BindingFlags.IgnoreCase | BindingFlags.Instance |
                                                              BindingFlags.Public);

            // use a property of field name if it exists
            if (prop != null)
            {
                MethodInfo mi = prop.GetSetMethod();

                if (mi != null)
                {
                    mi.Invoke(baseObj, new object[] {value});
                    return;
                }
                else
                {
                    Log.Bug("{0} property is read only.", prop.Name);
                }
            }

            info.SetValue(baseObj, value);
        }

        // [rgn] Private Methods (1)

        /// <summary>
        /// Checks for a custom edit method in this object being edited.
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="argument">The argument.</param>
        /// <returns>true if a custom edit method was executed.</returns>
        private bool CustomEdit(Player player, String argument)
        {
            CustomEditType custEd;

            if ((obj is CustomEditType) && (custEd = obj as CustomEditType) != null)
            {
                custEd.CustomEdit(player, this, argument);
                return true;
            }

            object[] atts = info.GetCustomAttributes(typeof (CustomEditAttribute), false);

            if (atts.Length == 0)
                return false;

            else if (atts.Length > 1)
                throw new Exception("Ambiguous custom edit attributes.");

            CustomEditAttribute custAtt = (atts[0] as CustomEditAttribute);

            if (custAtt == null)
                throw new Exception("Attribute is not a custom edit attribute.");

            MethodInfo mi = baseObj.GetType().GetMethod(custAtt.Method,
                                                        BindingFlags.Public | BindingFlags.Instance |
                                                        BindingFlags.Static);

            if (mi == null)
                throw new Exception("Custom edit attribute unable to find method.");

            mi.Invoke(baseObj, new object[] {player, this, argument});
            return true;
        }

        #endregion [rgn]
    }
}