using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sdaleo;
using Sdaleo.Systems.SQLServer;
using Pluto.Api;
using System.Data;
using Sdaleo.Systems;
using Yaulw.Win32;
using Sdaleo.Systems.Advantage;

namespace PlutoServer.MSL.Connectors
{
    public static class MedisoftConnector
    {
        internal static string MAKE_SURE_APOSTROPHY_IS_SQL_READY(string str)
        {
            if (!String.IsNullOrEmpty(str))
            {
                str = str.Replace("'", "''");
                return str;
            }
            return String.Empty;
        }

        internal static bool IS_VALID_FOUR_DIGIT_PIN(string fourdigitpin)
        {
            if (!String.IsNullOrEmpty(fourdigitpin))
            {
                fourdigitpin = fourdigitpin.Trim();
                if (fourdigitpin.Length == 4)
                {
                    foreach (char c in fourdigitpin)
                    {
                        if (!Char.IsDigit(c))
                            return false;
                    }
                    return true;
                }
            }
            return false;
        }

        #region System / Setup Specific
        
        /// <summary>
        /// Retrieve the entire Practice List for the given Shared DB Credential * Credential must point to Shared DB *
        /// </summary>
        /// <param name="credential"></param>
        /// <returns></returns>
        public static DBRetVal GetPracticeList(AdvantageCredential credential)
        {
            if (credential != null)
            {
                DB db = DB.Create(credential);
                DBRetVal retVal = db.FillDataTable("SELECT [DataSet Name],[Data Path],[UserAPIKey] FROM [MWDBList]");
                if (!retVal.IsValid)
                    MSLSpecific.Logger.Error("Medisoft. Failed to retrieve Practice List for DataSource:{0}", credential.DataSource);
                return retVal;
            }
            return new DBRetVal();
        }

        /// <summary>
        /// Retrieve the userapi key for a given practice, given Shared DB Credential * Credential must point to Shared DB *
        /// </summary>
        /// <param name="credential"></param>
        /// <returns></returns>
        public static string GetUserApiKeyForPractice(AdvantageCredential credential, string DataSetOrDataBaseName, out bool bErrorOccured)
        {
            bErrorOccured = true;
            if (credential != null && !String.IsNullOrEmpty(DataSetOrDataBaseName))
            {
                DB db = DB.Create(credential);
                DBRetVal retVal = db.ExecuteScalar(String.Format("SELECT [UserAPIKey] FROM [MWDBList] WHERE [DataSet Name]='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(DataSetOrDataBaseName)));
                bErrorOccured = retVal.ErrorOccured;
                if(retVal.IsValid)
                   return retVal.GetScalarRetVal().Trim();
            }
            return String.Empty;
        }

        /// <summary>
        /// Use this to Set the User API Key * Credential must point to Shared DB *
        /// </summary>
        /// <param name="credential"></param>
        /// <param name="PracticeName"></param>
        /// <param name="UserApiKey"></param>
        /// <returns></returns>
        public static bool SetUserApiKeyForPractice(AdvantageCredential credential, string DataSetOrDataBaseName, string UserApiKey)
        {
            if (credential != null && !String.IsNullOrEmpty(DataSetOrDataBaseName))
            {
                DB db = DB.Create(credential);
                DBRetVal retVal = db.ExecuteNonQuery(String.Format("UPDATE [MWDBList] SET [UserApiKey]='{0}' WHERE [DataSet Name]='{1}'", UserApiKey, MAKE_SURE_APOSTROPHY_IS_SQL_READY(DataSetOrDataBaseName)));
                if (!retVal.IsValid)
                    MSLSpecific.Logger.Error("Medisoft. Failed to set ApiKey for DataSource:{0} DataSet:{1}", credential.DataSource, DataSetOrDataBaseName);
                return retVal.IsValid;
            }
            return false;
        }

        /// <summary>
        /// we must ensure uniqueness amongst all our userapikeys (Lytec and somehow Medisoft get into an
        /// invalid state) where they duplicate them * Credential must point to Shared DB *
        /// </summary>
        /// <param name="credential"></param>
        /// <param name="PracticeName"></param>
        /// <param name="UserApiKey"></param>
        /// <returns></returns>
        public static void CleanUpDuplicateUserApiKeyExists(AdvantageCredential credential)
        {
            DBRetVal retVal = GetPracticeList(credential);
            if (retVal.IsValid)
            {
                Stack<string> userApiStack = new Stack<string>();
                foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
                {
                    string UserApiKey = DataRet.Retrieve(row["UserAPIKey"]);
                    if (!String.IsNullOrEmpty(UserApiKey))
                    {
                        if (!userApiStack.Contains(UserApiKey))
                        {
                            userApiStack.Push(UserApiKey);
                        }
                        else
                        {
                            // we found a duplicate!!! (Clear the UserApiKey for that Database - IMP!)
                            string DBName = DataRet.Retrieve(row["DataSet Name"]);
                            SetUserApiKeyForPractice(credential, DBName, String.Empty);
                        }
                    }
                }
            }
        }

        #endregion

        #region User Authentication

        /// <summary>
        /// User Authentication
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="User"></param>
        /// <param name="Password"></param>
        /// <returns></returns>
        public static UserInfo1 AuthenticateUserLogin(string SystemApiKey, string User, string Password)
        {
            if (!String.IsNullOrEmpty(User) && !String.IsNullOrEmpty(Password))
            {
                AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
                DB db = DB.Create(credential);
                DBRetVal result = db.FillDataTable(String.Format("SELECT [Code],[Name],[Access Level],[Password] FROM [mwSEC] Where upper([Code])='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper()));
                //if ((result != null) && (result.IsValid)) /* Rameshes fix that should not be needed and didn't make sense */
                if (result.IsValid)
                {
                    DataRow row = result.GetDataTableFirstRow();
                    string dbName = DataRet.Retrieve(row["Name"]);
                    string dbPwd = DataRet.Retrieve(row["Password"]);

                    // Authenticate! Password must match (case-insensitive) Medisoft also doesn't care about case
                    if (!String.IsNullOrEmpty(dbName) && (String.Compare(Password.Trim(), dbPwd, true) == 0))
                    {
                        UserInfo1 info = new UserInfo1();
                        info.AuthToken = SystemApiKey;
                        info.Id = DataRet.Retrieve(row["Code"]);
                        info.UserName = info.Id;

                        // Seperate First n' Last name, if possible
                        info.Name = dbName;
                        int nFirst = dbName.IndexOf(' ');
                        if (nFirst != -1)
                        {
                            info.FirstName = dbName.Substring(0, nFirst);
                            info.LastName = dbName.Substring(nFirst + 1);
                        }
                        else
                        {
                            info.FirstName = dbName;
                            info.LastName = "";
                        }

                        // Authenticated, return the object
                        info.IsProvider = true;
                        return info;
                    }
                }
                // Users must be setup with Mobile Privilege * ignore code below *
                //else
                //{
                //    // Check to see if any users are set up in the table
                //    result = db.ExecuteScalar("SELECT COUNT([Code]) FROM [mwSEC]");
                //    uint nCount = 0;

                //    // If there no users, let them in anyway
                //    if (result.IsValid && uint.TryParse(result.GetScalarRetVal(), out nCount) && nCount == 0)
                //    {
                //        UserInfo1 info = new UserInfo1();
                //        info.AuthToken = SystemApiKey;
                //        info.Id = "(Unspecified)";
                //        info.UserName = "(Unspecified)";

                //        // Default Authenticated, return the object (No Users exist in table)
                //        info.IsProvider = true;
                //        return info;
                //    }
                //}
            }
            return null;
        }        

        /// <summary>
        /// Pin Authentication
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="User"></param>
        /// <param name="pin"></param>
        /// <returns></returns>
        public static UserInfo1 AuthenticateUserPIN(string SystemApiKey, string User, string pin)
        {
            if (!String.IsNullOrEmpty(User) && IS_VALID_FOUR_DIGIT_PIN(pin))
            {
                AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
                DB db = DB.Create(credential);
                DBRetVal result = db.FillDataTable(String.Format("SELECT [Code],[Name],[MobilePIN] FROM [mwSEC] Where upper([Code])='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper()));
                if (result.IsValid)
                {
                    DataRow row = result.GetDataTableFirstRow();
                    string dbName = DataRet.Retrieve(row["Name"]);
                    string dbPin = DataRet.Retrieve(row["MobilePIN"]);

                    // Authenticate! Password must match (case-insensitive) Medisoft also doesn't care about case
                    if (!String.IsNullOrEmpty(dbName) && (String.Compare(pin.Trim(), dbPin, true) == 0))
                    {
                        UserInfo1 info = new UserInfo1();
                        info.AuthToken = SystemApiKey;
                        info.Id = DataRet.Retrieve(row["Code"]);
                        info.UserName = info.Id;

                        // Seperate First n' Last name, if possible
                        info.Name = dbName;
                        int nFirst = dbName.IndexOf(' ');
                        if (nFirst != -1)
                        {
                            info.FirstName = dbName.Substring(0, nFirst);
                            info.LastName = dbName.Substring(nFirst + 1);
                        }
                        else
                        {
                            info.FirstName = dbName;
                            info.LastName = "";
                        }

                        // Authenticated, return the object
                        info.IsProvider = true;
                        return info;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Create Pin
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="User"></param>
        /// <param name="pin"></param>
        /// <returns></returns>
        public static bool CreatePin(string SystemApiKey, string User, string pin)
        {
            if (!String.IsNullOrEmpty(User) && IS_VALID_FOUR_DIGIT_PIN(pin))
            {
                AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
                DB db = DB.Create(credential);
                string sql = String.Format("UPDATE [mwSEC] SET [MobilePIN]='{0}' WHERE upper([Code])='{1}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(pin), MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper());
                DBRetVal result = db.ExecuteNonQuery(sql);
                return result.IsValid;
            }
            return false;
        }

        /// <summary>
        /// Pin is Set
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="User"></param>
        /// <returns></returns>
        public static bool PinIsSet(string SystemApiKey, string User)
        {
            if (!String.IsNullOrEmpty(User))
            {
                AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
                DB db = DB.Create(credential);
                string sql = String.Format("SELECT [MobilePIN] FROM [mwSEC] Where upper([Code])='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper());
                DBRetVal result = db.ExecuteScalar(sql);
                if (result.IsValid)
                {
                    string currentPin = DataRet.Retrieve(result.GetScalarRetVal());
                    return IS_VALID_FOUR_DIGIT_PIN(currentPin);
                }
            }
            return false;
        }

        #endregion

        #region Medisoft User Options 

        internal enum DefaultCase
        {
            DateModified = 0,
            HighestCase = 1,
            LowestCase = 2
        }
        /// <summary>
        /// Which is the default case to use?
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        internal static DefaultCase GetDefaultCase(string SystemApiKey)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);
            DBRetVal result = db.ExecuteScalar("SELECT [Case Default] from [MWOPT]");
            if (result.IsValid)
            {
                try
                {
                    DefaultCase Case = (DefaultCase) Enum.Parse(typeof(DefaultCase), result.GetScalarRetVal());
                    return Case;
                }
                catch (Exception e) 
                { 
                    MSLSpecific.Logger.Error("Failed to retrieve Default Case", e);
                }
            }
            return DefaultCase.DateModified;                
        }

        /// <summary>
        /// Use CopayRemainder Calculation or plain Patient Balance Calculation
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        internal static bool UseCopayRemainderCalculation(string SystemApiKey)
        {
            //AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            //DB db = DB.Create(credential);

            ////The figureMissedCopay was the Boolean from the mwopt table
            //DBRetVal result = db.ExecuteScalar("SELECT [Case Default] from MWOPT");
            //if (result.IsValid)
            //{
            //    bool bSomethingSomething = DataRet.Retrieve<bool>(result.GetScalarRetVal(), false);
            //    return bSomethingSomething;
            //}

            // For Testing Purpose
            if ((DateTime.Now.Ticks % 2) == 0)
                return true;
            else
                return false;
        }

        #endregion        

        #region Public Simple Info Getters

        /// <summary>
        /// get Providers
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public static ProviderInfo1[] GetProviderList(string SystemApiKey)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);
            DBRetVal result = db.FillDataTable("SELECT [Code],[First Name],[Middle Name],[Last Name],[Phone] FROM [MWPHY] WHERE [Inactive] = false");
            List<ProviderInfo1> providers = new List<ProviderInfo1>();
            if (result.IsValid)
            {
                foreach (DataRow row in result.GetDataTableRetVal().Rows)
                {
                    ProviderInfo1 info = new ProviderInfo1();
                    info.UserId = DataRet.Retrieve(row["Code"]);
                    info.FirstName = DataRet.Retrieve(row["First Name"]);
                    info.MiddleName = DataRet.Retrieve(row["Middle Name"]);
                    info.LastName = DataRet.Retrieve(row["Last Name"]);
                    info.Id = -1;
                    string phone = DataRet.Retrieve(row["Phone"]);
                    if (!String.IsNullOrEmpty(phone) && phone != "(   )    -")
                    {
                        info.Phone = phone;
                    }
                    providers.Add(info);
                }
                return providers.ToArray();
            }
            return null;
        }

        /// <summary>
        /// Get Diagnosis
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="searchStr"></param>
        /// <returns></returns>
        public static TerminologyInfo1[] GetDiagnosisList(string SystemApiKey, string searchStr)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);
            string sql = "SELECT [Code 1],[Description] FROM [MWDIA] WHERE [Inactive] = false";
            if (!String.IsNullOrEmpty(searchStr))
                sql += String.Format(" AND (upper([Code 1]) like '%{0}%' OR upper([Description]) like '%{0}%')", MAKE_SURE_APOSTROPHY_IS_SQL_READY(searchStr).ToUpper());

            DBRetVal result = db.FillDataTable(sql);
            List<TerminologyInfo1> diagnoses = new List<TerminologyInfo1>();
            if (result.IsValid)
            {
                foreach (DataRow row in result.GetDataTableRetVal().Rows)
                {
                    TerminologyInfo1 info = new TerminologyInfo1();
                    info.Code = DataRet.Retrieve(row["Code 1"]);
                    info.Description = DataRet.Retrieve(row["Description"]);
                    diagnoses.Add(info);
                }
                return diagnoses.ToArray();
            }
            return null;
        }

        /// <summary>
        /// Get Procedures
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="chart"></param>
        /// <param name="searchStr"></param>
        /// <returns></returns>
        public static TerminologyInfo1[] GetProceduresList(string SystemApiKey, string chart, string searchStr)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);
            string sql = "SELECT [Code 1],[Description],[Default Modifier 1],[Default Modifier 2],[Default Modifier 3],[Default Modifier 4],[Default Units] FROM [MWPRO] WHERE [Inactive] = 0";
            if (!String.IsNullOrEmpty(searchStr))
                sql += String.Format(" AND (upper([Code 1]) like '%{0}%' OR upper([Description]) like '%{0}%')", MAKE_SURE_APOSTROPHY_IS_SQL_READY(searchStr).ToUpper());

            DBRetVal result = db.FillDataTable(sql);
            List<TerminologyInfo1> procedures = new List<TerminologyInfo1>();
            if (result.IsValid)
            {
                foreach (DataRow row in result.GetDataTableRetVal().Rows)
                {
                    TerminologyInfo1 info = new TerminologyInfo1();
                    info.Code = DataRet.Retrieve(row["Code 1"]);
                    info.Description = DataRet.Retrieve(row["Description"]);
                    info.Modifier1 = DataRet.Retrieve(row["Default Modifier 1"]);
                    info.Modifier2 = DataRet.Retrieve(row["Default Modifier 2"]);
                    info.Modifier3 = DataRet.Retrieve(row["Default Modifier 3"]);
                    info.Modifier4 = DataRet.Retrieve(row["Default Modifier 4"]);
                    info.Units = DataRet.Retrieve<decimal>(row["Default Units"]);
                    procedures.Add(info);
                }
                return procedures.ToArray();
            }
            return null;
        }

        /// <summary>
        /// Get Facilities
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public static FacilityInfo1[] GetFacilitiesList(string SystemApiKey)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);

            string sql = "SELECT [Code],[Name],[Street 1],[Street 2],[City],[State],[Zip Code],[Phone],[Contact],[Email] FROM mwadd WHERE Type='Facility' OR Type='Laboratory' AND [Inactive] = false";
            DBRetVal result = db.FillDataTable(sql);
            List<FacilityInfo1> facilities = new List<FacilityInfo1>();
            if (result.IsValid)
            {
                foreach (DataRow row in result.GetDataTableRetVal().Rows)
                {
                    FacilityInfo1 info = new FacilityInfo1();
                    info.Code = DataRet.Retrieve(row["Code"]);
                    info.Name = DataRet.Retrieve(row["Name"]);

                    // Address Info
                    info.Address = new AddressInfo1();
                    info.Address.Address1 = DataRet.Retrieve(row["Street 1"]);
                    info.Address.Address2 = DataRet.Retrieve(row["Street 2"]);
                    info.Address.City = DataRet.Retrieve(row["City"]);
                    info.Address.State = DataRet.Retrieve(row["State"]);
                    info.Address.Zip = DataRet.Retrieve(row["Zip Code"]); 

                    facilities.Add(info);
                }
                return facilities.ToArray();
            }
            return null;
        }

        /// <summary>
        /// Not implemented (NOT CALLED)
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <returns></returns>
        public static ResourceInfo1[] GetResourcesList(string SystemApiKey)
        {
            // Not implemented
            return null;
        }
        
        /// <summary>
        /// Get Patients
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="lastName"></param>
        /// <param name="firstName"></param>
        /// <param name="dateOfBirth"></param>
        /// <returns></returns>
        public static PatientInfo1[] GetPatientList(string SystemApiKey, string lastName, string firstName, string dateOfBirth)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);
            string sql = "SELECT [Chart Number],[First Name],[Middle Name],[Last Name],[Date of Birth] as [Birth Date],[Sex],[Street 1],[Street 2],[City],[State],[Zip Code],[Phone 1],[Phone 2],[Phone 3],[Social Security Number] FROM [MWPAT]";

            // Allow them to search by patient name or by date of birth,
            // not both that is just too confusing 
            bool bWhereIsSet = false;
            if (!String.IsNullOrEmpty(dateOfBirth))
            {
                DateTime dt;
                if (DateTime.TryParse(dateOfBirth, out dt))
                {
                    sql = sql + String.Format(" WHERE [Date of Birth] = '{0:D4}-{1:D2}-{2:D2}' ", dt.Year, dt.Month, dt.Day);
                    bWhereIsSet = true;
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(lastName))
                {
                    sql = sql + String.Format(" WHERE upper([Last Name]) like '%{0}%' {1}", MAKE_SURE_APOSTROPHY_IS_SQL_READY(lastName).ToUpper(), !String.IsNullOrEmpty(firstName) ? "OR" : "");
                    bWhereIsSet = true;
                }
                if (!String.IsNullOrEmpty(firstName) && !String.IsNullOrEmpty(lastName))
                {
                    sql = sql + String.Format(" {1} upper([First Name]) like '%{0}%' ", MAKE_SURE_APOSTROPHY_IS_SQL_READY(firstName).ToUpper(), String.IsNullOrEmpty(lastName) ? "WHERE" : "");
                }
            }

            // Only query for Active Patients
            if(bWhereIsSet)
               sql = sql + " AND [Inactive] = false "; 
            else
               sql = sql + " WHERE [Inactive] = false "; 

            // Order Correctly
            sql = sql + " ORDER BY [Last Name],[First Name],[Middle Name] ";

            DBRetVal result = db.FillDataTable(sql);
            List<PatientInfo1> patients = new List<PatientInfo1>();
            if (result.IsValid)
            {
                foreach (DataRow row in result.GetDataTableRetVal().Rows)
                {
                    PatientInfo1 info = FillInPatientInformation(row, false, true);
                    patients.Add(info);
                }
                return patients.ToArray();
            }
            return null;
        }

        #endregion

        #region Private Static SQL Query Helper

        /// <summary>
        /// Fills in the Patient Info from the Patient Table Columns
        /// </summary>
        /// <param name="row"></param>
        /// <param name="bFillAsMuchPatientInfoAsPossible">If Set to true, will fill in as much patient information as possible, otherwise will on fill basic patient name / chart information</param>
        /// <param name="bFillInPhoneInfo">if set to true will fill try to fill in phone information</param>
        /// <returns></returns>
        private static PatientInfo1 FillInPatientInformation(DataRow row, bool bFillAsMuchPatientInfoAsPossible, bool bFillInPhoneInfo)
        {
            PatientInfo1 info = new PatientInfo1();

            // Basic Patient Info (Always fill that information)
            info.ChartId = DataRet.Retrieve(row["Chart Number"]);
            info.Id = DataRet.Retrieve(row["Chart Number"]);
            info.FirstName = DataRet.Retrieve(row["First Name"]);
            info.MiddleName = DataRet.Retrieve(row["Middle Name"]);
            info.LastName = DataRet.Retrieve(row["Last Name"]);
            info.Weight = ""; // we don't track weight

            // Birth Date & Age Calculation
            info.DOB = DataRet.Retrieve<DateTime>(row["Birth Date"]);            
            if (info.DOB > DateTime.MinValue)
            {
                DateTime now = DateTime.Today;
                int age = now.Year - info.DOB.Year;
                if (info.DOB > now.AddYears(-age)) age--;
                info.Age = age.ToString();
            }

            // Gender
            info.Gender = DataRet.Retrieve(row["Sex"], "Unknown", true);

            // If we want more details on the Patient, this will be set to false
            if (bFillAsMuchPatientInfoAsPossible)
            {
                // Social Security
                info.SocialSecurity = DataRet.Retrieve(row["Social Security Number"]);

                // Primary Address
                AddressInfo1 address = new AddressInfo1();
                address.Address1 = DataRet.Retrieve(row["Street 1"]);
                address.Address2 = DataRet.Retrieve(row["Street 2"]);
                address.City = DataRet.Retrieve(row["City"]);
                address.State = DataRet.Retrieve(row["State"]);                
                address.Zip = DataRet.Retrieve(row["Zip Code"]);                
                info.PrimaryAddress = address;
            }

            // Phone Number may already be filled in for us
            if (bFillInPhoneInfo)
            {
                // Primary Phone
                string Phone = DataRet.Retrieve(row["Phone 1"]);
                if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                {
                    info.PrimaryPhone = Phone;
                }
                else
                {
                    Phone = DataRet.Retrieve(row["Phone 2"]);
                    if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                    {
                        info.PrimaryPhone = Phone;
                    }
                    else
                    {
                        Phone = DataRet.Retrieve(row["Phone 3"]);
                        if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                        {
                            info.PrimaryPhone = Phone;                            
                        }
                    }
                }
            }

            return info;
        }

        /// <summary>
        /// Basically the same query as Appointment Query except with no Appointment information because all we have
        /// is a Chart
        /// </summary>
        /// <param name="PatientChart">Patient Chart</param>
        /// <returns></returns>
        private static string BuildPatientQuery(string PatientChart, DefaultCase Case)
        {
            if (!String.IsNullOrEmpty(PatientChart))
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("SELECT TOP 1 p.[Chart Number], p.[First Name],p.[Middle Name],p.[Last Name],p.[Date of Birth] as [Birth Date],p.[Sex],p.[Street 1],p.[Street 2],p.[City]");
                //sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Work Phone],p.[Work Extension], p.[Social Security Number]");      // Not using Work Phone / Work Extension      
                sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Social Security Number]");
                sb.Append(",c.[Copayment Amount] as [Patient Copay]");                
                sb.Append(",i.[Name] as [Primary Insurance Name]");
                sb.Append(",h.[Last Name] as [Ref Phy Last Name],h.[First Name] as [Ref Phy First Name],h.[Office] as [Ref Physician Phone],h.[Phone] as [Ref Physician Main Phone], h.[Cell] as [Ref Physician Mobile Phone]");
                sb.Append(" FROM [MWPAT] p LEFT JOIN");
                sb.Append(" [MWCAS] c ON p.[Chart Number] = c.[Chart Number]");
                sb.Append(" LEFT JOIN [MWINS] i ON c.[Insurance Carrier #1] = i.[Code]");
                sb.Append(" LEFT JOIN [MWRPH] h ON c.[Referring Provider] = h.[Code]");
                sb.Append(" WHERE ");
                sb.Append(String.Format("p.[Chart Number] = '{0}' ", MAKE_SURE_APOSTROPHY_IS_SQL_READY(PatientChart)));
                
                // Don't forget the order by                
                switch (Case)
                {
                    case DefaultCase.DateModified:
                        sb.Append("Order By c.[Date Modified] DESC");
                        break;
                    case DefaultCase.HighestCase:
                        sb.Append("Order By c.[Case Number] DESC");
                        break;
                    case DefaultCase.LowestCase:
                        sb.Append("Order By c.[Case Number] ASC");
                        break;
                }
                return sb.ToString();
            }
            return String.Empty;
        }

        /// <summary>
        /// Build the Appointment / Patient SQL Query as needed, either specify a Date Range or give a specific ID
        /// </summary>
        /// <param name="startDT"></param>
        /// <param name="endDT"></param>
        /// <returns></returns>
        private static string BuildAppointmentQuery(int nApptID, DateTime startDT, DateTime endDT, string[] providers, string[] resources)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT a.[ID],a.[Resource],a.[Date], a.[Status],a.[Start Time],a.[Case Number],a.[Length],a.[Name],a.[Chart Number],a.[Provider],a.[Procedure Code],a.[Note],a.[Color],a.[Reason Code]");
            sb.Append(",a.[Phone] as [Appt Home Phone],a.[Cell Phone] as [Appt Mobile Phone]");
            sb.Append(",p.[First Name],p.[Middle Name],p.[Last Name],p.[Date of Birth] as [Birth Date],p.[Sex],p.[Street 1],p.[Street 2],p.[City]");
            //sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Work Phone],p.[Work Extension], p.[Social Security Number]");   // Not using Work Phone / Work Extension         
            sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Social Security Number]");            
            sb.Append(",c.[Copayment Amount] as [Patient Copay]");
            sb.Append(",r.[Description] as [Reason Description]");
            sb.Append(",i.[Name] as [Primary Insurance Name]");
            sb.Append(",h.[Last Name] as [Ref Phy Last Name],h.[First Name] as [Ref Phy First Name],h.[Office] as [Ref Physician Phone],h.[Phone] as [Ref Physician Main Phone], h.[Cell] as [Ref Physician Mobile Phone]");
            sb.Append(" FROM [OHAPP] a LEFT JOIN");
            sb.Append(" [MWPAT] p ON p.[Chart Number] = a.[Chart Number]");
            sb.Append(" LEFT JOIN [OHRSN] r ON a.[Reason Code] = r.[Code]");
            sb.Append(" LEFT JOIN [MWCAS] c ON a.[Case Number] = c.[Case Number]");
            sb.Append(" LEFT JOIN [MWINS] i ON c.[Insurance Carrier #1] = i.[Code]");
            sb.Append(" LEFT JOIN [MWRPH] h ON c.[Referring Provider] = h.[Code]");
            sb.Append(" WHERE ");

            // if ID is specified, we are looking for a single appointment,
            // otherwise we are looking for a range
            if (nApptID >= 0)
            {
                sb.Append(String.Format(" [ID] = {0} ", nApptID));
            }
            else
            {
                //    [Date] between '10/24/2012' and '10/27/2012' AND             
                const string DATE_FORMAT = "{0:D2}/{1:D2}/{2:D4}";
                string StartDate = String.Format(DATE_FORMAT, startDT.Month, startDT.Day, startDT.Year);
                string EndDate = String.Format(DATE_FORMAT, endDT.Month, endDT.Day, endDT.Year);
                sb.Append(String.Format("[Date] between '{0}' AND '{1}' AND ", StartDate, EndDate));

                //    [Appt Time] between '9:00:00 AM' AND '12:30:00 PM'                
                string StartTime = startDT.ToString("hh:mm:ss tt");
                string EndTime = endDT.ToString("hh:mm:ss tt");
                sb.Append(String.Format("[Start Time] between '{0}' AND '{1}' ", StartTime, EndTime));

                // Do we have providers?
                // AND [Provider] in ('LAB',)  
                if (providers != null && providers.Length > 0)
                {
                    sb.Append("AND [Provider] in ('',");  // Allow '' blank providers
                    for (int i = 0; i < providers.Length; ++i)
                    {
                        sb.Append("'");
                        sb.Append(MAKE_SURE_APOSTROPHY_IS_SQL_READY(providers[i]));
                        sb.Append("'");
                        if (i != providers.Length - 1)
                            sb.Append(",");
                    }
                    sb.Append(") ");
                }

                // Do we have resources?
                // AND [Resource] in (2, )
                if (resources != null && resources.Length > 0)
                {
                    sb.Append("AND [Resource] in ('',"); // Allow '' blank resources
                    for (int i = 0; i < resources.Length; ++i)
                    {
                        sb.Append(resources[i]);
                        if (i != resources.Length - 1)
                            sb.Append(",");
                    }
                    sb.Append(") ");
                }

                // Filter out all Cancelled Appointments
                sb.Append("AND (a.[Status] <> '4' ");

                // Allow "Break" appointment types
                sb.Append("OR a.[Status] IS NULL) ");

                // Don't forget the order by
                sb.Append("Order By [Date],[Start Time],[ID]");
            }

            return sb.ToString();
        }

        #endregion       

        #region Public Appointment Methods

        /// <summary>
        /// Retrieve all appointments for the given time period for the given providers or resources
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="providerInfo"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static AppointmentInfo1[] GetAppointments(string SystemApiKey, ProviderInfo1[] providerInfo, ResourceInfo1[] resourceInfo, DateTime startDate, DateTime endDate)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);

            string sql = "";
            List<string> providers = new List<string>();
            List<string> resources = new List<string>();
            if (providerInfo != null)
            {
                foreach (ProviderInfo1 info1 in providerInfo)
                {
                    if (!String.IsNullOrEmpty(info1.UserId))
                        providers.Add(info1.UserId);
                }
            }
            if (resourceInfo != null)
            {
                foreach (ResourceInfo1 info1 in resourceInfo)
                {
                    if (!String.IsNullOrEmpty(info1.Name))
                        resources.Add(info1.Name);
                }
            }

            // Build the Appointment QUERY (don't pass in an Appt ID and it will use a range)
            sql = BuildAppointmentQuery(-1, startDate, endDate, providers.ToArray(), resources.ToArray());

            DBRetVal result = db.FillDataTable(sql);
            List<AppointmentInfo1> appointments = new List<AppointmentInfo1>();
            if (result.IsValid)
            {
                foreach (DataRow row in result.GetDataTableRetVal().Rows)
                {
                    AppointmentInfo1 info = new AppointmentInfo1();

                    info.Id = DataRet.Retrieve<long>(row["ID"]);

                    // Retrieving Description for the Appointment Reason Code via join
                    info.Reason = DataRet.Retrieve(row["Reason Description"]);

                    //Retrieving Provider code for the appointment
                    info.ProviderCode = DataRet.Retrieve(row["Provider"]);

                    // Let's figgure out the Appointment DTs
                    // StartDateTime = DateTime.Parse(string.Format("{0} {1}:{2}:00 AM", today.ToShortDateString(), 2, 15)),
                    // EndDateTime = DateTime.Parse(string.Format("{0} {1}:{2}:00 PM", today.ToShortDateString(), 2, 30)),                    
                    DateTime dtDate = DataRet.Retrieve<DateTime>(row["Date"]);
                    DateTime dtTime = DataRet.Retrieve<DateTime>(row["Start Time"]);
                    int nDuration = DataRet.Retrieve<int>(row["Length"]);

                    info.StartDateTime = DateTime.Parse(string.Format("{0} {1:D2}:{2:D2}:{3:D2}", dtDate.ToShortDateString(), dtTime.Hour, dtTime.Minute, dtTime.Second));
                    if (nDuration == 0)
                    {
                        // Leave it as is (no duration was set)
                        info.EndDateTime = info.StartDateTime;
                    }
                    else
                    {
                        dtTime = dtTime.AddMinutes(nDuration);
                        info.EndDateTime = DateTime.Parse(string.Format("{0} {1:D2}:{2:D2}:{3:D2}", dtDate.ToShortDateString(), dtTime.Hour, dtTime.Minute, dtTime.Second));
                    }

                    // Appointment Primary Phone
                    bool bAppointmentPhoneFound = false;
                    string Phone = DataRet.Retrieve(row["Appt Home Phone"]);
                    if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                    {
                        bAppointmentPhoneFound = true;
                    }
                    else
                    {
                        Phone = DataRet.Retrieve(row["Appt Mobile Phone"]);
                        if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                            bAppointmentPhoneFound = true;
                    }

                    // Attach Chart Information (Only the most basic information, try to attach Phone Number if none found in appointment)
                    bool bHasChart = (DataRet.Retrieve(row["Chart Number"]) != "");
                    if (bHasChart)
                        info.Patient = FillInPatientInformation(row, false, !bAppointmentPhoneFound);
                    else
                        info.AppointmentDescription = DataRet.Retrieve(row["Name"]);

                    // Attach Appointment Phone always, if found
                    if (bAppointmentPhoneFound)
                        info.AppointmentPhone = Phone;

                    //[Background Color]
                    //#define DEFAULT_GRID_TEXT_COLOR    (RGB(0,0,0))
                    //#define DEFAULT_GRID_BKGND_COLOR  (RGB(255,255,255))
                    int ColorRef = DataRet.Retrieve<int>(row["Color"], 0);
                    if (ColorRef == 0)
                        info.AppointmentColor = new AppointmentColor1() { Red = 255, Green = 255, Blue = 255 };
                    else
                        info.AppointmentColor = new AppointmentColor1() { Red = Functions.GetRValue(ColorRef), Green = Functions.GetGValue(ColorRef), Blue = Functions.GetBValue(ColorRef) };

                    // Determine the Appointment Status
                    /*
                     * Medisoft Status
                        0 – Unconfirmed
                        1 – Confirmed
                        2 – Checked In
                        3 – Missed
                        4 – Cancelled
                        5 – Being Seen
                        6 – Checked Out
                        7 – Rescheduled
                     * --------------------------
                     * Medisoft To Mobile Mapping
                        0 – Unconfirmed_Pending
                        1 – Confirmed
                        2 – CheckedIn_Waiting
                        3 – Missed
                        4 – Cancelled
                        5 – Being Seen
                        6 – CheckedOut_Completed
                        7 - Rescheduled
                     * */
                    string status = DataRet.Retrieve(row["Status"]);
                    switch (status)
                    {
                        case "0":
                            info.AppointmentStatus = AppointmentStatusEnum.Unconfirmed_Pending;
                            break;
                        case "1":
                            info.AppointmentStatus = AppointmentStatusEnum.Confirmed;
                            break;
                        case "2":
                            info.AppointmentStatus = AppointmentStatusEnum.CheckedIn_Waiting;
                            break;
                        case "3":
                            info.AppointmentStatus = AppointmentStatusEnum.Missed;
                            break;
                        case "4":
                            info.AppointmentStatus = AppointmentStatusEnum.Cancelled;
                            break;
                        case "5":
                            info.AppointmentStatus = AppointmentStatusEnum.BeingSeen;
                            break;
                        case "6":
                            info.AppointmentStatus = AppointmentStatusEnum.CheckedOut_Completed;
                            break;
                        case "7":
                            info.AppointmentStatus = AppointmentStatusEnum.Rescheduled;
                            break;
                        default:
                            break;
                    }

                    // Finally, add the appointment
                    appointments.Add(info);
                }
                return appointments.ToArray();
            }
            return null;
        }

        /// <summary>
        /// Appointment Detail query. Fills in useful information for the patient regarding the Appointment
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="ApptID"></param>
        /// <returns></returns>
        public static AppointmentDetail1 GetAppointmentDetail(string SystemApiKey, int ApptID, bool bCalculateBalance)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);

            string sql = BuildAppointmentQuery(ApptID, DateTime.MinValue, DateTime.MinValue, null, null);
            DBRetVal result = db.FillDataTable(sql);
            if (result.IsValid)
            {
                DataRow row = result.GetDataTableFirstRow();
                AppointmentDetail1 detail = new AppointmentDetail1();

                // Appointment Stuff
                detail.ApptNote = DataRet.Retrieve(row["Note"]);
                detail.ApptReason = DataRet.Retrieve(row["Reason Description"]);

                // Chart Info
                bool bHasChart = (DataRet.Retrieve(row["Chart Number"]) != "");
                if (bHasChart)
                    detail.PatientDetail = GetPatientDetail(SystemApiKey, row, String.Empty, bCalculateBalance);

                // Return the Appointment Detail Item 
                return detail;
            }

            return null;
        }

        /// <summary>
        /// Similar to GetAppointmentDetail, except all we have here is only the chart, so no appointment information
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="UseThisRowInsteadOfQuery">if this is set to not null (a valid DBRetVal), it will use the DBRetVal and not re-query</param>
        /// <param name="ApptID"></param>
        /// <returns></returns>
        public static PatientDetail1 GetPatientDetail(string SystemApiKey, DataRow UseThisRowInsteadOfQuery, string PatientChart, bool bCalculateBalance)
        {
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);

            DataRow row = null;
            // Query only if the row has not been passed in
            if (UseThisRowInsteadOfQuery == null)
            {
                string sql = BuildPatientQuery(PatientChart, GetDefaultCase(SystemApiKey));
                DBRetVal result = db.FillDataTable(sql);
                if (result.IsValid)
                    row = result.GetDataTableFirstRow();
            }
            else
            {
                row = UseThisRowInsteadOfQuery;
            }

            // Fill in the information            
            if (row != null)
            {
                PatientDetail1 detail = new PatientDetail1();

                // Chart Info                
                detail.Patient = FillInPatientInformation(row, true, true);

                // Patient Billing Information
                detail.PatientBilling = new PatientBillingInfo1();

                detail.PatientBilling.CopayAmount = DataRet.Retrieve<decimal>(row["Patient Copay"], 0);
                detail.PatientBilling.PrimaryInsCarrier = DataRet.Retrieve(row["Primary Insurance Name"]);

                // Refering Provider comes from Patient Table also                    
                detail.PatientBilling.RefProvider = DataRet.Retrieve(row["Ref Phy First Name"]) + " " + DataRet.Retrieve(row["Ref Phy Last Name"]);

                // Fill in Refering Provider Phone 
                string Phone = DataRet.Retrieve(row["Ref Physician Phone"]);
                if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                {
                    detail.PatientBilling.RefProviderPhone = Phone;
                }
                else
                {
                    Phone = DataRet.Retrieve(row["Ref Physician Main Phone"]);
                    if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                    {
                        detail.PatientBilling.RefProviderPhone = Phone;
                    }
                    else
                    {
                        Phone = DataRet.Retrieve(row["Ref Physician Mobile Phone"]);
                        if (!String.IsNullOrEmpty(Phone) && Phone != "(   )    -")
                        {
                            detail.PatientBilling.RefProviderPhone = Phone;
                        }
                    }
                }

                // Calculate Account Balance (May take awhile)
                if (bCalculateBalance)
                {
                    detail.PatientBilling.AccountBalance = CalculatePatientBalance(detail.Patient.ChartId, SystemApiKey);
                }

                // Return the Appointment Detail Item 
                return detail;
            }

            return null;
        }

        /// <summary>
        /// Used to calculate the Patient Balance (stripped out of Lytec)
        /// </summary>
        /// <param name="Chart"></param>
        /// <returns></returns>
        private static decimal CalculatePatientBalance(string Chart, string SystemApiKey)
        {
            if (!String.IsNullOrEmpty(Chart))
            {
                // Copied from Medisoft Source Code (Sent to my by Scott Toney)
                // Query1: This is used to get the patient balance without the missed copays
                StringBuilder sb1 = new StringBuilder();
                sb1.Append("SELECT SUM([Amount] + ");
                sb1.Append("[Insurance 1 Amount Paid] + ");
                sb1.Append("[Insurance 2 Amount Paid] + ");
                sb1.Append("[Insurance 3 Amount Paid] + ");
                sb1.Append("[Guarantor Amount Paid] + ");
                sb1.Append("[Attorney Amount Paid] + ");
                sb1.Append("[Adjustment Amount] ");
                sb1.Append(") as RemainderBalance ");
                sb1.Append("FROM [MWTRN] ");
                //sb1.Append("WHERE [Transaction type] <= " + QuotedStr('H') + " AND ");
                sb1.Append("WHERE [Transaction type] <= 'H' AND ");
                sb1.Append("( [Guarantor Responsible] = True ) ");
                sb1.Append("AND ([Insurance 1 Paid] = True ) ");
                sb1.Append("AND ([Insurance 2 Paid] = True ) ");
                sb1.Append("AND ([Insurance 3 Paid] = True ) ");
                sb1.Append("AND ([Guarantor Paid] = False ) AND ");
                sb1.Append("[case number] IN ");
                sb1.Append("(SELECT [case number] FROM [MWCAS] WHERE [guarantor] = ");
                sb1.Append("(SELECT TOP 1 [guarantor] FROM [MWCAS] WHERE [chart number] = '" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(Chart) + "')) ");
                string sqlQuery1 = sb1.ToString();

                // Copied from Medisoft Source Code (Sent to my by Scott Toney)
                // Query2: run the query to get the missed copays
                StringBuilder sb2 = new StringBuilder();
                sb2.Append("SELECT SUM([Copayment_Expected_Amount] + ");
                sb2.Append("[Copayment_Amount_Paid] ");
                sb2.Append(") as CopayBalance ");
                sb2.Append("from [MWTRN] ");
                //sb2.Append("WHERE [Transaction type] <= " + QuotedStr('H') + " AND ");
                sb2.Append("WHERE [Transaction type] <= 'H' AND ");
                sb2.Append("( [Guarantor Responsible] = True ) ");
                sb2.Append("AND ( (( [Insurance 1 Responsible] = True ) and ( [Insurance 1 Paid] <> [Insurance 1 Responsible] )) ");
                sb2.Append("OR  (( [Insurance 2 Responsible] = True ) and ( [Insurance 2 Paid] <> [Insurance 2 Responsible] )) ");
                sb2.Append("OR  (( [Insurance 3 Responsible] = True ) and ( [Insurance 3 Paid] <> [Insurance 3 Responsible] )) ");
                sb2.Append(") ");
                sb2.Append("AND ( [Guarantor Paid] = False ) and ");
                sb2.Append("[case number] in ");
                sb2.Append("(SELECT [case number] From [MWCAS] WHERE [guarantor] = ");
                sb2.Append("(SELECT TOP 1 [guarantor] from [MWCAS] where [chart number] = '" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(Chart) + "')) ");
                string sqlQuery2 = sb2.ToString();

                // Determine which query to run (Balance or CoPay Remainder)
                string sql = "";
                if (UseCopayRemainderCalculation(SystemApiKey))
                    sql = sqlQuery2;
                else
                    sql = sqlQuery1;

                // Run the Query
                AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
                DB db = DB.Create(credential);
                DBRetVal result = db.ExecuteScalar(sql);
                if (result.IsValid)
                {
                    decimal Balance = DataRet.Retrieve<decimal>(result.GetScalarRetVal(), 0);
                    return Balance;
                }
            }
            return 0;
        }

        #endregion

        #region Post Billing

        /// <summary>
        /// Post Billing to Unproccessed Transactions
        /// </summary>
        /// <param name="SystemApiKey"></param>
        /// <param name="User"></param>
        /// <param name="billingInfo"></param>
        /// <returns></returns>
        public static bool PostBilling(string SystemApiKey, string User, BillingPost1 billingInfo)
        {
            // Make sure this is a valid BillingPost we are getting passed in
            if ((billingInfo == null) || (billingInfo.BillingDetails == null) ||
                (billingInfo.BillingDetails.Length == 0))
                return false;

            // Generate new Unique ID
            string UniqueID = Guid.NewGuid().ToString();
            UniqueID = UniqueID.Replace("-", "").Substring(0, 32);

            // Create encryption code.
            string sSource = billingInfo.ChartID + billingInfo.PatientLastName + UniqueID;
            string s64Trimmed = CrossProduct.Core.Utils.MD5EncryptMsg(sSource, 20);
            bool bSuccessfulPost = true;
            
            // Setup Main DB
            AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey);
            DB db = DB.Create(credential);

            // for auditing * may not work *
            AdvantageCredential credentialUser = null;
            DB dbUser = null;

            // Try to Audit, following Scott's instructions, however, there seems to be no
            // guarantee that this works (Advantage can get quickly out of sync it seems)
            bool bTryToAudit = !String.IsNullOrEmpty(User);
            bTryToAudit = false;
            if (bTryToAudit)
            {
                credentialUser = DBCache.GetAdvantageConnection(SystemApiKey, User);
                dbUser = DB.Create(credentialUser);
            }            

            // Deal with Notes first * Load them all up into a Memo Field *
            StringBuilder sbBillingNotes = new StringBuilder();
            for (int i = 0; i < billingInfo.BillingDetails.Length; ++i)
            {
                BillingDetail1 detail = billingInfo.BillingDetails[i];
                if (!String.IsNullOrEmpty(detail.BillingNote))
                {
                    sbBillingNotes.Append(detail.BillingNote);
                    sbBillingNotes.Append("\n"); // Could be '\r' - test
                }
            }
            if(sbBillingNotes.Length > 0)
               sbBillingNotes.Remove(sbBillingNotes.Length - 1, 1); // remove trailing '\n'

            // Deal with the rest of the items, notes are stuffed into the first item only
            bool bFirstInsert = true;
            int nNotesFound = 0;
            for (int i = 0; i < billingInfo.BillingDetails.Length && bSuccessfulPost; ++i)
            {
                BillingDetail1 detail = billingInfo.BillingDetails[i];

                // Skip Billing Notes (already dealt with)
                if (!String.IsNullOrEmpty(detail.BillingNote))
                {
                    nNotesFound++;
                    continue;
                }

                StringBuilder ValueList = new StringBuilder();
                string newID = CrossProduct.Core.Utils.CreateGUID();
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(newID) + "', ");                           // ID                                                        // ID
                ValueList.Append("0, ");                                                                            // Post, tinyint
                ValueList.Append("0, ");                                                                            // Transaction status, int
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.ChartID) + "', ");	            // varchar15
                ValueList.Append("0, ");                                                                            // Billing, Case int
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.ProviderID) + "', ");	        // varchar 15

                // Make sure valid Date From
                if (billingInfo.DateFrom != null && billingInfo.DateFrom != DateTime.MinValue)
                    ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTStringDateOnly(billingInfo.DateFrom) + "', sql_timestamp), "); // datetime
                else
                    ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTStringDateOnly(DateTime.Now) + "', sql_timestamp), "); // datetime

                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureCode) + "', ");	        // varchar 15
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier1) + "', ");	    // varchar 3
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier2) + "', ");       // varchar 3
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier3) + "', ");       // varchar 3
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier4) + "', ");       // varchar 3
                ValueList.Append(detail.Units + ", ");			                                                    // real
                ValueList.Append((i + 1 - nNotesFound).ToString() + ", ");                                          // Procedure Order, int
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.PatientLastName) + "', ");     // patient last name
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.PatientFirstName) + "', ");    // patient first name

                // Don't insert an invalid birthday (Medisoft allows nulls)
                if (billingInfo.BirthDate != null && billingInfo.BirthDate != DateTime.MinValue)
                    ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTStringDateOnly(billingInfo.BirthDate) + "', sql_timestamp), "); // datetime
                else
                    ValueList.Append(" NULL, "); // datetime

                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.PatientSocialSecurity) + "', ");// varchar 11
                ValueList.Append("-1, ");                                                                           // Exception	// int
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.Facility) + "', ");	        // Facility
                ValueList.Append("0, ");                                                                            // Created By Application.	// int
                ValueList.Append("0, ");                                                                 	        // Amount
                ValueList.Append("'" + UniqueID + "', ");               	                                        // varchar 32
                ValueList.Append("'', ");                                                                           // Error msg.	varchar 80
                ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTString(DateTime.Now) + "', sql_timestamp), ");   // Created Date, datetime            
                ValueList.Append("NULL, ");                                                                         // mod date, datetime
                ValueList.Append("0,");                                                                             // Viewed, tinyint            
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(s64Trimmed) + "',");                       // Encryption Code.            
                ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ImmunizationBatchNumber) + "', ");	// Immunization Batch Number ? 

                // On First Insert, add the billing notes to first post
                if (bFirstInsert)
                {
                    if (sbBillingNotes.Length > 0)
                        ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(sbBillingNotes.ToString()) + "', ");	// Mobile Notes. Memo.
                    else
                        ValueList.Append("'', ");                                                                       // Mobile Notes. Memo.
                    bFirstInsert = false;
                }
                else
                    ValueList.Append("'', ");                                                                           // Mobile Notes. Memo.

                /// *******
                /// Attach each Diagnosis Code (we can do up to 12) for each procedure
                /// Always do this last, deals with trailing Comma (,)
                /// *******                
                for (int j = 0; j < 12; ++j)
                {
                    try
                    {
                        ValueList.Append("'");
                        ValueList.Append(MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.DiagnosisCodes[j]));    // Diagnosis 1 - 12, varchar 15
                        if (j < 11)
                            ValueList.Append("', ");
                        else
                            ValueList.Append("'");
                    }                    
                    catch (IndexOutOfRangeException)
                    {
                        if (j < 11)
                            ValueList.Append("', ");
                        else
                            ValueList.Append("'");
                    }
                    catch(NullReferenceException)
                    {
                        if (j < 11)
                            ValueList.Append("', ");
                        else
                            ValueList.Append("'");
                    }
                }
                
                // Build our sql statement.
                StringBuilder sb = new StringBuilder("INSERT INTO [MWUTC] ");                
                sb.Append(" ([ID], [Post], [Transaction_Status], [Chart_Number], [Case_Number], [Provider], [Date_From], [Procedure_Code], [Modifier_1], [Modifier_2], [Modifier_3], [Modifier_4], [Units], [Procedure_Order], [Patient_Last_Name], [Patient_First_Name], [Birth_Date], [SSN], [Exception], [Facility], [Creating_App], [Amount], [HL7MessageID], [ErrorMsg], [Date_Created], [Date_Modified], [Viewed], [Checksum], [Immunization Batch Number], [Mobile Notes], [Diagnosis_Code_1], [Diagnosis_Code_2], [Diagnosis_Code_3], [Diagnosis_Code_4], [Diagnosis_Code_5], [Diagnosis_Code_6], [Diagnosis_Code_7], [Diagnosis_Code_8], [Diagnosis_Code_9], [Diagnosis_Code_10], [Diagnosis_Code_11], [Diagnosis_Code_12])");                                
                sb.Append(" VALUES (" + ValueList.ToString() + ")");
                string StrSQL = sb.ToString();

                DBRetVal result = null;
                if (bTryToAudit)
                {
                    // Post the Billing (via user way)
                    result = dbUser.ExecuteNonQuery(StrSQL);
                    bSuccessfulPost = result.IsValid;
                    if (bSuccessfulPost)
                    {
                        // posted succesful for user moving on
                        continue;                        
                    }
                    else
                    {
                        MSLSpecific.Logger.Info("submitting superbill via the user {0} didn't work, will submitt the superbill without the user credential (msg:{1})", User, result.ErrorMsg);
                        bTryToAudit = false;
                    }
                }
                
                // Post the Billing (the regular way)
                result = db.ExecuteNonQuery(StrSQL);
                bSuccessfulPost = result.IsValid;   
            }

            return bSuccessfulPost;
        }

        #endregion
    }
}
