using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using LumiSoft.Net.IO;
using LumiSoft.Net.MIME;

namespace LumiSoft.Net.MIME
{
    /// <summary>
    /// Represents a MIME entity. Defined in RFC 2045 2.4.
    /// </summary>
    public class MIME_Entity : IDisposable
    {
        private bool              m_IsDisposed    = false;
        private MIME_Entity       m_pParent       = null;
        private MIME_h_Collection m_pHeader       = null;
        private MIME_b            m_pBody         = null;
        private MIME_b_Provider   m_pBodyProvider = null;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public MIME_Entity()
        {
            m_pHeader = new MIME_h_Collection(new MIME_h_Provider());
            m_pBodyProvider = new MIME_b_Provider();
        }

        #region method Dispose

        /// <summary>
        /// Cleans up any resources being used. This method is thread-safe.
        /// </summary>
        public void Dispose()
        {
            lock(this){
                if(m_IsDisposed){
                    return;
                }
                m_IsDisposed = true;

                m_pHeader = null;
                m_pParent = null;
            }
        }

        #endregion


        #region static method CreateEntity_Text_Plain

        /// <summary>
        /// Creates text/plain MIME entity.
        /// </summary>
        /// <param name="transferEncoding">Content transfer encoding.</param>
        /// <param name="charset">Charset to use to encode text. If not sure, utf-8 is recommended.</param>
        /// <param name="text">Text.</param>
        /// <returns>Returns created entity.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>transferEncoding</b>, <b>charset</b> or <b>text</b> is null reference.</exception>
        /// <exception cref="NotSupportedException">Is raised when body contains not supported Content-Transfer-Encoding.</exception>
        public static MIME_Entity CreateEntity_Text_Plain(string transferEncoding,Encoding charset,string text)
        {
            if(transferEncoding == null){
                throw new ArgumentNullException("transferEncoding");
            }
            if(charset == null){
                throw new ArgumentNullException("charset");
            }
            if(text == null){
                throw new ArgumentNullException("text");
            }

            MIME_Entity retVal = new MIME_Entity();
            MIME_b_Text body = new MIME_b_Text(MIME_MediaTypes.Text.plain);
            retVal.Body = body;
            body.SetText(transferEncoding,charset,text);

            return retVal;
        }

        #endregion

        #region static method CreateEntity_Text_Html

        /// <summary>
        /// Creates text/html MIME entity.
        /// </summary>
        /// <param name="transferEncoding">Content transfer encoding.</param>
        /// <param name="charset">Charset to use to encode text. If not sure, utf-8 is recommended.</param>
        /// <param name="text">Text.</param>
        /// <returns>Returns created entity.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>transferEncoding</b>, <b>charset</b> or <b>text</b> is null reference.</exception>
        /// <exception cref="NotSupportedException">Is raised when body contains not supported Content-Transfer-Encoding.</exception>
        public static MIME_Entity CreateEntity_Text_Html(string transferEncoding,Encoding charset,string text)
        {
            if(transferEncoding == null){
                throw new ArgumentNullException("transferEncoding");
            }
            if(charset == null){
                throw new ArgumentNullException("charset");
            }
            if(text == null){
                throw new ArgumentNullException("text");
            }

            MIME_Entity retVal = new MIME_Entity();
            MIME_b_Text body = new MIME_b_Text(MIME_MediaTypes.Text.html);
            retVal.Body = body;
            body.SetText(transferEncoding,charset,text);

            return retVal;
        }

        #endregion

        #region static method CreateEntity_Attachment

        /// <summary>
        /// Creates attachment(application/octet-stream) entity.
        /// </summary>
        /// <param name="fileName">File name with path.</param>
        /// <returns>Returns created entity.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>fileName</b> is null reference.</exception>
        public static MIME_Entity CreateEntity_Attachment(string fileName)
        {
            if(fileName == null){
                throw new ArgumentNullException("stream");
            }

            using(Stream stream = File.OpenRead(fileName)){
                return CreateEntity_Attachment(Path.GetFileName(fileName),stream);
            }
        }

        /// <summary>
        /// Creates attachment(application/octet-stream) entity.
        /// </summary>
        /// <param name="attachmentName">Attachment file name what appears in mail message. Value null means argument 'fileName' file name is used.</param>
        /// <param name="fileName">File name with path.</param>
        /// <returns>Returns created entity.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>fileName</b> is null reference.</exception>
        public static MIME_Entity CreateEntity_Attachment(string attachmentName,string fileName)
        {
            if(fileName == null){
                throw new ArgumentNullException("stream");
            }

            using(Stream stream = File.OpenRead(fileName)){
                return CreateEntity_Attachment(string.IsNullOrEmpty(attachmentName) ? Path.GetFileName(fileName) : attachmentName,stream);
            }
        }

        /// <summary>
        /// Creates attachment(application/octet-stream) entity.
        /// </summary>
        /// <param name="attachmentName">Attachment file name what appears in mail message.</param>
        /// <param name="stream">Attachment data stream. Data is read from stream current position.</param>
        /// <returns>Returns created entity.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>attachmentName</b> or <b>stream</b> is null reference.</exception>
        public static MIME_Entity CreateEntity_Attachment(string attachmentName,Stream stream)
        {
            if(attachmentName == null){
                throw new ArgumentNullException("attachmentName");
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            long fileSize = stream.CanSeek ? (stream.Length - stream.Position) : -1;

            MIME_Entity retVal = new MIME_Entity();
            MIME_b_Application body = new MIME_b_Application(MIME_MediaTypes.Application.octet_stream);
            retVal.Body = body;
            body.SetData(stream,MIME_TransferEncodings.Base64);
            retVal.ContentType.Param_Name = Path.GetFileName(attachmentName);

            MIME_h_ContentDisposition disposition = new MIME_h_ContentDisposition(MIME_DispositionTypes.Attachment);
            disposition.Param_FileName           = Path.GetFileName(attachmentName);
            disposition.Param_Size               = fileSize;
            //disposition.Param_CreationDate     = fileInfo.CreationTime;
            //disposition.Param_ModificationDate = fileInfo.LastWriteTime;
            //disposition.Param_ReadDate         = fileInfo.LastAccessTime;
            retVal.ContentDisposition = disposition;

            return retVal;
        }

        #endregion


        #region method ToFile

        /// <summary>
        /// Stores MIME entity to the specified file.
        /// </summary>
        /// <param name="file">File name with path where to store MIME entity.</param>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>file</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public void ToFile(string file,MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
        {
            ToFile(file,headerWordEncoder,headerParmetersCharset,false);
        }

        /// <summary>
        /// Stores MIME entity to the specified file.
        /// </summary>
        /// <param name="file">File name with path where to store MIME entity.</param>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <param name="headerReencode">If true always specified encoding is used for header. If false and header field value not modified, 
        /// original encoding is kept.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>file</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public void ToFile(string file,MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset,bool headerReencode)
        {
            if(file == null){
                throw new ArgumentNullException("file");
            }
            if(file == ""){
                throw new ArgumentException("Argument 'file' value must be specified.");
            }

            using(FileStream fs = File.Create(file)){
                ToStream(fs,headerWordEncoder,headerParmetersCharset,headerReencode);
            }
        }

        #endregion

        #region method ToStream

        /// <summary>
        /// Store MIME enity to the specified stream.
        /// </summary>
        /// <param name="stream">Stream where to store MIME entity. Storing starts form stream current position.</param>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void ToStream(Stream stream,MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
        {
            ToStream(stream,headerWordEncoder,headerParmetersCharset,false);
        }

        /// <summary>
        /// Store MIME enity to the specified stream.
        /// </summary>
        /// <param name="stream">Stream where to store MIME entity. Storing starts form stream current position.</param>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <param name="headerReencode">If true always specified encoding is used for header. If false and header field value not modified, 
        /// original encoding is kept.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void ToStream(Stream stream,MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset,bool headerReencode)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            m_pHeader.ToStream(stream,headerWordEncoder,headerParmetersCharset,headerReencode);
            stream.Write(new byte[]{(int)'\r',(int)'\n'},0,2);
            m_pBody.ToStream(stream,headerWordEncoder,headerParmetersCharset,headerReencode);
        }

        #endregion

        #region method ToString

        /// <summary>
        /// Returns MIME entity as string.
        /// </summary>
        /// <returns>Returns MIME entity as string.</returns>
        public override string ToString()
        {
            return ToString(null,null);
        }

        /// <summary>
        /// Returns MIME entity as string.
        /// </summary>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <returns>Returns MIME entity as string.</returns>
        public string ToString(MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
        {
            return ToString(headerWordEncoder,headerParmetersCharset,false);
        }

        /// <summary>
        /// Returns MIME entity as string.
        /// </summary>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <param name="headerReencode">If true always specified encoding is used for header. If false and header field value not modified, 
        /// original encoding is kept.</param>
        /// <returns>Returns MIME entity as string.</returns>
        public string ToString(MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset,bool headerReencode)
        {
            using(MemoryStream ms = new MemoryStream()){
                ToStream(ms,headerWordEncoder,headerParmetersCharset,headerReencode);

                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        #endregion

        #region method ToByte

        /// <summary>
        /// Returns MIME entity as byte[].
        /// </summary>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <returns>Returns MIME entity as byte[].</returns>
        public byte[] ToByte(MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset)
        {
            return ToByte(headerWordEncoder,headerParmetersCharset,false);
        }

        /// <summary>
        /// Returns MIME entity as byte[].
        /// </summary>
        /// <param name="headerWordEncoder">Header 8-bit words ecnoder. Value null means that words are not encoded.</param>
        /// <param name="headerParmetersCharset">Charset to use to encode 8-bit header parameters. Value null means parameters not encoded.</param>
        /// <param name="headerReencode">If true always specified encoding is used for header. If false and header field value not modified, 
        /// original encoding is kept.</param>
        /// <returns>Returns MIME entity as byte[].</returns>
        public byte[] ToByte(MIME_Encoding_EncodedWord headerWordEncoder,Encoding headerParmetersCharset,bool headerReencode)
        {
            using(MemoryStream ms = new MemoryStream()){
                ToStream(ms,headerWordEncoder,headerParmetersCharset,headerReencode);

                return ms.ToArray();
            }
        }

        #endregion

        #region method DataToStream

        /// <summary>
        /// Stores body decoded-data to the specified stream. Note: This method is available for single part entities only.
        /// </summary>
        /// <param name="stream">Stream where to store body data.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null reference.</exception>
        /// <exception cref="InvalidOperationException">Is raised when this method is called for multipart entity.</exception>
        public void DataToStream(Stream stream)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            if(this.Body == null){
                throw new InvalidOperationException("Mime entity body has been not set yet.");
            }
            if(!(this.Body is MIME_b_SinglepartBase)){
                throw new InvalidOperationException("This method is available only for single part entities, not for multipart.");
            }

            MIME_b_SinglepartBase body = (MIME_b_SinglepartBase)this.Body;
            using(Stream dataStream = body.GetDataStream()){
                Net_Utils.StreamCopy(dataStream,stream,64000);
            }
        }

        #endregion

        #region method DataToFile

        /// <summary>
        /// Stores body decoded-data to the specified file. Note: This method is available for single part entities only.
        /// </summary>
        /// <param name="fileName">File name with path, where to store body data.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>fileName</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="InvalidOperationException">Is raised when this method is called for multipart entity.</exception>
        public void DataToFile(string fileName)
        {
            if(fileName == null){
                throw new ArgumentNullException("fileName");
            }            
            if(fileName == string.Empty){
                throw new ArgumentException("Argument 'fileName' value must be specified.");
            }
            if(this.Body == null){
                throw new InvalidOperationException("Mime entity body has been not set yet.");
            }
            if(!(this.Body is MIME_b_SinglepartBase)){
                throw new InvalidOperationException("This method is available only for single part entities, not for multipart.");
            }

            MIME_b_SinglepartBase body = (MIME_b_SinglepartBase)this.Body;
            using(Stream fs = File.Create(fileName)){
                using(Stream dataStream = body.GetDataStream()){
                    Net_Utils.StreamCopy(dataStream,fs,64000);
                }
            }
        }

        #endregion

        #region method DataToByte

        /// <summary>
        /// Returns body data as byte[]. Note: This method is available for single part entities only.
        /// </summary>
        /// <exception cref="InvalidOperationException">Is raised when this method is called for multipart entity.</exception>
        /// <returns>Returns body data as byte[].</returns>
        public byte[] DataToByte()
        {
            if(this.Body == null){
                throw new InvalidOperationException("Mime entity body has been not set yet.");
            }
            if(!(this.Body is MIME_b_SinglepartBase)){
                throw new InvalidOperationException("This method is available only for single part entities, not for multipart.");
            }

            MemoryStream stream = new MemoryStream();
            MIME_b_SinglepartBase body = (MIME_b_SinglepartBase)this.Body;
            using(Stream dataStream = body.GetDataStream()){
                Net_Utils.StreamCopy(dataStream,stream,64000);
            }

            return stream.ToArray();
        }

        #endregion


        #region method Parse

        /// <summary>
        /// Parses MIME entiry from the specified stream.
        /// </summary>
        /// <param name="stream">Source stream.</param>
        /// <param name="headerEncoding">Header reading encoding. If not sure UTF-8 is recommended.</param>
        /// <param name="defaultContentType">Default content type.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b>,<b>headerEncoding</b> or <b>defaultContentType</b> is null reference.</exception>
        internal protected void Parse(SmartStream stream,Encoding headerEncoding,MIME_h_ContentType defaultContentType)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            if(headerEncoding == null){
                throw new ArgumentNullException("headerEncoding");
            }
            if(defaultContentType == null){
                throw new ArgumentNullException("defaultContentType");
            }
            
            m_pHeader.Parse(stream,headerEncoding); 
          
            m_pBody = m_pBodyProvider.Parse(this,stream,defaultContentType);
            m_pBody.SetParent(this,false);         
        }

        #endregion

        #region method SetParent

        /// <summary>
        /// Sets MIME entity parent entity.
        /// </summary>
        /// <param name="parent">Parent entity.</param>
        internal void SetParent(MIME_Entity parent)
        {
            m_pParent = parent;
        }

        #endregion


        #region Properties Implementation

        // Permanent headerds list: http://www.rfc-editor.org/rfc/rfc4021.txt

        /// <summary>
        /// Gets if this object is disposed.
        /// </summary>
        public bool IsDisposed
        {
            get{ return m_IsDisposed; }
        }

        /// <summary>
        /// Gets if this entity is modified since it has loaded.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is riased when this class is disposed and this property is accessed.</exception>
        public bool IsModified
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                return m_pHeader.IsModified || m_pBody.IsModified; 
            }
        }

        /// <summary>
        /// Gets the parent entity of this entity, returns null if this is the root entity.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public MIME_Entity Parent
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                return m_pParent; 
            }
        }

        /// <summary>
        /// Gets MIME entity header field collection.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        public MIME_h_Collection Header
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                return m_pHeader; 
            }
        }
                                
        /// <summary>
        /// Gets or sets MIME version number. Value null means that header field does not exist. Normally this value is 1.0. Defined in RFC 2045 section 4.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>An indicator that this message is formatted according to the MIME
        /// standard, and an indication of which version of MIME is used.</remarks>
        public string MimeVersion
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("MIME-Version");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("MIME-Version");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("MIME-Version");
                    if(h == null){
                        h = new MIME_h_Unstructured("MIME-Version",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets content body part ID. Value null means that header field does not exist. Defined in RFC 2045 7.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Specifies a Unique ID for one MIME body part of the content of a message.</remarks>
        public string ContentID
        {
            get{ 
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-ID");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                if(value == null){
                    m_pHeader.RemoveAll("Content-ID");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-ID");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-ID",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets description of message body part. Value null means that header field does not exist. Defined in RFC 2045 8.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Description of a particular body part of a message; for example, a caption for an image body part.</remarks>
        public string ContentDescription
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Description");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Description");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Description");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Description",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets content transfer encoding. Value null means that header field does not exist. 
        /// RFC defined values are in <see cref="MIME_TransferEncodings">MIME_TransferEncodings</see>. Defined in RFC 2045 6.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Coding method used in a MIME message body part.</remarks>
        public string ContentTransferEncoding
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Transfer-Encoding");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value.Trim();
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Transfer-Encoding");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Transfer-Encoding");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Transfer-Encoding",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets MIME content type. Value null means that header field does not exist. Defined in RFC 2045 5.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <exception cref="ParseException">Is raised when header field parsing errors.</exception>
        public MIME_h_ContentType ContentType
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Type");
                if(h != null){
                    if(!(h is MIME_h_ContentType)){
                        throw new ParseException("Header field 'ContentType' parsing failed.");
                    }

                    return (MIME_h_ContentType)h;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Type");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Type");
                    if(h == null){
                        m_pHeader.Add(value);
                    }
                    else{
                        m_pHeader.ReplaceFirst(value);
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets base to be used for resolving relative URIs within this content part. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Base to be used for resolving relative URIs within this content part. See also Content-Location.</remarks>
        public string ContentBase
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Base");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Base");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Base");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Base",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets URI for retrieving a body part. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>URI using which the content of this body-part part was retrieved,
        /// might be retrievable, or which otherwise gives a globally unique identification of the content.</remarks>
        public string ContentLocation
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Location");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Location");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Location");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Location",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets content features of a MIME body part. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>The 'Content-features:' header can be used to annotate a MIME body part with a media feature expression, 
        /// to indicate features of the body part content. See also RFC 2533, RFC 2506, and RFC 2045.</remarks>
        public string Contentfeatures
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-features");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                if(value == null){
                    m_pHeader.RemoveAll("Content-features");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-features");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-features",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets content disposition. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Indicates whether a MIME body part is to be shown inline or is an attachment; can also indicate a 
        /// suggested filename for use when saving an attachment to a file.</remarks>
        /// <exception cref="ParseException">Is raised when header field parsing errors.</exception>
        public MIME_h_ContentDisposition ContentDisposition
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Disposition");
                if(h != null){
                    if(!(h is MIME_h_ContentDisposition)){
                        throw new ParseException("Header field 'ContentDisposition' parsing failed.");
                    }

                    return (MIME_h_ContentDisposition)h;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Disposition");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Disposition");
                    if(h == null){
                        m_pHeader.Add(value);
                    }
                    else{
                        m_pHeader.ReplaceFirst(value);
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets language of message content. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Can include a code for the natural language used in a message; e.g., 'en' for English. 
        /// Can also contain a list of languages for a message containing more than one language.</remarks>
        public string ContentLanguage
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Language");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Language");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Language");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Language",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets message alternative content. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Information about the media features of alternative content formats available for the current message.</remarks>
        public string ContentAlternative
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Alternative");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Alternative");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Alternative");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Alternative",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }
        
        /// <summary>
        /// Gets or sets content MD5 checksum. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Checksum of content to ensure that it has not been modified.</remarks>
        public string ContentMD5
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-MD5");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-MD5");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-MD5");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-MD5",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }
        
        /// <summary>
        /// Gets or sets time duration of content. Value null means that header field does not exist.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this property is accessed.</exception>
        /// <remarks>Time duration of body part content, in seconds (e.g., for audio message).</remarks>
        public string ContentDuration
        {
            get{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }

                MIME_h h = m_pHeader.GetFirst("Content-Duration");
                if(h != null){
                    return ((MIME_h_Unstructured)h).Value;
                }
                else{
                    return null;
                }
            }

            set{
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                
                if(value == null){
                    m_pHeader.RemoveAll("Content-Duration");
                }
                else{
                    MIME_h h = m_pHeader.GetFirst("Content-Duration");
                    if(h == null){
                        h = new MIME_h_Unstructured("Content-Duration",value);
                        m_pHeader.Add(h);
                    }
                    else{
                        ((MIME_h_Unstructured)h).Value = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets MIME entity body.
        /// </summary>
        /// <exception cref="ArgumentNullException">Is raised when null reference passed.</exception>
        public MIME_b Body
        {
            get{ return m_pBody; }

            set{
                if(value == null){
                    throw new ArgumentNullException("Body");
                }

                m_pBody = value;
                m_pBody.SetParent(this,true);
            }
        }
                        
        #endregion

    }
}
