//  
//   Rebex Sample Code License
// 
//   Copyright 2025, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;

using Rebex.Mail;
using Rebex.Mime.Headers;

namespace Rebex.Samples
{
    /// <summary>
    /// MsgMessage editor.
    /// </summary>
    public partial class MailEditor : System.Windows.Forms.Form
    {
        // Indicates whether the message was modified.
        private bool _modified;

        // Current mail message.
        private MsgMessage _message;

        // Message file path.
        private string _path;

        /// <summary>
        /// Constructor.
        /// </summary>
        public MailEditor()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            _message = new MsgMessage();
        }

        /// <summary>
        /// Saves the message to its current location.
        /// </summary>
        private void SaveMessage()
        {
            if (_path == null)
            {
                _path = SaveAs();
                return;
            }

            SaveMessage(_path);
        }

        /// <summary>
        /// Saves message.
        /// </summary>
        private void SaveMessage(string path)
        {
            try
            {
                MsgMessage message = ToMessage();
                message.Save(path);
                _modified = false;
                MessageBox.Show("Message saved.");
            }
            catch (Exception x)
            {
                MessageBox.Show(x.ToString());
            }
        }

        /// <summary>
        /// Creates new message.
        /// </summary>
        private void NewMessage()
        {
            if (!CheckModifications())
                return;

            MsgMessage message = new MsgMessage();
            LoadMessage(message);
            _path = null;
        }

        /// <summary>
        /// Closes the message.
        /// </summary>
        private void CloseMessage()
        {
            if (!CheckModifications())
                return;

            base.Close();
        }

        /// <summary>
        /// Opens a new message.
        /// </summary>
        private void OpenMessage()
        {
            if (!CheckModifications())
                return;

            if (openFileDialogMessage.ShowDialog() != DialogResult.OK)
                return;

            LoadMessage(openFileDialogMessage.FileName);
        }

        /// <summary>
        /// Asks the user whether to save or discard modifications, and saves the message if desired.
        /// </summary>
        /// <returns>True if caller should proceed, false if cancel was selected.</returns>
        private bool CheckModifications()
        {
            if (!_modified)
                return true;

            DialogResult res = MessageBox.Show(this, "Would you like to save the changes you made to the current message?", "Message Editor", MessageBoxButtons.YesNoCancel);
            if (res == DialogResult.No)
                return true;
            if (res != DialogResult.Yes)
                return false;

            if (_path == null)
                _path = SaveAs();
            else
                SaveMessage(_path);

            return true;
        }

        /// <summary>
        /// Saves attachments to a folder.
        /// </summary>
        private void SaveAttachments()
        {
            if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
                return;

            string path = folderBrowserDialog.SelectedPath;
            foreach (var att in _message.Attachments)
            {
                att.Save(Path.Combine(path, att.FileName));
            }
        }

        /// <summary>
        /// Saves message to a new file.
        /// </summary>
        private string SaveAs()
        {
            if (saveFileDialogMessage.ShowDialog() != DialogResult.OK)
                return null;

            string path = saveFileDialogMessage.FileName;

            SaveMessage(path);

            return path;
        }

        /// <summary>
        /// Removes attachment specified by file name.
        /// </summary>
        private void RemoveAttachment(string name)
        {
            listBoxAttachments.Items.Remove(name);

            for (int i = 0; i < _message.Attachments.Count; i++)
            {
                if (_message.Attachments[i].FileName == name)
                {
                    _message.Attachments.RemoveAt(i);
                    _modified = true;
                    break;
                }
            }
        }

        /// <summary>
        /// Loads a mail message.
        /// </summary>
        public void LoadMessage(string path)
        {
            try
            {
                MsgMessage message = new MsgMessage();
                _path = path;
                message.Load(_path);
                LoadMessage(message);
            }
            catch (Exception err)
            {
                MessageBox.Show(err.ToString());
            }
        }

        /// <summary>
        /// Creates MSG message from dialog's data.
        /// </summary>
        private MsgMessage ToMessage()
        {
            MsgMessage msg = _message;

            msg.Subject = txtSubject.Text;
            msg.SetBody(txtBody.Text);

            return msg;
        }

        /// <summary>
        /// Initializes the editor with MsgMessage data.
        /// </summary>
        private void LoadMessage(MsgMessage message)
        {
            _message = message;

            // allow editing of SMTP addresses only
            txtFrom.Enabled = IsSmtpAddress(_message.From);
            txtTo.Enabled = _message.To.All(address => IsSmtpAddress(address));
            txtCc.Enabled = _message.CC.All(address => IsSmtpAddress(address));

            txtFrom.Text = MsgAddressFormatter.ToString(_message.From);
            txtTo.Text = MsgAddressFormatter.ToString(_message.To);
            txtCc.Text = MsgAddressFormatter.ToString(_message.CC);

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

            listBoxAttachments.Items.Clear();
            for (int i = 0; i < _message.Attachments.Count; i++)
            {
                var attachment = _message.Attachments[i];

                string name = attachment.FileName;
                if (string.IsNullOrEmpty(name))
                    name = string.Format("ATT{0}.DAT", i);

                listBoxAttachments.Items.Add(name);
            }

            // update Window title
            Text = message.Subject;

            _modified = false;
        }

        /// <summary>
        /// Indicates whether the specified address has SMTP type.
        /// </summary>
        private bool IsSmtpAddress(MsgAddress address)
        {
            return address.AddressType == MsgAddressType.Smtp;
        }

        /// <summary>
        /// Deletes attachment.
        /// </summary>
        private void menuItemDelete_Click(object sender, System.EventArgs e)
        {
            if (listBoxAttachments.SelectedItem == null)
                return;

            string file = listBoxAttachments.SelectedItem.ToString();
            RemoveAttachment(file);
        }

        /// <summary>
        /// Adds new attached file.
        /// </summary>
        private void menuItemFileAttachment_Click(object sender, System.EventArgs e)
        {
            if (openFileDialogAttachment.ShowDialog() != DialogResult.OK)
                return;

            var att = _message.Attachments.Add(openFileDialogAttachment.FileName, null);
            listBoxAttachments.Items.Add(att.FileName);

            _modified = true;
        }

        /// <summary>
        /// Creates new message.
        /// </summary>
        private void menuItemNewMessage_Click(object sender, System.EventArgs e)
        {
            NewMessage();
        }

        /// <summary>
        /// Opens message from file.
        /// </summary>
        private void menuItemOpenMessage_Click(object sender, System.EventArgs e)
        {
            OpenMessage();
        }

        /// <summary>
        /// Saves message to file.
        /// </summary>
        private void menuItemSave_Click(object sender, System.EventArgs e)
        {
            SaveMessage();
        }

        /// <summary>
        /// Saves message to a new file.
        /// </summary>
        private void menuItemSaveAs_Click(object sender, System.EventArgs e)
        {
            SaveAs();
        }

        /// <summary>
        /// Saves all attachments to folder.
        /// </summary>
        private void menuItemSaveAttachments_Click(object sender, System.EventArgs e)
        {
            SaveAttachments();
        }

        /// <summary>
        /// Closes application.
        /// </summary>
        private void menuItemClose_Click(object sender, System.EventArgs e)
        {
            CloseMessage();
        }

        /// <summary>
        /// Handles 'From' textbox changes.
        /// </summary>
        private void txtFrom_TextChanged(object sender, System.EventArgs e)
        {
            txtFrom.BackColor = SystemColors.Window;
            if (!txtFrom.Enabled)
                return;

            _message.From = null;
            _modified = true;

            string text = txtFrom.Text.Trim();
            if (text.Length == 0)
                return;

            var addresses = MsgAddressFormatter.ParseSmtpAddressCollection(text);
            if (addresses == null || addresses.Count != 1)
            {
                txtFrom.BackColor = Color.Red;
            }
            else
            {
                var add = addresses[0];
                _message.From = new MsgAddress(add.Address, add.DisplayName);
            }
        }

        /// <summary>
        /// Handles 'To' textbox changes.
        /// </summary>
        private void txtTo_TextChanged(object sender, System.EventArgs e)
        {
            txtTo.BackColor = SystemColors.Window;
            if (!txtTo.Enabled)
                return;

            _message.To.Clear();
            _modified = true;

            string text = txtTo.Text.Trim();
            if (text.Length == 0)
                return;

            var addresses = MsgAddressFormatter.ParseSmtpAddressCollection(text);
            if (addresses == null)
            {
                txtTo.BackColor = Color.Red;
            }
            else
            {
                foreach (MailAddress add in addresses)
                {
                    _message.To.Add(add.Address, add.DisplayName);
                }
            }
        }

        /// <summary>
        /// Handles 'Cc' textbox changes.
        /// </summary>
        private void txtCc_TextChanged(object sender, System.EventArgs e)
        {
            txtCc.BackColor = SystemColors.Window;
            if (!txtCc.Enabled)
                return;

            _message.CC.Clear();
            _modified = true;

            string text = txtCc.Text.Trim();
            if (text.Length == 0)
                return;

            var addresses = MsgAddressFormatter.ParseSmtpAddressCollection(text);
            if (addresses == null)
            {
                txtCc.BackColor = Color.Red;
            }
            else
            {
                foreach (MailAddress add in addresses)
                {
                    try
                    {
                        _message.CC.Add(add.DisplayName, add.Address);
                    }
                    catch (ArgumentException)
                    {
                        txtCc.BackColor = Color.Red;
                    }
                }
            }
        }

        /// <summary>
        /// Handles 'Subject' textbox changes.
        /// </summary>
        private void txtSubject_TextChanged(object sender, System.EventArgs e)
        {
            _modified = true;

            // update Window title
            Text = txtSubject.Text;
        }

        /// <summary>
        /// Handles 'Body' textbox changes.
        /// </summary>
        private void txtBody_TextChanged(object sender, System.EventArgs e)
        {
            _modified = true;
        }
    }
}
