using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration.Claims;
using System.IO;


namespace SharePointProducts.CopyMove.Samples
{
    /// <summary>
    /// This sample class demonstrates how to use the CopyMove API. 
    /// All the interesting action can be found in the method
    /// Run(...) below
    /// </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);
            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];
            }
        }

        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 UpdateSourceItems(string url)
        {
            try
            {
                var site = new SPSite(url);
                SPWeb web = site.OpenWeb();

                int pathIndex = url.IndexOf("/", 9, StringComparison.Ordinal);
                string path = url.Substring(pathIndex);

                SPList list = web.GetList(path);
                SPFolder folder = web.GetFolder(path);

                var query = new SPQuery
                {
                    Folder = folder,
                    RowLimit = 100
                };

                foreach (SPListItem item in list.GetItems(query))
                {
                    var filename = (string)item["FileLeafRef"];
                    var title = (string)item["Title"];
                    sourceItemsListBox.Items.Add(new ListItem(item.UniqueId, item.ID, filename, title));
                }
            }

            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 EnableButtons(bool enable)
        {
            RefreshButton.Enabled = enable;
            CopyButton.Enabled = enable;
            MoveButton.Enabled = enable;
            ExitButton.Enabled = enable;
        }

        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 (Exception ex)
            {
                MessageBox.Show(this, ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            finally
            {
                EnableButtons(true);
            }
        }

        #endregion

        #region API Code

        /// <summary>
        /// This is the method to study in order to understand how the CopyMove API works.
        /// </summary>
        private void Run(Operation operation, string sourceFolderUrl, string targetFolderUrl, ListItem[] selectedSourceItems)
        {
            //
            // Create CopyMove settings object 
            //
            var settings = new CopyMoveSettings
            {
                MaxTransactionSize = 250 * 1024 * 1024,     // Max. 250 MB
                MaxTransactionItemCount = 200,              // Max. 200 items
                WarnOnContentTypeLoss = true,
                WarnOnVersionHistoryLoss = true,
                UseRecycleBin = false,                      // Leave source items in the recycle bin on move
                TemporaryFilesLocation = "."                // Folder where CopyMove may temporarily store data. The current user (application pool account) must have full access to the specified folder.
            };

            //
            // Create new CopyMove processor with the settings from above plus a context user
            // that CopyMove will validate SharePoint permissions against. The transaction
            // is also audited with this user if auditing is enabled on the source site.
            //
            //string contextUser = "SHAREPOINT\\SYSTEM";                                // Disable any permissions checking for the CopyMove transaction
            string contextUser = GetContextUserLogin(sourceFolderUrl, WindowsIdentity.GetCurrent().Name);       // Get claims encoded user login
            var processor = new CopyMoveProcessor(settings, contextUser);

            //
            // The CopyMoveProcessor emit events that you may opt to listen in to
            //
            processor.Completed += delegate(object sender, CopyMoveResultEventArgs e)
            {
                // Handle completed transaction  
            };
            processor.Aborted += delegate(object sender, CopyMoveResultEventArgs e)
            {
                // Handle aborted transaction  
            };

            //
            // 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
                HaltOnWarning = !ignoreWarningsCheckBox.Checked,
                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
            //
            transaction.ExcludeProperties = new[] { "FieldName1", "FieldName2" };

            //
            // 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. 
            //
            string transactionId;
            if (operation == Operation.Copy)
                transactionId = processor.CopyAsync(transaction);
            else
                transactionId = processor.MoveAsync(transaction);

            //
            // CopyMove keeps track of the progress in the worker thread 
            // and you can ask for it using the GetStatus method.
            //
            CopyMoveStatus status;
            do
            {
                Thread.Sleep(500);
                status = processor.GetStatus(transactionId);
            } while (status.Running);

            //
            // Get the final result of the CopyMove transaction. The GetResult 
            // method blocks until the CopyMove transaction has completed or 
            // was aborted by an error
            //
            CopyMoveResult result = processor.GetResult(transactionId);

            //
            // Check and show the result of the CopyMove transaction. 
            // Possible outcomes are success, warning or error.
            // The CopyMove API does not throw exceptions.  
            // 
            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>
        /// Gets the claims encoded login in SharePoint for the specified Windows user 
        /// </summary>
        /// <param name="siteUrl"></param>
        /// <param name="windowsUserName"></param>
        /// <returns></returns>
        private string GetContextUserLogin(string siteUrl, string windowsUserName)
        {
            string contextUserLogin = null;
            SPSite site = null;
            SPWeb web = null;

            try
            {
                site = new SPSite(siteUrl);
                web = site.OpenWeb();
                SPUserCollection users = web.SiteUsers;
                if (web.Site.WebApplication.UseClaimsAuthentication && windowsUserName.IndexOf('|') == -1)
                {
                    SPClaimProviderManager claimManager = SPClaimProviderManager.Local;

                    foreach (SPUser candidateUser in users)
                    {
                        if (candidateUser.IsDomainGroup) continue;
                        if (candidateUser.LoginName.IndexOf('|') == -1) continue;
                        SPClaim claim = claimManager.DecodeClaim(candidateUser.LoginName);
                        claim = new SPClaim(SPClaimTypes.UserLogonName, windowsUserName, "http://www.w3.org/2001/XMLSchema#string", claim.OriginalIssuer);
                        contextUserLogin = claimManager.EncodeClaim(claim);
                        break;
                    }
                } else
                {
                    contextUserLogin = windowsUserName;
                }
            }

            finally
            {
                if (web != null) web.Dispose();
                if (site != null) site.Dispose();
            }

            return contextUserLogin;
        }


        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))
                {
                    return _filename;
                }
                return _title;
            }
        }

        #endregion
    }
}
