using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Security;

using SharePointProducts.CopyMove.Samples.CopyMove;


namespace SharePointProducts.CopyMove.Samples
{
    /// <summary>
    /// This sample class demonstrates how to use the CopyMove WCF Service 
    /// at http://server/_vti_bin/CopyMove.svc
    /// All the interesting action can be found in the Run(...) method.
    /// </summary>
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Load += Form1_Load;
            FormClosing += Form1_FormClosing;
        }

        #region UI Code

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            IList<string> lines = new List<string>(2);
            lines.Add(sourceFolderTextBox.Text);
            lines.Add(targetFolderTextBox.Text);
            lines.Add(userNameTextBox.Text);
            File.WriteAllLines("FormValues.txt", lines);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            if (File.Exists("FormValues.txt"))
            {
                string[] lines = File.ReadAllLines("FormValues.txt");
                if (lines.Length > 0) sourceFolderTextBox.Text = lines[0];
                if (lines.Length > 1) targetFolderTextBox.Text = lines[1];
                if (lines.Length > 2) userNameTextBox.Text = lines[2];
            }
        }

        private void RefreshButtonClick(object sender, EventArgs e)
        {
            EnableButtons(false);
            sourceItemsListBox.Items.Clear();

            if (!String.IsNullOrEmpty(sourceFolderTextBox.Text))
            {
                UpdateSourceItems(sourceFolderTextBox.Text);
            }

            EnableButtons(true);
            CopyButton.Enabled = sourceItemsListBox.Items.Count > 0;
            MoveButton.Enabled = CopyButton.Enabled;
        }


        private void CopyButtonClick(object sender, EventArgs e)
        {
            Run(Operation.Copy);
        }


        private void MoveButtonClick(object sender, EventArgs e)
        {
            Run(Operation.Move);
        }


        private void ExitButtonClick(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void useCurrentCredentialsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            userNameTextBox.Enabled = !useCurrentCredentialsCheckBox.Checked;
            passwordTextBox.Enabled = !useCurrentCredentialsCheckBox.Checked;
        }

        private void EnableButtons(bool enable)
        {
            RefreshButton.Enabled = enable;
            CopyButton.Enabled = enable;
            MoveButton.Enabled = enable;
            ExitButton.Enabled = enable;
        }


        private void UpdateSourceItems(string url)
        {
            try
            {
                CopyMoveServiceClient proxy = GetProxy(url);
                CopyMoveItem[] items = proxy.GetItems(url);
                foreach (CopyMoveItem item in items)
                {
                    int filenameIndex = item.Url.LastIndexOf('/');
                    string filename = item.Url.Substring(filenameIndex + 1);
                    string title = item.Title;
                    sourceItemsListBox.Items.Add(new ListItem(item.UniqueId, item.Id, filename, title));
                }
            }

            catch (ApplicationException ex)
            {
                MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            catch (Exception ex)
            {
                MessageBox.Show(this, ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        private ListItem[] GetSelectedSourceItems()
        {
            var items = new List<ListItem>();
            foreach (ListItem item in sourceItemsListBox.CheckedItems)
            {
                items.Add(item);
            }
            return items.ToArray();
        }


        private void Run(Operation operation)
        {
            try
            {
                EnableButtons(false);

                string sourceFolderUrl = sourceFolderTextBox.Text;
                string targetFolderUrl = targetFolderTextBox.Text;
                ListItem[] selectedSourceItems = GetSelectedSourceItems();

                Run(operation, sourceFolderUrl, targetFolderUrl, selectedSourceItems);
            }

            catch (ApplicationException ex)
            {
                MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            catch (Exception ex)
            {
                MessageBox.Show(this, ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            finally
            {
                EnableButtons(true);
            }
        }

        #endregion

        #region API Code

        private void Run(Operation operation, string sourceFolderUrl, string targetFolderUrl, ListItem[] selectedSourceItems)
        {
            //
            // Create WCF Proxy from the specified source folder url.
            //
            CopyMoveServiceClient proxy = GetProxy(sourceFolderUrl);

            //
            // Create CopyMove transaction
            //
            var transaction = new CopyMoveItemTransaction()
            {
                OverwriteFiles = true,
                IncludeTimestamps = true,           // Include item created and item modified information
                IncludeUserInfo = true,             // Include created by and modified by information
                IncludeVersions = true,             // Include version history
                IncludeSecurity = true,             // Include item permissions
                SourceUrl = sourceFolderUrl,        // The absolute URL of the source folder or source list
                TargetUrl = targetFolderUrl,        // The absolute URL of the target folder or target list
                EnforceRequiredFieldValues = true,  // Enforce values for required fields                

                // Optional: Exclude one or more list columns 
                ExcludeProperties = new[] { "FieldName1", "FieldName2" },

                HaltOnWarning = !ignoreWarningsCheckBox.Checked
            };

            //
            // If transaction.EnforceRequiredFieldValues is true then specify default field values as follows.
            // Otherwise, CopyMove will report errors for missing values on source items.
            //
            /*transaction.DefaultFieldValues = new[]
            {
                "RequiredText=Lorem ipsum...",
                "RequiredComment=Lorem ipsum...",
                "RequiredDate=2012-04-29T19:12:53",
                "RequiredSingleChoice=Value",
                "RequiredMultiChoice=;#Value1;#Value2e;#Value3;#",
                "RequiredLookupValue=3",
                "RequiredMetaDataValue=Green|6771511d-cc77-4c42-8d8d-fe40e64981e7",
                "RequiredMetaDataValues=Blue|dd2ac6dd-e197-4dda-bd04-e7e25b7a977e;White|f551895a-136b-4044-bf76-3eb226e18a69",
                "RequiredNumber=27",
                "RequiredCurrency=1234",
                "RequiredHyperlink=http://www.sharepointproducts.com, SharePoint Products",
                "RequiredUser=-1;#i:0#.w|dom\\laf"
            };*/

            //
            // The source items to copy or move. Each item can be specified by
            // its absolute URL, relative URL, filename, list item GUID or list item ID
            //
            var items = new string[selectedSourceItems.Length];
            for (int i = 0; i < selectedSourceItems.Length; i++)
            {
                items[i] = selectedSourceItems[i].UniqueId.ToString();  // List item GUID
                //items[i] = selectedSourceItems[i].Filename; // List item filename (FileLeafRef)                
                //items[i] = selectedSourceItems[i].Id.ToString();  // List item ID
            }
            transaction.Items = items;

            //
            // Start the Copy/Move process in a separate thread. The CopyAsync
            // and MoveAsync methods returns immediately after launching the 
            // background thread that doesthe actual copying and moving of items. 
            //
            CopyMoveResult result;
            if (operation == Operation.Copy)
                result = proxy.Copy(transaction);
            else
                result = proxy.Move(transaction);

            //
            // Check and show the result of the CopyMove transaction. 
            // Possible outcomes are success, warning or error.
            // The CopyMove service does not propagate exceptions to clients from errors that
            // occurred during a Copy or Move transaction. Clients can only get exceptions from 
            // communication problems with the WCF service.
            // 
            if (result.ErrorCode == 0)
            {
                string message;
                if (operation == Operation.Copy)
                    message = "The seleced items were successfully copied to the target folder";
                else
                    message = "The seleced items were successfully moved to the target folder";
                MessageBox.Show(this, message, "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
            } else if (result.ErrorCode == 1)
            {
                var warnings = new StringBuilder();
                foreach (CopyMoveMessage message in result.Warnings)
                {
                    warnings.Append("Warning ").Append(message.Id).Append(": ").Append(message.Text).Append(Environment.NewLine);
                }
                MessageBox.Show(this, warnings.ToString(), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            } else if (result.ErrorCode == 2)
            {
                var errors = new StringBuilder();
                foreach (CopyMoveMessage message in result.Errors)
                {
                    errors.Append("Error ").Append(message.Id).Append(": ").Append(message.Text).Append(Environment.NewLine);
                }
                MessageBox.Show(this, errors.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        /// <summary>
        /// Creates a connection to the CopyMove WCF service from the specified source folder URL.
        /// </summary>
        /// <param name="sourceFolderUrl"></param>
        /// <returns></returns>
        private CopyMoveServiceClient GetProxy(string sourceFolderUrl)
        {
            var serviceUrls = new List<string>();
            CopyMoveServiceClient proxy = null;
            string siteUrl = sourceFolderUrl;
            while (true)
            {
                //
                // We cannot for sure derive the SharePoint site URL from the source folder URL.
                // Consequently, we just try shorter and shorter URLs until we have a connection 
                // to the CopyMove service.
                //
                int index = siteUrl.LastIndexOf('/');
                if (index < 8) break;
                siteUrl = siteUrl.Substring(0, index);
                string serviceUrl = siteUrl + "/_vti_bin/CopyMove.svc";
                serviceUrls.Add(serviceUrl);

                try
                {
                    //
                    // Create proxy that authenticates with the current user
                    //
                    var binding = new BasicHttpBinding();
                    if (serviceUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                    {
                        binding.Security.Mode = BasicHttpSecurityMode.Transport;
                    } else if (serviceUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
                    {
                        binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
                    }
                    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;

                    var endpoint = new EndpointAddress(serviceUrl);
                    proxy = new CopyMoveServiceClient(binding, endpoint);

                    if (useCurrentCredentialsCheckBox.Checked)
                    {
                        proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
                    } else
                    {
                        proxy.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(userNameTextBox.Text, passwordTextBox.Text);
                    }

                    //
                    // Get the CopyMove version number. This service call will fail if a 
                    // connection to the WCF Service cannot be established.
                    //
                    string version = proxy.GetVersion();

                    // Success - do not try more service URLs
                    break;
                }

                catch (EndpointNotFoundException ex)
                {
                    proxy = null;
                }

                catch (FaultException ex)
                {
                    proxy = null;
                }
            }

            if (proxy == null)
            {
                string message = "Cannot connect to the CopyMove WCF Service at any of the following end-points:";
                foreach (string serviceUrl in serviceUrls)
                {
                    message += Environment.NewLine;
                    message += serviceUrl;
                }
                throw new ApplicationException(message);
            }

            return proxy;
        }


        private enum Operation
        {
            Copy,
            Move
        }


        private class ListItem
        {
            private readonly Guid _uniqueId;
            private readonly int _id;
            private readonly string _filename;
            private readonly string _title;

            internal ListItem(Guid uniqueId, int id, string filename, string title)
            {
                _uniqueId = uniqueId;
                _id = id;
                _filename = filename;
                _title = title;
            }

            public Guid UniqueId
            {
                get { return _uniqueId; }
            }

            public int Id
            {
                get { return _id; }
            }

            internal string Filename
            {
                get { return _filename; }
            }

            internal string Title
            {
                get { return _title; }
            }

            public override string ToString()
            {
                if (!String.IsNullOrEmpty(_title) && _filename.EndsWith(".000"))
                {
                    return _title;
                }
                return _filename;
            }
        }

        #endregion
    }
}
