/
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.Text;
using System.Xml.Serialization;
using Arthea.Connections.Players;
using Arthea.Environment;

namespace Arthea.Connections.Colors
{
    /// <summary>
    /// Implements a color
    /// </summary>
    public class Color
    {
        #region Constants

        /// <summary>
        /// The attribute of a color
        /// </summary>
        public const int Attribute = 0;

        /// <summary>
        /// The background of a color
        /// </summary>
        public const int Background = 2;

        /// <summary>
        /// The color escape code
        /// </summary>
        public const char CODE = '~';

        /// <summary>
        /// Escape code
        /// </summary>
        public const string ESC = "\x1B[";

        /// <summary>
        /// The foreground of a color
        /// </summary>
        public const int Foreground = 1;

        #endregion

        private const int Max = 3;
        private static readonly Color[] lastColor = new Color[2];
        private static int lastColorIndex = 0;
        private int saveValue;
        private int[] values = new int[Max];

        #region Constructors (3)

        /// <summary>
        /// Initializes the <see cref="Color"/> class.
        /// </summary>
        static Color()
        {
            lastColor[0] = new Color();
            lastColor[1] = new Color();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Color"/> class.
        /// </summary>
        public Color()
        {
            Clear();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Color"/> class.
        /// </summary>
        /// <param name="c">The c.</param>
        public Color(char c)
        {
            Clear();
            Set(c);
        }

        #endregion

        #region Properties (1)

        /// <summary>
        /// Gets or sets the values.
        /// </summary>
        /// <value>The values.</value>
        [XmlIgnore]
        public int[] Values
        {
            get { return values; }
            set { values = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Toes the string.
        /// </summary>
        /// <param name="saveFlag">The save flag.</param>
        /// <returns></returns>
        public string ToString(int saveFlag)
        {
            StringBuilder buf = new StringBuilder();
            buf.Append(values[Attribute]);
            buf.Append("-");
            buf.Append(values[Foreground]);
            if (ValidColor(Background, values[Background]))
            {
                buf.Append("-");
                buf.Append(values[Background]);
            }

            return buf.ToString();
        }

        /// <summary>
        /// Parses the specified word to color values (#-#-# format).
        /// </summary>
        /// <param name="word">The word.</param>
        /// <returns></returns>
        public static Color Parse(string word)
        {
            string[] words = word.Split('-');
            Color color = new Color();

            for (int i = 0; i < words.Length; i++)
            {
                color.values[i] = int.Parse(words[i]);
            }

            return color;
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        private void Clear()
        {
            for (int i = 0; i < values.Length; i++)
                values[i] = ColorValue.None;

            saveValue = ColorValue.Mod;
        }

        /// <summary>
        /// Copies the from the specified color.
        /// </summary>
        /// <param name="from">From.</param>
        public void Copy(Color from)
        {
            for (int x = 0; x < values.Length; x++)
                values[x] = from.values[x];

            saveValue = from.saveValue;
        }

        /// <summary>
        /// Sets the specified part of the color.
        /// </summary>
        /// <param name="part">The part.</param>
        /// <param name="value">The value.</param>
        public void Set(int part, int value)
        {
            if (part < values.Length)
                values[part] = value;
            else
                saveValue = value;
        }

        /// <summary>
        /// Sets the color based on a code.
        /// </summary>
        /// <param name="c">The c.</param>
        public void Set(char c)
        {
            if (char.IsUpper(c))
                values[Attribute] = ColorValue.Bright;
            else
                values[Attribute] = ColorValue.None;

            switch (char.ToLower(c))
            {
                case '?':
                case '`':
                    values[Attribute] = ColorValue.Random;
                    values[Foreground] = ColorValue.Random;
                    break;
                case 'z':
                    values[Attribute] = ColorValue.Random;
                    values[Background] = ColorValue.Random;
                    break;
                case 'b':
                    values[Foreground] = ColorValue.Blue;
                    break;
                case 'c':
                    values[Foreground] = ColorValue.Cyan;
                    break;
                case 'g':
                    values[Foreground] = ColorValue.Green;
                    break;
                case 'm':
                    values[Foreground] = ColorValue.Magenta;
                    break;
                case 'd':
                    values[Foreground] = ColorValue.Black;
                    break;
                case 'r':
                    values[Foreground] = ColorValue.Red;
                    break;
                case 'y':
                    values[Foreground] = ColorValue.Yellow;
                    break;
                case 'w':
                    values[Foreground] = ColorValue.White;
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Validates this instance.
        /// </summary>
        private void Validate()
        {
            for (int i = 0; i < values.Length; i++)
            {
                if (values[i] == ColorValue.Random)
                {
                    values[i] = RandomColor(i);
                }
            }

            if (ValidColor(Foreground, saveValue))
            {
                if (ValidColor(Foreground, values[Foreground]))
                    values[Background] = (values[Foreground] + ColorValue.Mod);

                values[Foreground] = saveValue;
            }

            if (ValidColor(Attribute, saveValue))
                values[Attribute] = saveValue;

            saveValue = ColorValue.Mod;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString()
        {
            Validate();

            StringBuilder buf = new StringBuilder();

            for (int i = 0; i < values.Length; i++)
            {
                if (ValidColor(i, values[i]))
                {
                    buf.Append(";");
                    buf.Append(values[i]);
                }
            }

            if (buf.Length > 0)
                buf.Remove(0, 1);
            else
                buf.Append("0");

            buf.Append("m");

            return ESC + buf;
        }

        #endregion

        /// <summary>
        /// A random part of a color.
        /// </summary>
        /// <param name="part">The part.</param>
        /// <returns></returns>
        private static int RandomColor(int part)
        {
            int i;

            switch (part)
            {
                case Attribute:
                    i = Randomizer.Next(ColorValue.None, ColorValue.Bright);
                    break;
                case Foreground:
                    i = Randomizer.Next(ColorValue.Red, ColorValue.White);
                    break;
                case Background:
                    i = Randomizer.Next(ColorValue.RedBG, ColorValue.WhiteBG);
                    break;
                default:
                    return ColorValue.None;
            }
            return i;
        }

        /// <summary>
        /// Validates part of the color.
        /// </summary>
        /// <param name="part">The part.</param>
        /// <param name="code">The code.</param>
        /// <returns></returns>
        public static bool ValidColor(int part, int code)
        {
            switch (part)
            {
                case Foreground:
                    return (code >= ColorValue.Black && code <= ColorValue.White);
                case Background:
                    return (code >= ColorValue.BlackBG && code <= ColorValue.WhiteBG);
                case Attribute:
                    return (code >= ColorValue.None && code <= ColorValue.Hidden);
                default:
                    return false;
            }
        }

        /// <summary>
        /// Converts the specified text.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="player">The player.</param>
        /// <returns></returns>
        public static string Convert(string text, Player player)
        {
            if (text == null)
                return "";

            Color col = new Color();
            CharEnumerator ps = text.GetEnumerator();
            StringBuilder buf = new StringBuilder();

            ps.MoveNext();

            do
            {
                if (ps.Current == CODE)
                {
                    if (!ps.MoveNext())
                        break;

                    if (char.IsDigit(ps.Current))
                    {
                        int slot = (ps.Current - '0');

                        /*
                         * Use a number code to set an ansi colour attribute
                         */

                        if (ValidColor(Attribute, slot))
                        {
                            col.Set(col.Values.Length, slot);
                        }
                        /*
                         * Use a '+' sign to add any colour codes, ex. {5G for
                         * blinking green text {5G=B for blinking green text on a
                         * blue background
                        */
                        if (!ps.MoveNext())
                            break;

                        if (ps.Current == '+')
                        {
                            if (!ps.MoveNext())
                                break;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    /*
                     * User a '=' sign to point to a background colour ex. "CT(_VHIT)" sets a
                     * blue background colour
                     */
                    if (ps.Current == '=')
                    {
                        if (!ps.MoveNext())
                            break;
                        col.Set(col.Values.Length, col.Values[Foreground]);
                    }

                    if (player.Config.Has(PlayerConfig.Color))
                    {
                        switch (ps.Current)
                        {
                            case 'n':
                                /*
                                 * Enter the mud's name
                                 */
                                buf.Append("Arthea MUD");
                                break;
                            case 'N':
                                /*
                                 * Enter capitalized mud name
                                 */
                                buf.Append("Arthea MUD".ToUpper());
                                break;
                            case 'P':
                            case 'p':
                            case '*':
                                buf.Append("\007");
                                break;
                            case CODE:
                                buf.Append(CODE);
                                break;
                            case 'x':
                                col.Copy(lastColor[(lastColorIndex + 1)%2]);
                                buf.Append(col);
                                break;
                            case 'X':
                                col.Clear();
                                buf.Append(col);
                                break;
                            default:
                                col.Set(ps.Current);
                                ++lastColorIndex;
                                lastColor[lastColorIndex %= 2] = col;
                                buf.Append(col);
                                break;
                        } // end case
                    }
                }
                else if (ps.Current == CustomColor.BEGIN)
                {
                    int slot = -1;
                    bool fail = false;

                    do
                    {
                        if (!ps.MoveNext())
                        {
                            fail = true;
                            break;
                        }
                        if (char.IsDigit(ps.Current))
                        {
                            if (slot == -1)
                                slot = 0;
                            slot = (slot*10) + (ps.Current - '0');
                        }
                    } while (ps.Current != CustomColor.END);

                    if (fail)
                        break;

                    if (player.Config.Has(PlayerConfig.Color))
                    {
                        if (CustomColor.Valid(slot))
                        {
                            ++lastColorIndex;
                            lastColor[lastColorIndex %= 2] = player.Colors[slot];
                            buf.Append(player.Colors[slot]);
                        }
                    }
                }
                else
                {
                    buf.Append(ps.Current);
                }
            } while (ps.MoveNext());
            return buf.ToString();
        }
    }
}