using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Text;
using NUnit.Framework;

namespace Telerik.Web.UI.Tests.SessionDataSource
{
    [TestFixture]
    public class MergeTests
    {
        private DataTable _dbTable;
        private DataTable _sessionTable;
        private int _seed = int.MinValue;


        [SetUp]
        public void SetUp()
        {
            _dbTable = ExecuteQuery1();
            _sessionTable = _dbTable.Copy();
        }

        [Test]
        public void SimulatedQueriesTest()
        {
            VerifyTableLooksLikeQueryResult(ExecuteQuery1());
            VerifyTableLooksLikeQueryResult(ExecuteQuery2());
        }

        [Test]
        public void NoChangesBetweenQueriesTest()
        {
            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery2());

            Assert.AreEqual(_dbTable.Rows.Count, _sessionTable.Rows.Count);
        }

        [Test]
        public void RowAddedBetweenIdenticalQueriesTest()
        {
            int nextID = _seed++;

            DataRow addedRow = _sessionTable.NewRow();
            addedRow["ID"] = nextID;
            addedRow["Subject"] = "Added row";
            _sessionTable.Rows.Add(addedRow);

            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery1());

            Assert.AreEqual(3, _sessionTable.Rows.Count);
            Assert.AreEqual("Added row", _sessionTable.Rows.Find(nextID)["Subject"]);
        }

        [Test]
        public void RowAddedBetweenDifferentQueriesTest()
        {
            int nextID = _seed++;

            DataRow addedRow = _sessionTable.NewRow();
            addedRow["ID"] = nextID;
            addedRow["Subject"] = "Added row";
            _sessionTable.Rows.Add(addedRow);

            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery2());

            Assert.AreEqual(3, _sessionTable.Rows.Count);
            Assert.AreEqual("Added row", _sessionTable.Rows.Find(nextID)["Subject"]);
        }

        [Test]
        public void RowUpdatedBetweenIdenticalQueriesTest()
        {
            int id = (int) _sessionTable.Rows[0]["ID"];
            _sessionTable.Rows[0]["Subject"] = "This was Appointment 1";

            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery1());

            Assert.AreEqual(2, _sessionTable.Rows.Count);
            Assert.AreEqual("This was Appointment 1", _sessionTable.Rows.Find(id)["Subject"]);
        }

        [Test]
        public void RowUpdatedBetweenDifferentQueriesTest()
        {
            int id = (int) _sessionTable.Rows[0]["ID"];
            _sessionTable.Rows[0]["Subject"] = "This was Appointment 1";

            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery2());

            Assert.AreEqual(2, _sessionTable.Rows.Count);
            Assert.IsNull(_sessionTable.Rows.Find(id));
        }

        [Test]
        public void RowDeletedBetweenIdenticalQueries()
        {
            int id = (int) _sessionTable.Rows[0]["ID"];
            _sessionTable.Rows.Find(id).Delete();

            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery1());

            Assert.AreEqual(1, _sessionTable.Rows.Count);
            Assert.IsNull(_sessionTable.Rows.Find(id));
        }

        [Test]
        public void RowDeletedBetweenDifferentQueries()
        {
            int id = (int) _sessionTable.Rows[0]["ID"];
            _sessionTable.Rows.Find(id).Delete();

            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery2());

            Assert.AreEqual(2, _sessionTable.Rows.Count);
            Assert.IsNull(_sessionTable.Rows.Find(id));
        }


        private void PerformMerge()
        {
            _sessionTable = SmartMerge(_sessionTable, ExecuteQuery2());
        }

        private DataTable SmartMerge(DataTable sessionTable, DataTable queryTable)
        {
            if (sessionTable.GetChanges() == null)
            {
                return queryTable;
            }

            DataTable result = queryTable.Copy();
            result.BeginLoadData();

            DataTable added = sessionTable.GetChanges(DataRowState.Added);
            if (added != null)
            {
                foreach (DataRow row in added.Rows)
                {
                    result.Rows.Add(row.ItemArray);
                }
            }

            DataTable deleted = sessionTable.GetChanges(DataRowState.Deleted);
            if (deleted != null)
            {
                foreach (DataRow row in deleted.Rows)
                {
                    DataRow rowToDelete = result.Rows.Find(row["ID", DataRowVersion.Original]);
                    if (rowToDelete != null)
                    {
                        rowToDelete.Delete();
                    }
                }
            }

            DataTable modified = sessionTable.GetChanges(DataRowState.Modified);
            if (modified != null)
            {
                foreach (DataRow row in modified.Rows)
                {
                    DataRow rowToUpdate = result.Rows.Find(row["ID"]);
                    if (rowToUpdate != null)
                    {
                        rowToUpdate.Delete();
                        result.Rows.Add(row.ItemArray);
                    }
                }
            }

            result.EndLoadData();
            result.AcceptChanges();

            return result;
        }

        private static void VerifyTableLooksLikeQueryResult(DataTable table)
        {
            foreach (DataRow row in table.Rows)
            {
                Assert.AreEqual(DataRowState.Unchanged, row.RowState);
            }
        }

        private DataTable ExecuteQuery1()
        {
            DataTable _dbTable = CreateSchema();

            _dbTable.BeginLoadData();

            DataRow row1 = _dbTable.NewRow();
            row1["ID"] = 1;
            row1["Subject"] = "Appointment 1";
            _dbTable.Rows.Add(row1);

            DataRow row2 = _dbTable.NewRow();
            row2["ID"] = 2;
            row2["Subject"] = "Appointment 2";
            _dbTable.Rows.Add(row2);

            _dbTable.EndLoadData();
            _dbTable.AcceptChanges();

            return _dbTable;
        }

        private DataTable ExecuteQuery2()
        {
            DataTable _dbTable = CreateSchema();

            _dbTable.BeginLoadData();

            DataRow row1 = _dbTable.NewRow();
            row1["ID"] = 3;
            row1["Subject"] = "Appointment 3";
            _dbTable.Rows.Add(row1);

            DataRow row2 = _dbTable.NewRow();
            row2["ID"] = 4;
            row2["Subject"] = "Appointment 4";
            _dbTable.Rows.Add(row2);

            _dbTable.EndLoadData();
            _dbTable.AcceptChanges();

            return _dbTable;
        }

        private DataTable CreateSchema()
        {
            DataTable _dbTable = new DataTable();

            DataColumn tasksPk = new DataColumn("ID", typeof(int));
            _dbTable.Columns.Add(tasksPk);
            _dbTable.PrimaryKey = new DataColumn[] { tasksPk };

            _dbTable.Columns.Add(new DataColumn("Subject", typeof(string)));
            return _dbTable;
        }
    }
}
