//
// Copyright 2020 Google LLC
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//

using Google.Solutions.Mvvm.Properties;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Web;
using System.Windows.Forms;
using static System.Windows.Forms.ListViewItem;

namespace Google.Solutions.Mvvm.Controls
{
    public static class ListViewExtensions
    {
        public static string ToTabSeparatedText(
            this ListView listView,
            bool selectedItemsOnly)
        {
            var headers = listView.Columns.Cast<ColumnHeader>();
            var items = selectedItemsOnly
                    ? listView.SelectedItems.Cast<ListViewItem>()
                    : listView.Items.Cast<ListViewItem>();

            var buffer = new StringBuilder();
            buffer.Append(string.Join(
                "\t",
                headers.Select(h => $"\"{h.Text.Replace("\"", "'")}\"")));
            buffer.Append("\r\n");

            foreach (var item in items)
            {
                buffer.Append(string.Join(
                    "\t",
                    item.SubItems
                        .Cast<ListViewSubItem>()
                        .Select(s => $"\"{s.Text.Replace("\"", "'")}\"")));
                buffer.Append("\r\n");
            }

            return buffer.ToString();
        }

        public static string ToHtml(
            this ListView listView,
            bool selectedItemsOnly)
        {
            var headers = listView.Columns.Cast<ColumnHeader>();
            var items = selectedItemsOnly
                    ? listView.SelectedItems.Cast<ListViewItem>()
                    : listView.Items.Cast<ListViewItem>();

            var buffer = new StringBuilder();
            buffer.AppendLine("<table>");

            buffer.AppendLine("<tr>");
            buffer.AppendLine(string.Join(
                string.Empty,
                headers.Select(h => $"<th>{HttpUtility.HtmlEncode(h.Text)}</th>")));
            buffer.AppendLine("</tr>");

            foreach (var item in items)
            {
                buffer.AppendLine("<tr>");
                buffer.AppendLine(string.Join(
                    string.Empty,
                    item.SubItems
                        .Cast<ListViewSubItem>()
                        .Select(s => $"<td>{HttpUtility.HtmlEncode(s.Text)}</td>")));
                buffer.AppendLine("</tr>");
            }

            buffer.AppendLine("</table>");

            return buffer.ToString();
        }

        private static void CopyToClipboard(
            ListView listView,
            bool selectedItemsOnly)
        {
            //
            // Add contents to clipboard in tab-separated and HTML format.
            // Tab-separated format is understood by Excel and Sheets,
            // HTML is for Docs and Word.
            //
            var dataObject = new DataObject();
            dataObject.SetData(
                DataFormats.Text,
                listView.ToTabSeparatedText(selectedItemsOnly));
            dataObject.SetData(
                DataFormats.Html,
                HtmlClipboardFormat.Format(listView.ToHtml(selectedItemsOnly)));

            ClipboardUtil.SetDataObject(dataObject);
        }

        public static void AddCopyCommands(this ListView listView)
        {
            if (listView.ContextMenuStrip == null)
            {
                listView.ContextMenuStrip = new ContextMenuStrip();
            }

            var copy = new ToolStripMenuItem(
                "&Copy",
                Resources.Copy_16x,
                (sender, args) => CopyToClipboard(listView, true));
            listView.ContextMenuStrip.Items.Add(copy);

            var copyAll = new ToolStripMenuItem(
                "Copy &all",
                Resources.Copy_16x,
                (sender, args) => CopyToClipboard(listView, false));
            listView.ContextMenuStrip.Items.Add(copyAll);

            listView.ContextMenuStrip.Opening += (sender, args) =>
            {
                copy.Enabled = listView.SelectedIndices.Count > 0;
                copyAll.Enabled = listView.Items.Count > 0;
            };
        }

        public static IntPtr GetHeaderHandle(this ListView listView)
        {
            return NativeMethods.SendMessage(
                listView.Handle,
                NativeMethods.LVM_GETHEADER,
                IntPtr.Zero,
                IntPtr.Zero);
        }

        //---------------------------------------------------------------------
        // P/Invoke.
        //---------------------------------------------------------------------

        private static class NativeMethods
        {
            public const int LVM_FIRST = 0x1000;
            public const int LVM_GETHEADER = (LVM_FIRST + 31);

            [DllImport("user32.dll")]
            public static extern IntPtr SendMessage(
                IntPtr hWnd,
                int msg,
                IntPtr wparam,
                IntPtr lparam);
        }
    }
}
