using System;
using System.Drawing;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.OleDb;
using System.IO;

using C1.Data;
using C1.Data.SchemaObjects;

namespace ResyncFromDatabase
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        private C1.Data.C1SchemaDef c1SchemaDef1;
        private C1.Data.C1DataSet c1DataSet1;
        private System.Windows.Forms.DataGrid dataGrid1;
        private System.Windows.Forms.Button button1;
        private C1.Data.C1DataSet resyncDataSet;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Button button3;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            //
            // TODO: Add any constructor code after InitializeComponent call
            //
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.c1SchemaDef1 = new C1.Data.C1SchemaDef();
            this.c1DataSet1 = new C1.Data.C1DataSet();
            this.dataGrid1 = new System.Windows.Forms.DataGrid();
            this.button1 = new System.Windows.Forms.Button();
            this.resyncDataSet = new C1.Data.C1DataSet();
            this.button2 = new System.Windows.Forms.Button();
            this.button3 = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.c1SchemaDef1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.c1DataSet1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.resyncDataSet)).BeginInit();
            this.SuspendLayout();
            // 
            // c1SchemaDef1
            // 
            this.c1SchemaDef1.DataObjectsAssemblyFlags = ((C1.Data.DataObjectsAssemblyFlags)((C1.Data.DataObjectsAssemblyFlags.DataSetNamespaces | C1.Data.DataObjectsAssemblyFlags.StaticNameFields)));
            this.c1SchemaDef1.DummyImage = ((System.Drawing.Bitmap)(resources.GetObject("c1SchemaDef1.DummyImage")));
            this.c1SchemaDef1.CreateSchema += new System.EventHandler(this.c1SchemaDef1_CreateSchema);
            this.c1SchemaDef1.SerializedSchemaXML = this.c1SchemaDef1.GetSchemaFromResources(resources, "c1SchemaDef1", this);
            this.c1SchemaDef1.SerializedDesignTimeOptionsXML = this.c1SchemaDef1.GetDesignTimeOptionsFromResources(resources, "c1SchemaDef1", this);
            // 
            // c1DataSet1
            // 
            this.c1DataSet1.BindingContextCtrl = this;
            this.c1DataSet1.DataLibrary = "";
            this.c1DataSet1.DataLibraryUrl = "";
            this.c1DataSet1.DataSetDef = "DataSet";
            this.c1DataSet1.Locale = new System.Globalization.CultureInfo("en-US");
            this.c1DataSet1.Name = "c1DataSet1";
            this.c1DataSet1.SchemaClassName = "";
            this.c1DataSet1.SchemaDef = this.c1SchemaDef1;
            // 
            // dataGrid1
            // 
            this.dataGrid1.DataMember = "Customers";
            this.dataGrid1.DataSource = this.c1DataSet1;
            this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
            this.dataGrid1.Location = new System.Drawing.Point(8, 16);
            this.dataGrid1.Name = "dataGrid1";
            this.dataGrid1.Size = new System.Drawing.Size(808, 376);
            this.dataGrid1.TabIndex = 0;
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(248, 416);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(120, 32);
            this.button1.TabIndex = 1;
            this.button1.Text = "Resync current row";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // resyncDataSet
            // 
            this.resyncDataSet.BindingContextCtrl = this;
            this.resyncDataSet.DataLibrary = "";
            this.resyncDataSet.DataLibraryUrl = "";
            this.resyncDataSet.DataSetDef = "DataSet";
            this.resyncDataSet.FillOnRequest = false;
            this.resyncDataSet.Locale = new System.Globalization.CultureInfo("en-US");
            this.resyncDataSet.Name = "resyncDataSet";
            this.resyncDataSet.SchemaClassName = "";
            this.resyncDataSet.SchemaDef = this.c1SchemaDef1;
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(688, 416);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(120, 32);
            this.button2.TabIndex = 2;
            this.button2.Text = "Commit changes";
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // button3
            // 
            this.button3.Location = new System.Drawing.Point(408, 416);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(136, 32);
            this.button3.TabIndex = 3;
            this.button3.Text = "Resync all rows";
            this.button3.Click += new System.EventHandler(this.button3_Click);
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(824, 469);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.dataGrid1);
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.c1SchemaDef1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.c1DataSet1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.resyncDataSet)).EndInit();
            this.ResumeLayout(false);

        }
        #endregion

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }

        private void ResyncRow(C1DataRow row, C1DataRow dbRow)
        {
            // This method synchronizes a simple table row with a row fetched from the database.
            if (row.RowState != DataRowStateEnum.Modified && row.RowState != DataRowStateEnum.Unchanged)
                // deleted rows don't need synchronization; added rows don't exist in the database, so they also don't need synchronization
                return;
            bool originalValuesChanged = false; // this flag will be true if original values in the row should
            // change as a result of the synchronization, i.e., the database row was changed by another user after the row was fetched
            foreach (DataField field in row.DataTable.Fields)
            {
                if (!row[field, DataRowVersionEnum.Original].Equals(dbRow[field.Name]))
                    // database value is different from the original value stored in the row
                    // (the database row was changed by another user after the row was fetched).
                    originalValuesChanged = true;
            }
            if (originalValuesChanged)
            {
                row.BeginEdit();
                ListDictionary changedValues = new ListDictionary(); // we will save here all changes made to the row
                foreach (DataField field in row.DataTable.Fields)
                {
                    if (!row[field].Equals(row[field.Name, DataRowVersionEnum.Original]))
                        // value was changed, save the changes, to be applied after we call AcceptChanges.
                        changedValues.Add(field, row[field]);
                    // Change the value in the row. It will become the new original value after we call AcceptChanges.
                    row[field] = dbRow[field.Name];
                }
                // Call AcceptChanges for the row. After this call, the current row values
                // (which are copied from dbRow) become Original values of the row.
                // This way we copied all field values from dbRow to row[, DataRowVersion.Original].
                row.AcceptChanges();
                // Apply all the changes made by the current user to the row
                foreach (DataField field in changedValues.Keys)
                {
                    row[field] = changedValues[field];
                }
                row.EndEdit();
            }
        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            // Synchronize current row with the database.
            // Changes made to this row by other users will appear in the row, unless the same field was
            // changed by the current user.
            CurrencyManager cm = (CurrencyManager)BindingContext[c1DataSet1, "Customers"];
            if (cm.Position < 0)
            {
                MessageBox.Show("No current row");
                return;
            }
            // Get the current row
            C1DataRow tableViewRow = C1DataRow.FromDataItem(cm.Current);
            string customerID = tableViewRow["CustomerID"].ToString();
            C1DataTable dataTable = c1DataSet1.Tables["Customers"];
            C1DataRow tableRow = dataTable.Rows.Find(customerID);
            // Fetch the row from the database to the temporary data set resyncDataSet, using the primary key, CustomerID value as filter.
            // This will fill resyncDataSet with a single row (or no rows, if the row was deleted by another user)
            FilterCondition filter = new FilterCondition(resyncDataSet, "Customers", "[CustomerID] = '" + customerID + "'");
            resyncDataSet.Fill(filter, false);
            C1DataTable resyncDataTable = resyncDataSet.Tables["Customers"];
            if (resyncDataTable.Rows.Count == 0)
            {
                // Row was deleted from the database by another user
                MessageBox.Show("Row with CustomerID=" + customerID + " no longer exists in the database");
                return;
            }
            // Synchronize the row
            C1DataRow dbRow = resyncDataTable.Rows[0];
            ResyncRow(tableRow, dbRow);
        }

        private void button3_Click(object sender, System.EventArgs e)
        {
            // Synchronize all rows with the database.
            // Changes made by other users will become visible in our rowset.
            // Changes made by the current user are preserved.
            // Rows added by other users will appear, rows deleted by other users will disappear
            // (unless they were changed by the current user), rows modified by other users
            // will show new field values (except fields changed by the current user, all changes
            // made by the current user are preserved).
            //
            // Fetch data from the database to the temporary data set resyncDataSet
            resyncDataSet.Fill();
            // Get the changes made by the current user. We use the ADO.NET GetChanges method to get the changes.
            System.Data.DataSet changesAddedDeleted = c1DataSet1.StorageDataSet.GetChanges(System.Data.DataRowState.Added | System.Data.DataRowState.Deleted);
            System.Data.DataSet changesModified = c1DataSet1.StorageDataSet.GetChanges(System.Data.DataRowState.Modified);
            // To modify the ADO.NET data table storing C1DataTable's data, we need to
            // use StorageChangeBegin/StorageChanged/StorageChangeEnd
            // (see "Working with ADO.NET Dataset" in C1DataObjects documentation)
            c1DataSet1.StorageChangeBegin();
            C1DataTable customersTable = c1DataSet1.Tables["Customers"];
            // Using ADO.NET method Merge, copy data fetched from the database to our Customers table
            customersTable.StorageDataTable.Clear();
            c1DataSet1.StorageDataSet.Merge(resyncDataSet.Tables["Customers"].StorageDataTable);
            // Now the Customers table is filled with rows that are currently in the database
            customersTable.StorageDataTable.AcceptChanges();
            // If there are changes made by the current user, apply those changes to the Customers table.
            if (changesAddedDeleted != null)
            {
                // To apply added and deleted rows, we use the ADO.NET Merge method
                System.Data.DataTable changesCustomers = changesAddedDeleted.Tables["Customers"];
                if (changesCustomers != null)
                    c1DataSet1.StorageDataSet.Merge(changesCustomers);
            }
            if (changesModified != null)
            {
                // To apply modified rows, we go row-by-row and change field values
                System.Data.DataTable changesCustomers = changesModified.Tables["Customers"];
                if (changesCustomers != null)
                {
                    foreach (System.Data.DataRow modifiedRow in changesCustomers.Rows)
                    {
                        System.Data.DataRow dataRow = customersTable.StorageDataTable.Rows.Find(modifiedRow["CustomerID"]);
                        if (dataRow != null)
                        {
                            foreach (System.Data.DataColumn column in modifiedRow.Table.Columns)
                                if (!modifiedRow[column].Equals(modifiedRow[column, System.Data.DataRowVersion.Original]))
                                    dataRow[column.ColumnName] = modifiedRow[column];
                        }
                    }
                }
            }
            // Table Customers is synchronized with the database.
            c1DataSet1.StorageChanged(true);
            // In this sample, we do nothing between StorageChanged and StorageChangeEnd, thus allowing C1DataObjects
            // to fill all table views (we have only one table view in this sample) with table rows.
            // If your table views are restricted (if you used FilterConditions filling the data set),
            // you may need to call SetTableViewRows here, see "Working with ADO.NET Dataset" in C1DataObjects documentation.
            c1DataSet1.StorageChangeEnd();
        }

        private void button2_Click(object sender, System.EventArgs e)
        {
            // Commit changes to the database
            c1DataSet1.Update();
        }

        #region Switch to C1NWind.mdb database

        private void c1SchemaDef1_CreateSchema(object sender, EventArgs e)
        {
            string dbPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal) +
                "\\ComponentOne Samples\\Common\\C1NWind.mdb";
            if (File.Exists(dbPath))
            {
                Connection conn = c1SchemaDef1.Schema.Connections[0];
                OleDbConnectionStringBuilder csb = new OleDbConnectionStringBuilder(conn.ConnectionString);
                csb.DataSource = dbPath;
                conn.ConnectionString = csb.ToString();
            }
        }

        #endregion
    }
}
