using System;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ComponentPro.Net.Mail;

namespace MimeSamples
{
    public partial class MimeComposer : Form
    {
        private readonly bool _exception;
        private bool _changed;
        private string _mailFileName;
        private AttachmentCollection _attachments;
        private bool _internalChanges;

        public MimeComposer()
        {
            try
            {
                InitializeComponent();
            }
            catch (ComponentPro.Licensing.Mail.UltimateLicenseException exc)
            {
                MessageBox.Show(exc.Message, "Error");
                _exception = true;
                return;
            }

            // Attachs the TextChanged event handler to all text boxes.
            txtBcc.TextChanged += txt_TextChanged;
            txtBody.TextChanged += txt_TextChanged;
            txtCc.TextChanged += txt_TextChanged;
            txtFrom.TextChanged += txt_TextChanged;
            txtSubject.TextChanged += txt_TextChanged;
            txtTo.TextChanged += txt_TextChanged;

            // New messages.
            NewMesssage();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (_exception)
                this.Close();
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            if (AskSaving())
                e.Cancel = true;

            base.OnClosing(e);
        }

        void txt_TextChanged(object sender, EventArgs e)
        {
            // Changes the form's title if needed.
            bool b = _changed;
            _changed = true;
            saveButton.Enabled = true;
            if (b == false)
                SetTitle();
        }

        private void SetTitle()
        {
            if (!_internalChanges)
            {
                // Sets form's title.
                string str = "Ultimate Mime Composer";

                if (_mailFileName != null)
                    str += " - " + Path.GetFileName(_mailFileName);

                if (txtSubject.Text.Length > 0)
                    str += " - " + txtSubject.Text;

                if (_changed)
                    str += "*";

                this.Text = str;
            }
        }

        private void txtSubject_TextChanged(object sender, EventArgs e)
        {
            // Changes the form's title whenever the subject is changed.
            SetTitle();
        }

        /// <summary>
        /// Creates new message.
        /// </summary>
        private void NewMesssage()
        {
            _internalChanges = true;
            txtBcc.Text = string.Empty;
            txtBody.Text = string.Empty;
            txtCc.Text = string.Empty;
            txtFrom.Text = string.Empty;
            txtSubject.Text = string.Empty;
            txtTo.Text = string.Empty;
            cbxAttachment.Items.Clear();
            _mailFileName = null;
            _attachments = null;
            _changed = false;
            _internalChanges = false;
            SetTitle();
            saveAttachmentsButton.Enabled = false;
            saveButton.Enabled = false;
        }

        /// <summary>
        /// Opens an existing message.
        /// </summary>
        /// <param name="fileName"></param>
        private void OpenMessage(string fileName)
        {
            _internalChanges = true;
            MailMessage message = new MailMessage();
            _mailFileName = fileName;
            message.Load(fileName);

            txtSubject.Text = message.Subject;
            txtBody.Text = message.BodyText.Replace("\n", "\r\n");

            StringBuilder sb = new StringBuilder();

            foreach (MailAddress to in message.To)
            {
                string address = to.ToString();
                if (address.Length > 0)
                {
                    sb.Append(address);
                    sb.Append(';');
                }
            }
            if (sb.Length > 0)
                sb.Length--;
            txtTo.Text = sb.ToString();

            sb.Length = 0;
            foreach (MailAddress bcc in message.Bcc)
            {
                string address = bcc.ToString();
                if (address.Length > 0)
                {
                    sb.Append(address);
                    sb.Append(';');
                }
            }
            if (sb.Length > 0)
                sb.Length--;
            txtBcc.Text = sb.ToString();

            sb.Length = 0;
            foreach (MailAddress cc in message.Cc)
            {
                string address = cc.ToString();
                if (address.Length > 0)
                {
                    sb.Append(address);
                    sb.Append(';');
                }
            }
            if (sb.Length > 0)
                sb.Length--;
            txtCc.Text = sb.ToString();

            sb.Length = 0;
            foreach (MailAddress from in message.From)
            {
                string address = from.ToString();
                if (address.Length > 0)
                {
                    sb.Append(address);
                    sb.Append(';');
                }
            }
            if (sb.Length > 0)
                sb.Length--;
            txtFrom.Text = sb.ToString();

            if (message.Attachments.Count > 0)
            {
                _attachments = new AttachmentCollection();
                foreach (Attachment att in message.Attachments)
                {
                    _attachments.Add(att);
                    cbxAttachment.Items.Add(Path.GetFileName(att.FileName));
                }
                cbxAttachment.SelectedIndex = 0;
                btnRemoveAttachment.Enabled = true;
                saveAttachmentsButton.Enabled = true;
            }
            else
                saveAttachmentsButton.Enabled = false;
            _changed = false;            
            saveButton.Enabled = false;
            _internalChanges = false;
        }

        /// <summary>
        /// Shows Save as dialog and save the message to the specified file.
        /// </summary>
        private void SaveMessageAs()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            try
            {
                dlg.OverwritePrompt = true;
                dlg.Filter = "Email files (*.eml)|*.eml|All files (*.*)|*.*";
                dlg.FilterIndex = 1;
                dlg.Title = "Save e-mail message as";
                if (dlg.ShowDialog() != DialogResult.OK)
                    return;

                _mailFileName = dlg.FileName;
                SaveMessageAs(dlg.FileName);                
            }
            catch
            {
                MessageBox.Show(dlg.FileName + " is not an email file", "Error");
            }
        }

        /// <summary>
        /// Creates a new Message object.
        /// </summary>
        /// <returns>A new Message object.</returns>
        private MailMessage CreateMessage()
        {
            MailMessage message = new MailMessage();

            message.Subject = txtSubject.Text;
            message.BodyText = txtBody.Text;

            foreach (string to in txtTo.Text.Split(new char[] {';', ','}))
            {
                string email = to.Trim();
                if (email.Length > 0)
                    message.To.Add(email);
            }

            foreach (string bcc in txtBcc.Text.Split(new char[] { ';', ',' }))
            {
                string email = bcc.Trim();
                if (email.Length > 0)
                    message.Bcc.Add(email);
            }

            foreach (string cc in txtCc.Text.Split(new char[] { ';', ',' }))
            {
                string email = cc.Trim();
                if (email.Length > 0)
                    message.Cc.Add(email);
            }

            foreach (string from in txtFrom.Text.Split(new char[] { ';', ',' }))
            {
                string email = from.Trim();
                if (email.Length > 0)
                    message.From.Add(email);
            }

            if (_attachments != null)
                foreach (Attachment att in _attachments)
                {
                    message.Attachments.Add(att);
                }

            return message;
        }

        /// <summary>
        /// Saves the current message as a file.
        /// </summary>
        /// <param name="fileName"></param>
        private void SaveMessageAs(string fileName)
        {
            MailMessage message = CreateMessage();
            message.Save(fileName);
            _changed = false;
            saveButton.Enabled = false;
            SetTitle();
        }

        private void SaveMessage()
        {
            if (_mailFileName == null)
            {
                SaveMessageAs();
            }
            else
            {
                try
                {
                    SaveMessageAs(_mailFileName);
                }
                catch (Exception exc)
                {
                    MessageBox.Show(string.Format("Unable to save the message to file: '{0}'\r\n{1}", _mailFileName, exc.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }
            }
        }

        /// <summary>
        /// Saves attachments to a specific folder.
        /// </summary>
        private void SaveAttachmentAs(string folder)
        {
            foreach (Attachment att in _attachments)
            {
                att.Save(Path.Combine(folder, att.FileName));
            }
        }

        /// <summary>
        /// Saves attachments to a specific folder. A browing folder dialog will be shown.
        /// </summary>
        private void SaveAttachmentAs()
        {
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
            if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
                return;

            string path = folderBrowserDialog.SelectedPath;
            SaveAttachmentAs(path);
        }

        /// <summary>
        /// If the message has not been saved, shows a message box asking user to save the changes.
        /// </summary>
        /// <returns>A bool value indicating whether the closing action should be cancelled.</returns>
        private bool AskSaving()
        {
            if (_changed)
            {
                DialogResult result = MessageBox.Show("Do you want to save the changes you have made?", "Mime Composer", MessageBoxButtons.YesNoCancel);
                if (result == DialogResult.Cancel)
                    return true;
                if (result == DialogResult.Yes)
                    SaveMessage();
            }

            return false;
        }

        private void newMessageButton_Click(object sender, EventArgs e)
        {
            if (!AskSaving())
                NewMesssage();
        }

        private void openMessageButton_Click(object sender, EventArgs e)
        {
            if (!AskSaving())
            {
                OpenFileDialog dlg = new OpenFileDialog();
                try
                {
                    dlg.Filter = "Email files (*.eml)|*.eml|Outlook files (*.msg)|*.msg|All files (*.*)|*.*";
                    dlg.FilterIndex = 1;
                    dlg.Title = "Select an email file";
                    if (dlg.ShowDialog() != DialogResult.OK)
                        return;

                    OpenMessage(dlg.FileName);
                    SetTitle();
                }
                catch
                {
                    MessageBox.Show(dlg.FileName + " is not an email file", "Error");
                }
            }
        }

        private void saveButton_Click(object sender, EventArgs e)
        {
            if (_changed)
                SaveMessage();
        }

        private void saveAsButton_Click(object sender, EventArgs e)
        {
            SaveMessageAs();
        }

        private void saveAttachmentsButton_Click(object sender, EventArgs e)
        {
            SaveAttachmentAs();
        }

        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void cutButton_Click(object sender, EventArgs e)
        {
            txtBody.Cut();
        }

        private void copyButton_Click(object sender, EventArgs e)
        {
            txtBody.Copy();
        }

        private void pasteButton_Click(object sender, EventArgs e)
        {
            txtBody.Paste();
        }

        private void selectAllButton_Click(object sender, EventArgs e)
        {
            txtBody.SelectAll();
        }

        private void btnAddAttachment_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            try
            {
                bool b = _changed;

                dlg.Filter = "All files (*.*)|*.*";
                dlg.FilterIndex = 1;
                dlg.Title = "Select a file";
                dlg.Multiselect = true;
                if (dlg.ShowDialog() != DialogResult.OK)
                    return;

                // Creates an AttachmentCollection if _attachments is null.
                if (_attachments == null)
                    _attachments = new AttachmentCollection();

                // Adds selected files to the attachment collection and the combo box as well.
                foreach (string fileName in dlg.FileNames)
                {
                    _attachments.Add(fileName);
                    cbxAttachment.Items.Add(System.IO.Path.GetFileName(fileName));
                }
                // Selects the last item.
                cbxAttachment.SelectedIndex = cbxAttachment.Items.Count - 1;                

                btnRemoveAttachment.Enabled = true;
                _changed = true;
                saveButton.Enabled = true;
                saveAttachmentsButton.Enabled = true;
                if (b == false) // sets the title if needed.
                    SetTitle();
            }
            catch
            {
                MessageBox.Show(dlg.FileName + " is not a file", "Error");
            }
        }

        private void btnRemoveAttachment_Click(object sender, EventArgs e)
        {
            bool b = _changed;

            // Removes an attachment from the collection.
            _attachments.RemoveAt(cbxAttachment.SelectedIndex);

            if (_attachments.Count > 0)
            {
                // and the combo box.
                cbxAttachment.Items.RemoveAt(cbxAttachment.SelectedIndex);
            }
            else
            {
                cbxAttachment.SelectedItem = null;
                cbxAttachment.Items.Clear();
            }

            if (cbxAttachment.Items.Count == 0)
            {
                // Disables the "Remove" button. 
                btnRemoveAttachment.Enabled = false;
                _attachments = null;

                saveAttachmentsButton.Enabled = false;
            }
            else if (cbxAttachment.SelectedIndex == -1)
                cbxAttachment.SelectedIndex = cbxAttachment.Items.Count - 1;
            _changed = true;
            saveButton.Enabled = true;
            if (b == false)
                SetTitle();
        }
    }
}