using System;
using System.Data;
using System.Data.Common;
using System.Text;

namespace IndianHealthService.BMXNet.Ado
{
    /// <summary>
    /// BMXNET implementaiton of DbDataAdaptor.  This class can be used like
    /// any ADO.NET DbDataAdaptor implementation  or the suggested RemoteSession API's can
    /// be used.
    /// 
    /// See ADO.NET documentation for details of this class.
    /// </summary>
	public class BMXNetDataAdapter : DbDataAdapter, IDbDataAdapter
	{
		private BMXNetCommand m_selectCommand;
		private BMXNetCommand m_insertCommand;
		private BMXNetCommand m_updateCommand;
		private BMXNetCommand m_deleteCommand;

		/*
		 * Inherit from Component through DbDataAdapter. The event
		 * mechanism is designed to work with the Component.Events
		 * property. These variables are the keys used to find the
		 * events in the components list of events.
		 */
		static private readonly object EventRowUpdated = new object(); 
		static private readonly object EventRowUpdating = new object(); 

		public BMXNetDataAdapter()
		{
		}

        new public BMXNetCommand SelectCommand
        {
            get { return m_selectCommand; }
            set { m_selectCommand = value; }
        }

        IDbCommand IDbDataAdapter.SelectCommand
        {
            get { return m_selectCommand; }
            set { m_selectCommand = (BMXNetCommand)value; }
        }

		new public BMXNetCommand InsertCommand 
		{
			get { return m_insertCommand; }
			set { m_insertCommand = value; }
		}

		IDbCommand IDbDataAdapter.InsertCommand 
		{
			get { return m_insertCommand; }
			set { m_insertCommand = (BMXNetCommand)value; }
		}

		new public BMXNetCommand UpdateCommand 
		{
			get { return m_updateCommand; }
			set { m_updateCommand = value; }
		}

		IDbCommand IDbDataAdapter.UpdateCommand 
		{
			get { return m_updateCommand; }
			set { m_updateCommand = (BMXNetCommand)value; }
		}

		new public BMXNetCommand DeleteCommand 
		{
			get { return m_deleteCommand; }
			set { m_deleteCommand = value; }
		}

		IDbCommand IDbDataAdapter.DeleteCommand 
		{
			get { return m_deleteCommand; }
			set { m_deleteCommand = (BMXNetCommand)value; }
		}

		/*
		 * Implement abstract methods inherited from DbDataAdapter.
		 */

		public override int Fill(DataSet ds)
		{
			//The inital call to base.fill calls the RPC which loads up the array
			//After base.fill returns, create a datareader
			BMXNetConnection bmxConn = (BMXNetConnection) this.SelectCommand.Connection;
			RPMSDb bmxDB = bmxConn.RPMSDb;

            
			DataTable dt = ds.Tables.Count == 0 ? new DataTable() : ds.Tables[0];

			//Execute the RPC call
			base.Fill(dt);
			//Get the table name
            this.ApplyMetaData(bmxDB.ResultSets[0].metaData, dt);
            dt.TableName = dt.TableName == null ? bmxDB.ResultSets[0].fmFileID : dt.TableName;
            dt.ExtendedProperties["fmSeed"] = bmxDB.ResultSets[0].fmSeed;
            dt.ExtendedProperties["BMXParentPath"]=bmxDB.ResultSets[0].fmParentPath;
			//dt.ExtendedProperties.Add("fmSeed", bmxDB.ResultSets[0].fmSeed);
            //dt.ExtendedProperties.Add("BMXParentPath", bmxDB.ResultSets[0].fmParentPath);

			
			string sParentTable = dt.TableName;

			//Add the first table to the DataSet
            if (!ds.Tables.Contains(dt.TableName))
            {
                ds.Tables.Add(dt);
            }

			//If bmxDB resultset array count is greater than 1
			int nSets = bmxDB.ResultSets.GetUpperBound(0) + 1;

			if (nSets > 1)
			{
				//Set primary key for first table
				string sKeyField = bmxDB.ResultSets[0].fmKeyField;
				DataColumn dcKey = dt.Columns[sKeyField];
				DataColumn[] dcKeys = new DataColumn[1];
				dcKeys[0] = dcKey;
				dt.PrimaryKey = dcKeys;

				string[] sRelations = new string[nSets];

				//loop and get the rest of the tables
				for (int k = 1; k < nSets; k++)
				{
					//Increment the current recordset counter in bmxDB
					bmxDB.CurrentRecordSet++;
					//Create the next table
					dt = new DataTable();
					//Fill it
					base.Fill(dt);
					//Get the table name
					string sChildTable = bmxDB.ResultSets[k].fmFileID;
					dt.TableName = sChildTable;
					//Add it to the dataset
					ds.Tables.Add(dt);

					//Get the foreign key field
					string sForeignKey = bmxDB.ResultSets[k].fmForeignKey;

                    //Set the data relationship
					string sParentKey;
					sParentKey = "BMXIEN";
					sParentKey = "PATIENT_IEN";

					DataRelation dr = new DataRelation("Relation" + k.ToString() ,	//Relation Name
						ds.Tables[sParentTable].Columns[sParentKey],  //;	//parent
						ds.Tables[sChildTable].Columns[sForeignKey]) ;//child
					ds.Relations.Add(dr);
				}
				bmxDB.CurrentRecordSet = 0;				  
			}
			return dt.Rows.Count;
		}

        private void ApplyMetaData(RPMSDb.RPMSDbResultSet.MetaData[] metaData, DataTable aTable)
        {
            foreach (RPMSDb.RPMSDbResultSet.MetaData each in metaData)
            {
                aTable.Columns[each.name].ReadOnly=each.fmReadOnly;
            }
        }

		override protected DataTable FillSchema(
			DataTable dataTable,
			SchemaType schemaType,
			IDbCommand command,
			CommandBehavior behavior
			)
		{
			behavior = CommandBehavior.SchemaOnly;
			BMXNetDataReader dReader =(BMXNetDataReader) command.ExecuteReader(behavior);
			DataTable dtSchema = dReader.GetSchemaTable();
			return dtSchema;
		}

		override protected int Update(
			DataRow[] dataRows,
			DataTableMapping tableMapping
			)
		{
			//Build UpdateCommand's m_sCmdText using UpdateCommand's parameters
			// and data in dataRows;
			// execute non query and increment nRet;

			string sCmd = "";
			int nRecordsAffected = 0;

			//Get recordset-level info from parameters
            BMXNetParameter parm = (BMXNetParameter)UpdateCommand.BmxNetParameters[0];
			string sFileID = parm.SourceColumn;
			parm = (BMXNetParameter) UpdateCommand.BmxNetParameters[1];
			string sKeyField = parm.SourceColumn;
            String parentPath=	dataRows[0].Table.ExtendedProperties.ContainsKey("BMXParentPath") ? dataRows[0].Table.ExtendedProperties["BMXParentPath"].ToString() :"";
            String[] parentPathPeices = parentPath.Split(new char[] { ',' }); 
            string[] saKeyFields = sKeyField.Split(new char[] { ',' });
        	string sKeyID = null;
			char[] cRecDelim = new char[1];
			cRecDelim[0] = (char) 30;

			string sValue = "";
			string sFMFieldID;
			string sColumnName;
			int		nColIndex;

			//Process deletions
			foreach (DataRow r in dataRows)
			{
				if (r.RowState == DataRowState.Deleted)
				{
					r.RejectChanges(); //so that I can get to the row id
					//Build DAS
					string sTmp = "";
					for (int j = 0; j < saKeyFields.GetLength(0); j++)
					{
                        if (saKeyFields[j] != "")
                        {
                            if (j > 0)
                                sTmp = sTmp + ",";
                            if (j == saKeyFields.GetLength(0) - 1)
                                sTmp += "-";
                            sTmp += r[saKeyFields[j]];
                        }
					}
					sCmd = sTmp;
					sCmd = sFileID + "^" + sCmd + "^";
					UpdateCommand.CommandText = "UPDATE " + sCmd;
					int nRet = this.UpdateCommand.ExecuteNonQuery();
					r.Delete();
					nRecordsAffected += nRet;
				}
			}

			//Process Edits and Adds
			foreach (DataRow r in dataRows)
			{
                sKeyID = null;                      
				if (r.RowState != DataRowState.Deleted)
				{
					string	sMsg = "";
					for (int j=2; j < UpdateCommand.BmxNetParameters.Count; j++)
					{

                        parm = (BMXNetParameter)UpdateCommand.BmxNetParameters[j];
						sColumnName = parm.ParameterName;
						sFMFieldID = parm.SourceColumn;
						//Find a column id in r whose column name is sColumnName
						nColIndex = -1;
						for (int k = 0; k < r.Table.Columns.Count; k ++)
						{
							if (r.Table.Columns[k].ColumnName == sColumnName)
							{
								nColIndex = k;
								break;
							}
						}
						if (nColIndex > -1) 
						{
							if (r.ItemArray[nColIndex].GetType() == typeof(System.DateTime))
							{
								DateTime dValue = (DateTime) r.ItemArray[nColIndex];
                                if (dValue.Second != 0)
                                {
                                    throw new BMXNetException("Seconds are not allowed in a Date");
                                }
								if ((dValue.Minute == 0) && (dValue.Hour == 0))
								{
									sValue = dValue.ToString("M-d-yyyy");
								}
								else
								{
									sValue = dValue.ToString("M-d-yyyy@HH:mm");
								}
							}
							else
							{
								sValue = r.ItemArray[nColIndex].ToString();
							}
							if (parm.IsKey == false)
							{
								if (sMsg != "")
									sMsg += (char) 30;
								sMsg += sFMFieldID + "|" + sValue;
							}
						}


                        if (sKeyID == null)
                        {
                            StringBuilder keyBuilder = new StringBuilder();
                            bool needsDelimiter = false;

                            for (int i = 0; i < saKeyFields.Length; i++)
                            {
                                String currentKey = saKeyFields[i];
                                if (currentKey != "")
                                {
                                    String currentValue = r[currentKey].ToString().Trim();
                                    if (currentValue.Length == 0)
                                    {
                                        if (parentPathPeices.Length >= (i + 1))
                                        {
                                            currentValue = parentPathPeices[i];
                                        }
                                    }

                                    if (needsDelimiter)
                                        keyBuilder.Append(',');

                                    keyBuilder.Append(currentValue);
                                    needsDelimiter = true;
                                }
                            }
                            sKeyID = keyBuilder.ToString();
                        }


                        /*
						switch (sFMFieldID)
						{
							case ".0001":
								if (sKeyID == "")
								{
									sKeyID = sValue + ",";
								}
								else
								{
									sKeyID = sValue + "," + sKeyID;
								}
								break;
							case ".001":
								if (sKeyID == "")
								{
									sKeyID = sValue;
								}
								else
								{
									sKeyID = sKeyID + sValue;
								}
								break;
							default:
								break;

						}
                         **/

					}
					sCmd = sFileID + "^" + sKeyID + "^" + sMsg;
					UpdateCommand.CommandText = "UPDATE " + sCmd;
					int nRet = this.UpdateCommand.ExecuteNonQuery();
					nRecordsAffected += nRet;
				}//end if RowState != deleted
			}//end for

			return nRecordsAffected;
		}

		override protected RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping)
		{
			return new BMXNetRowUpdatedEventArgs(dataRow, command, statementType, tableMapping);
		}

		override protected RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping)
		{
			return new BMXNetRowUpdatingEventArgs(dataRow, command, statementType, tableMapping);
		}

		override protected void OnRowUpdating(RowUpdatingEventArgs value)
		{
			BMXNetRowUpdatingEventHandler handler = (BMXNetRowUpdatingEventHandler) Events[EventRowUpdating];
			if ((null != handler) && (value is BMXNetRowUpdatingEventArgs)) 
			{
				handler(this, (BMXNetRowUpdatingEventArgs) value);
			}
		}

		override protected void OnRowUpdated(RowUpdatedEventArgs value)
		{
			BMXNetRowUpdatedEventHandler handler = (BMXNetRowUpdatedEventHandler) Events[EventRowUpdated];
			if ((null != handler) && (value is BMXNetRowUpdatedEventArgs)) 
			{
				handler(this, (BMXNetRowUpdatedEventArgs) value);
			}
		}

        internal event BMXNetRowUpdatingEventHandler RowUpdating
		{
			add { Events.AddHandler(EventRowUpdating, value); }
			remove { Events.RemoveHandler(EventRowUpdating, value); }
		}

		internal event BMXNetRowUpdatedEventHandler RowUpdated
		{
			add { Events.AddHandler(EventRowUpdated, value); }
			remove { Events.RemoveHandler(EventRowUpdated, value); }
		}
	}

	internal delegate void BMXNetRowUpdatingEventHandler(object sender, BMXNetRowUpdatingEventArgs e);
	internal delegate void BMXNetRowUpdatedEventHandler(object sender, BMXNetRowUpdatedEventArgs e);

	internal class BMXNetRowUpdatingEventArgs : RowUpdatingEventArgs
	{
		public BMXNetRowUpdatingEventArgs(DataRow row, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) 
			: base(row, command, statementType, tableMapping) 
		{
		}

		// Hide the inherited implementation of the command property.
		new public BMXNetCommand Command
		{
			get  { return (BMXNetCommand)base.Command; }
			set  { base.Command = value; }
		}
	}

	internal class BMXNetRowUpdatedEventArgs : RowUpdatedEventArgs
	{
		public BMXNetRowUpdatedEventArgs(DataRow row, IDbCommand command, StatementType statementType, DataTableMapping tableMapping)
			: base(row, command, statementType, tableMapping) 
		{
		}

		// Hide the inherited implementation of the command property.
		new public BMXNetCommand Command
		{
			get  { return (BMXNetCommand)base.Command; }
		}
	}
}
