using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Xml;
using System.IO;
using System.Xml.Serialization;

namespace CodeSmith.CustomProperties
{
    public class NameValueCollectionTypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(XmlNode))
            {
                return true;
            }
            if (sourceType == typeof(string))
            {
                return true;
            }

            return base.CanConvertFrom(context, sourceType);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(XmlNode))
            {
				
                return true;
            }

            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is XmlNode)
            {
                XmlNode node = (XmlNode)value;
                NameValueCollection collection = new NameValueCollection();

                XmlNodeList nodeList = node.SelectNodes("item");
                if (nodeList != null && nodeList.Count > 0)
                {
                    for (int i = 0; i < nodeList.Count; i++)
                    {
                        if (nodeList[i].Attributes.Count > 0)
                            collection.Add(nodeList[i].Attributes[0].Value, nodeList[i].InnerText);
                    }
                }

                return collection;
            }

            if (value is string) 
			{
                return ParseKeyValue(value.ToString());
            }

            return base.ConvertFrom(context, culture, value);
        }

        private NameValueCollection ParseKeyValue(string keyValue)
        {
            NameValueCollection nameValue = new NameValueCollection();

            bool inString = false;
            bool inKey = true;

            StringBuilder key = new StringBuilder();
            StringBuilder value = new StringBuilder();

            for (int pos = 0; pos < keyValue.Length; pos++)
            {
                char c = keyValue[pos];

                if (char.IsControl(c))
                    continue;

                if (c == '"')
                {
                    char n = pos < keyValue.Length - 1 ? keyValue[pos + 1] : ' ';
                    if (n == '"')
                    {
                        pos++;
                    }
                    else
                    {
                        inString = !inString; // flip
                        continue;
                    }
                }

                if (c == '=' && !inString)
                {
                    inKey = false;
                    continue;
                }

                if (c == ';' && !inString)
                {
                    nameValue.Add(key.ToString(), value.ToString());
                    key.Length = 0;
                    value.Length = 0;
                    inKey = true;
                    continue;
                }

                if (inKey)
                    key.Append(c);
                else
                    value.Append(c);

            } // foreach

            if (key.Length != 0 || value.Length != 0)
                nameValue.Add(key.ToString(), value.ToString());

            return nameValue;
        }


        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(XmlNode))
            {
                StringWriter writer = new StringWriter();
                XmlTextWriter xmlwriter = new XmlTextWriter(writer);
                NameValueCollection collection = value as NameValueCollection;

                xmlwriter.WriteStartElement("root");

                if (collection != null && collection.Count > 0)
                {
                    for (int i = 0; i < collection.Count; i++)
                    {
                        xmlwriter.WriteStartElement("item");
                        xmlwriter.WriteAttributeString("key", collection[i].Key);
                        xmlwriter.WriteString(collection[i].Value);
                        xmlwriter.WriteEndElement();
                    }                    
                }

                xmlwriter.WriteEndElement();

                string xml = writer.ToString();
                writer.Close();
                xmlwriter.Close();

                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xml);

                return doc.SelectSingleNode("/root");
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}
