/* Copyright (c) Citrix Systems Inc. 
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, 
 * with or without modification, are permitted provided 
 * that the following conditions are met: 
 * 
 * *   Redistributions of source code must retain the above 
 *     copyright notice, this list of conditions and the 
 *     following disclaimer. 
 * *   Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the 
 *     following disclaimer in the documentation and/or other 
 *     materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 */

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Commands;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.SettingsPanels;
using XenAPI;

namespace XenAdmin.Wizards.GenericPages
{
    // This design for this class is in NewVMGroupVMsPageBase: see notes there.

    public class NewVMGroupVMsPage<T> : NewVMGroupVMsPageBase where T : XenObject<T>
    {
        private Pool _pool;
        public Pool Pool
        {
            get { return _pool; }
            set
            {
                _pool = value;
                if (_pool != null)
                    label1.Text = string.Format(Helpers.IsPool(_pool.Connection) ? Messages.VMS_IN_POOL : Messages.VMS_IN_SERVER,
                                                _pool.Name.Ellipsise(60));
                label2.Text = VMGroup<T>.ChooseVMsPage_Rubric;
                dataGridView1.Columns["ColumnCurrentGroup"].HeaderText = VMGroup<T>.ChooseVMsPage_CurrentGroup;
            }
        }

        public NewVMGroupVMsPage()
        {
        	SelectedVMs = new List<VM>();
            dataGridView1.SortCompare += dataGridView1_SortCompare;
        }

        public override string Text
        {
            get { return VMGroup<T>.ChooseVMsPage_Text; }
        }

        public override string SubText
        {
            get { return labelCounterVMs.Text; }
        }

        public override string HelpID
        {
            get { return VMGroup<T>.ChooseVMsPage_HelpID; } 
        }

        public override Image Image
        {
            get { return Properties.Resources._000_VM_h32bit_16; }
        }

        public override string PageTitle
        {
            get { return VMGroup<T>.ChooseVMsPage_Title; }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        internal List<VM> SelectedVMs { get; set; }

        public string GroupName { private get; set; }

        public List<XenRef<VM>> SelectedVMsRefs
        {
            get { return SelectedVMs.ConvertAll<XenRef<VM>>(converter); }
        }

        private static XenRef<VM> converter(VM vm)
        {
            return new XenRef<VM>(vm.opaque_ref);
        }

        protected override void searchTextBox1_TextChanged(object sender, System.EventArgs e)
        {
            if (Pool != null)
            {
				var sortedColumn = dataGridView1.SortedColumn;
				var sortOrder = dataGridView1.SortOrder;

                dataGridView1.Rows.Clear();
                foreach (var vm in Pool.Connection.Cache.VMs)
                {
                    if (searchTextBox1.Matches(vm.Name) && vm.is_a_real_vm)
						dataGridView1.Rows.Add(new VMDataGridViewRow(SelectedVMs.Contains(vm), vm));
                }

				if (sortOrder != SortOrder.None && sortedColumn != null)
					dataGridView1.Sort(sortedColumn, (sortOrder == SortOrder.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending);
                UpdateCounterLabelAndButtons();
            }
        }

        private void UpdateCounterLabelAndButtons()
        {
            labelCounterVMs.Text = SelectedVMs.Count == 1 ? Messages.ONE_VM_SELECTED : string.Format(Messages.MOREONE_VM_SELECTED, SelectedVMs.Count);
            buttonClearAll.Enabled = SelectedVMs.Count > 0;
            buttonSelectAll.Enabled = SelectedVMs.Count < dataGridView1.RowCount;
        }

        // How to sort the VMs in the grid view: CA-69817
        void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
        {
            e.Handled = false;

            bool sortByName = false;

            if (e.Column == ColumnCheckBox)
            {
                // Sort by check mark first, then by name
                int checked1 = (bool)e.CellValue1 ? 1 : 0;
                int checked2 = (bool)e.CellValue2 ? 1 : 0;
                if (checked1 != checked2)
                {
                    e.SortResult = checked2 - checked1;
                    e.Handled = true;
                }
                else
                    sortByName = true;
            }

            if (e.Column == ColumnName || sortByName)
            {
                // In order to sort by name, we actually use the built-in VM sort.
                // This ensures we use the correct name sorting, and gives a consistent tie-breaker.
                VM vm1 = ((VMDataGridViewRow)dataGridView1.Rows[e.RowIndex1]).Vm;
                VM vm2 = ((VMDataGridViewRow)dataGridView1.Rows[e.RowIndex2]).Vm;
                e.SortResult = vm1.CompareTo(vm2);
                e.Handled = true;
            }

            // For any other column, we fall off the end of this function with e.Handled still false,
            // and let the framework handle it.
        }

        private class VMDataGridViewRow : DataGridViewRow
        {
            private DataGridViewCheckBoxCell _selectedCell = new DataGridViewCheckBoxCell();
            private DataGridViewTextBoxCell _nameCell = new DataGridViewTextBoxCell();
            private DataGridViewTextBoxCell _descriptionCell = new DataGridViewTextBoxCell();
            private DataGridViewTextBoxCell _currentGroupCell = new DataGridViewTextBoxCell();
            public readonly VM Vm;
            public VMDataGridViewRow(bool selected, VM vm)
            {
                Vm = vm;
                Cells.Add(_selectedCell);
                Cells.Add(_nameCell);
                Cells.Add(_descriptionCell);
                Cells.Add(_currentGroupCell);
                Refresh(selected);
            }

            void Refresh(bool selected)
            {
                _selectedCell.Value = selected;
                _nameCell.Value = Vm.Name;
                _descriptionCell.Value = Vm.Description;
                T group = Vm.Connection.Resolve(VMGroup<T>.VmToGroup(Vm));
                _currentGroupCell.Value = group == null ? Messages.NONE : group.Name;
            }
        }

        public override void PageLoaded(PageLoadedDirection direction)
        {
            base.PageLoaded(direction);
            RefreshTab(null);
        } 

        private T _group = null;
        private void RefreshTab(T group)
        {
            _group = group;
            if (Pool != null)
            {
                dataGridView1.Rows.Clear();
                foreach (var vm in Pool.Connection.Cache.VMs)
                {
                    int index = 0;
                    if (vm.is_a_real_vm && vm.Show(Properties.Settings.Default.ShowHiddenVMs))
                    {
                        bool selected = group != null && VMGroup<T>.GroupToVMs(group).Contains(new XenRef<VM>(vm.opaque_ref));
						index = dataGridView1.Rows.Add(new VMDataGridViewRow(selected, vm));

                        if (SelectedVMs.Contains(vm))
                            dataGridView1.Rows[index].Cells[0].Value = true;
						else if (selected && !SelectedVMs.Contains(vm))
							SelectedVMs.Add(vm);
                    }
                }

                dataGridView1.Sort(ColumnCheckBox, ListSortDirection.Ascending);
                dataGridView1.AutoResizeColumns();
            }
            UpdateCounterLabelAndButtons();
        }

        protected override void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 0 && e.RowIndex >= 0)
            {
                dataGridView1.Rows[e.RowIndex].Cells[0].Value = !(bool)dataGridView1.Rows[e.RowIndex].Cells[0].Value;
            }
        }

        protected override void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
		{
			if (e.ColumnIndex != 0 || e.RowIndex < 0 && e.RowIndex >= dataGridView1.RowCount)
				return;

			var row = dataGridView1.Rows[e.RowIndex] as VMDataGridViewRow;

			if (row != null)
			{
				if ((bool)row.Cells[0].Value)
				{
					if (!SelectedVMs.Contains(row.Vm))
						SelectedVMs.Add(row.Vm);
				}
				else
					SelectedVMs.Remove(row.Vm);
			}

            UpdateCounterLabelAndButtons();
		}

        protected override void dataGridView1_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
		{
			if ((e.Row.State & DataGridViewElementStates.Selected) != 0)
				e.Row.Selected = false;
		}

        public override void PageLeave(PageLoadedDirection direction, ref bool cancel)
        {
            if (direction == PageLoadedDirection.Back)
                return;

            if (!userAcceptsWarning())
                cancel = true;

            base.PageLeave(direction, ref cancel);
        }

        private bool userAcceptsWarning()
        {
            string groupName = (_group != null) ? _group.Name : GroupName;
            return AssignGroupToolStripMenuItem<T>.AssignGroupToVMCommand.ChangesOK(SelectedVMs, _group, groupName);
        }

        protected override void buttonClearAll_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow dataGridViewRow in dataGridView1.Rows)
            {
                dataGridViewRow.Cells[0].Value = false;
            }
        }

        protected override void buttonSelectAll_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow dataGridViewRow in dataGridView1.Rows)
            {
                dataGridViewRow.Cells[0].Value = true;
            }
        }

        #region IEditPage implementation
        public override AsyncAction SaveSettings()
        {
            return VMGroup<T>.AssignVMsToGroupAction(_clone, SelectedVMsRefs, true);
        }

        private T _clone;
        public override void SetXenObjects(IXenObject orig, IXenObject clone)
        {
            _clone = (T)clone;
            Pool = Helpers.GetPoolOfOne(_clone.Connection);
            RefreshTab(_clone);
        }

        public override bool ValidToSave
        {
            get { return userAcceptsWarning(); }
        }

        public override void ShowLocalValidationMessages()
        {
        }

        public override void Cleanup()
        {
            dataGridView1.Rows.Clear();
        }

        public override bool HasChanged
        {
            get
            {
                return (!Helpers.CompareLists(SelectedVMsRefs, VMGroup<T>.GroupToVMs(_clone)));
            }
        }
        #endregion
    }
}