using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Globalization;
using System.Collections;

namespace XmlRpcProxy
{
    public class Param
    {
        private ValueContent _value;

        [XmlElement("value")]
        public ValueContent Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }

        public Param()
            : this(null)
        {
        }

        public Param(object value)
        {
            _value = new ValueContent(value);
        }
    }

    public class ValueContent
    {
        private object _value;

        private ValueDataType _valueType;

        [XmlElement("string")]
        public string StringValue
        {
            get
            {
                if (_valueType != ValueDataType.StringType)
                {
                    return null;
                }
                else
                {
                    return _value.ToString();
                }
            }
            set
            {
                _valueType = ValueDataType.StringType;
                _value = value.ToString();
            }
        }

        [XmlElement("int")]
        public string IntValue
        {
            get
            {
                if (_valueType != ValueDataType.IntType)
                {
                    return null;
                }
                else
                {
                    return _value.ToString();
                }
            }
            set
            {
                _valueType = ValueDataType.IntType;
                _value = int.Parse(value);
            }
        }

        [XmlElement("dateTime.iso8601")]
        public string DateTimeValue
        {
            get
            {
                if (_valueType != ValueDataType.DateTimeType)
                {
                    return null;
                }
                else
                {
                    return ((DateTime)_value).ToString("yyyyMMddTHH:mm:ss");
                }
            }
            set
            {
                _valueType = ValueDataType.DateTimeType;
                _value = DateTime.ParseExact(value, "yyyyMMddTHH:mm:ss", CultureInfo.InvariantCulture);
            }
        }

        [XmlElement("boolean")]
        public string BooleanValue
        {
            get
            {
                if (_valueType != ValueDataType.BooleanType)
                {
                    return null;
                }
                else
                {
                    return _value.ToString();
                }
            }
            set
            {
                _valueType = ValueDataType.BooleanType;
                _value = bool.Parse(value);
            }
        }

        [XmlElement("base64")]
        public byte[] Base64Value
        {
            get
            {
                if (_valueType != ValueDataType.Base64Type)
                {
                    return null;
                }
                else
                {
                    return (byte[])_value;
                }
            }
            set
            {
                _valueType = ValueDataType.Base64Type;
                _value = value;
            }
        }

        [XmlElement("array")]
        public ArrayContent ArrayValue
        {
            get
            {
                if (_valueType != ValueDataType.ArrayType)
                {
                    return null;
                }
                else
                {
                    return (ArrayContent)_value;
                }
            }
            set
            {
                _valueType = ValueDataType.ArrayType;
                _value = value;
            }
        }

        [XmlElement("struct")]
        public StructContent StructValue
        {
            get
            {
                if (_valueType != ValueDataType.StructType)
                {
                    return null;
                }
                else
                {
                    return (StructContent)_value;
                }
            }
            set
            {
                _valueType = ValueDataType.StructType;
                _value = value;
            }
        }

        [XmlIgnore]
        public object Value
        {
            get
            {
                switch (_valueType)
                {
                    case ValueDataType.ArrayType:
                        ArrayContent arrayContent = (ArrayContent)_value;
                        return arrayContent.Value;

                    case ValueDataType.StructType:
                        StructContent structContent = (StructContent)_value;
                        return structContent.Value;

                    default:
                        return _value;
                }
            }
        }

        public ValueContent()
            : this(null)
        {
        }

        public ValueContent(object value)
        {
            _value = value;
            if (value is int)
            {
                _valueType = ValueDataType.IntType;
            }
            else if (value is DateTime)
            {
                _valueType = ValueDataType.DateTimeType;
            }
            else if (value is bool)
            {
                _valueType = ValueDataType.BooleanType;
            }
            else if (value is byte[])
            {
                _valueType = ValueDataType.Base64Type;
            }
            else if (value is object[])
            {
                _valueType = ValueDataType.ArrayType;
                _value = new ArrayContent((object[])value);
            }
            else if (value is IDictionary<string, object>)
            {
                _valueType = ValueDataType.StructType;
                _value = new StructContent((IDictionary<string, object>)value);
            }
            else
            {
                _valueType = ValueDataType.StringType;
            }
        }
    }

    public class ArrayContent
    {
        private List<ValueContent> _data = new List<ValueContent>();

        [XmlArray("data")]
        [XmlArrayItem("value")]
        public List<ValueContent> Data
        {
            get
            {
                return _data;
            }
            set
            {
                _data = value;
            }
        }

        [XmlIgnore]
        public object[] Value
        {
            get
            {
                int length = _data.Count;
                object[] array = new object[length];
                for (int i = 0; i < length; i++)
                {
                    array[i] = _data[i].Value;
                }
                return array;
            }
        }

        public ArrayContent()
            : this(new object[] { })
        {
        }

        public ArrayContent(object[] data)
        {
            foreach (object value in data)
            {
                _data.Add(new ValueContent(value));
            }
        }
    }

    public class StructContent
    {
        private List<Member> _members = new List<Member>();

        [XmlElement("member")]
        public List<Member> Members
        {
            get { return _members; }
            set { _members = value; }
        }

        [XmlIgnore]
        public IDictionary<string, object> Value
        {
            get
            {
                IDictionary<string, object> map = new Dictionary<string, object>();
                foreach (Member member in _members)
                {
                    map.Add(member.Name, member.Value.Value);
                }
                return map;
            }
        }

        public StructContent()
        {
        }

        public StructContent(IDictionary<string, object> map)
        {
            foreach (string key in map.Keys)
            {
                _members.Add(new Member(key, map[key]));
            }
        }
    }

    public class Member
    {
        private string _name;

        private ValueContent _value;

        [XmlElement("name")]
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }

        [XmlElement("value")]
        public ValueContent Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }

        public Member()
            : this(null, null)
        {
        }

        public Member(string name, object value)
        {
            _name = name;
            _value = new ValueContent(value);
        }
    }

    public enum ValueDataType
    {
        StringType,
        IntType,
        DateTimeType,
        BooleanType,
        Base64Type,
        ArrayType,
        StructType,
    }
}