void PaintHeadings(PaintEventArgs e, int Start, int End, RichTextBox RichText, System.Text.RegularExpressions.Regex regex2, Color color, string text, Font f)
        {
            //string exp = ".*\\=([^)]+?)\\=";

            // this one was fast but inaccurate (very)
            //string exp = String.Format (".*?\\=\\w([^)]+?)\\w\\={0}?", Environment.NewLine); // added lazy operations to attempt a speed improvement
                Graphics g = e.Graphics;//RichText.CreateGraphics ();

            System.Drawing.Pen pen1 = new Pen(color);

            System.Text.RegularExpressions.MatchCollection matches = regex2.Matches (RichText.Text, Start);

            foreach (System.Text.RegularExpressions.Match match in matches) {
                if (match.Index > End)
                    break; // we exit if already at end

                Point pos = RichText.GetPositionFromCharIndex (match.Index);

                if (pos.X > -1 && pos.Y > -1) {

                    pen1.Width = 8;
                    string stext = text+match.Value+text;
                    //pos = CoreUtilities.General.BuildLocationForOverlayText (pos, DockStyle.Right,match.Value );
                    int newWidth = Convert.ToInt32 (g.MeasureString (stext, f).Width * RichText.ZoomFactor);
                    pos =  new Point(pos.X + (newWidth), pos.Y + 10);
                    g.DrawLine (pen1, pos.X, pos.Y, pos.X + 500, pos.Y);

            //					Rectangle rec = GetRectangleForSmallRectangle(g, pos, RichText, match.Index, match.Value, true);// new Rectangle (new Point (pos.X+(int)(scaler*1.5), pos.Y -(5+scaler)), new Size ((int)(scaler * 1.5), (int)(scaler * 1.5)));
            //				rec.Height = 2;
            //
            //				g.DrawRectangle(pen1, rec);
                }
            }
            //	g.Dispose (); don't dispose what we are borrowing
        }
        public Contact ValidateProviderContact( DataTable objDT, System.Text.RegularExpressions.Regex objRegExZip, System.Text.RegularExpressions.Regex objRegExEmail, System.Text.RegularExpressions.Regex objRegExPhone, List<EMSC2SF.User> objExistingUsers, List<EMSC2SF.Sub_Region__c> objSubRegions, List<string> objSpecialties, string[] strSpecialtySearchFor, string[] strSpecialtySubstitutions, List<string> objLeadSources, List<string> obj2ndLeadSources, string[] strSearchFor, string[] strSubstitutions, List<Contact> objContacts, DataRow objDR )
        {
            // copy all datatable columns to contact object attributes
            Contact objNewContact = objDR.ConvertTo<Contact>( true );

            objNewContact.Middle_Name__c = objNewContact.Middle_Name__c.RemoveSpecialCharacters();

            // validate zip
            System.Text.RegularExpressions.MatchCollection objMatchZip = objRegExZip.Matches( objNewContact.Zipcode__c );
            if( objMatchZip.Count > 0 )
                objNewContact.Zipcode__c = objMatchZip[ 0 ].Value;

            objNewContact.Zipcode__c = Company2SFUtils.ValidatePostalCode( objNewContact.Zipcode__c );
            objNewContact.OtherPostalCode = Company2SFUtils.ValidatePostalCode( objNewContact.OtherPostalCode );

            // validate ssn
            if( objNewContact.SSN__c != null && objNewContact.SSN__c.Length != 9 )
                objNewContact.SSN__c = "";

            // extract and validate email
            string strUnformattedEmail = objDR[ "UnformattedEmail" ].ToString();
            strUnformattedEmail = strUnformattedEmail.Replace( ",", "." ).Replace( "..", "." ).Replace( " ", "" )
                    .Replace( ">", "." ).Replace( "@@", "@" );
            System.Text.RegularExpressions.MatchCollection objMatch = objRegExEmail.Matches( strUnformattedEmail );
            if( objMatch.Count > 0 )
                objNewContact.Email = objMatch[ 0 ].Value;
            if( objMatch.Count > 1 )
                objNewContact.Contacts_Email_No_2__c = objMatch[ 1 ].Value;
            if( objMatch.Count > 2 )
                objNewContact.Contacts_Email_No_3__c = objMatch[ 2 ].Value;
            if( objMatch.Count == 0 )
                objNewContact.Email = "";

            // validate phones
            objNewContact.HomePhone = Company2SFUtils.ValidPhone( objRegExPhone, objNewContact.HomePhone );
            objNewContact.OtherPhone = Company2SFUtils.ValidPhone( objRegExPhone, objNewContact.OtherPhone );
            objNewContact.Work_Phone__c = Company2SFUtils.ValidPhone( objRegExPhone, objNewContact.Work_Phone__c );
            objNewContact.MobilePhone = Company2SFUtils.ValidPhone( objRegExPhone, objNewContact.MobilePhone );
            objNewContact.Fax = Company2SFUtils.ValidPhone( objRegExPhone, objNewContact.Fax );

            // set the owning region using appointments, if blank, use the region in the provider's record
            string strOwningRegion = objDR[ "Default_Owning_Region" ].ToString();
            switch( strOwningRegion )
            {
                case "CEN":
                case "6":
                case "14":
                case "2":
                    strOwningRegion = "SWED";
                    break;
                case "SE":
                case "7":
                    strOwningRegion = "SED";
                    break;
                case "NE":
                    strOwningRegion = "NED";
                    break;
                case "PW":
                    strOwningRegion = "PWED";
                    break;
                case "DMS":
                    strOwningRegion = "PWDMS";
                    break;
                case "SANJ":
                    strOwningRegion = "PWSANJ";
                    break;
                case "4":
                case "1":
                case "9":
                case "11":
                case "13":
                    strOwningRegion = "";
                    break;
                case "":
                    strOwningRegion = objDR[ "Less_Reliable_Region" ].ToString();
                    break;
                default: // don't change it
                    break;
            }
            objNewContact.Owning_Region__c = strOwningRegion;

            // convert specialty
            objNewContact.Specialty__c = Company2SFUtils.ConvertToStandardValue( objSpecialties, strSpecialtySearchFor, strSpecialtySubstitutions
                , objNewContact.Specialty__c );

            // convert lead source
            LeadSourcePair objLSResults = ConvertLeadSource( objLeadSources, obj2ndLeadSources
                , strSearchFor, strSubstitutions, objNewContact.Lead_Source_1__c );
            objNewContact.Lead_Source_1__c = objLSResults.LeadSource1;
            objNewContact.Lead_Source_Text__c = objLSResults.LeadSource2;

            // convert recruiter usercode to Salesforce User ID
            if( !objDR[ "Recruiter" ].IsNullOrBlank() )
            {
                string strRecruiter = objDR[ "Recruiter" ].ToString();
                EMSC2SF.User objFound = objExistingUsers.FirstOrDefault(
                            u => u.Company_Usercode__c != null
                                && u.Company_Usercode__c.Equals( strRecruiter )
                                && u.IsActive == true );
                if( objFound != null )
                    objNewContact.OwnerId = objFound.Id;
                else
                {
                    // find the recruiting manager for the region
                    Sub_Region__c objFoundSR = objSubRegions.FirstOrDefault( i => i.Name.Equals( strOwningRegion ) );
                    if( objFoundSR != null )
                    {
                        // check whether recruiting manager/user is active
                        objFound = objExistingUsers.FirstOrDefault(
                                    u => u.Id.Equals( objFoundSR.Recruiting_Manager__c )
                                        && u.IsActive == true );
                        if( objFound != null )
                            objNewContact.OwnerId = objFound.Id;
                    }

                    // many providers don't have a region (or the region manager is inactive)
                    // so the owner will be whoever does the data load
                }
            }

            // fix time zone bug
            objNewContact.Birthdate = Company2SFUtils.FixTimeZoneBug( objNewContact.Birthdate );
            objNewContact.Drivers_License_Expiration_Date__c =
                Company2SFUtils.FixTimeZoneBug( objNewContact.Drivers_License_Expiration_Date__c );

            // for some reason, this is required for the upsert to work
            objNewContact.RecruitingID__cSpecified = true;
            objNewContact.PhysicianNumber__cSpecified = true;
            objNewContact.CreatedDateSpecified = true;
            objNewContact.Active_Military__cSpecified = true;
            objNewContact.Deceased__cSpecified = true;
            objNewContact.Directorship_Experience__cSpecified = true;
            objNewContact.Drivers_License_Expiration_Date__cSpecified = true;
            objNewContact.BirthdateSpecified = ( objNewContact.Birthdate != null );

            // copy other address into main address if main is empty
            if( objNewContact.Address_Line_1__c.IsNullOrBlank() & objNewContact.City__c.IsNullOrBlank()
                && !objNewContact.OtherStreet.IsNullOrBlank() )
            {
                objNewContact.Address_Line_1__c = objNewContact.OtherStreet;
                objNewContact.Address_Line_2__c = "";
                objNewContact.City__c = objNewContact.OtherCity;
                objNewContact.State__c = objNewContact.OtherState;
                objNewContact.Zipcode__c = objNewContact.OtherPostalCode;

                objNewContact.OtherStreet = "";
                objNewContact.OtherCity = "";
                objNewContact.OtherState = "";
                objNewContact.OtherPostalCode = "";
            }

            // add metaphone values
            string strName = objNewContact.FirstName;
            if( objNewContact.Middle_Name__c.IsNullOrBlank() )
                strName = string.Concat( strName, " ", objNewContact.LastName );
            else
                strName = string.Concat( strName, " ", objNewContact.Middle_Name__c, " ", objNewContact.LastName );

            objNewContact.Metaphone_Name__c = strName.ToNormalizedMetaphone();
            objNewContact.Metaphone_Address__c = objNewContact.Address_Line_1__c.ToNormalizedMetaphone().Left( 50 );
            objNewContact.Metaphone_City__c = objNewContact.City__c.ToNormalizedMetaphone();

            // find duplicate record in the list by matching metaphone name, phone and email
            Contact objContactFound = objContacts.FirstOrDefault( c =>
                                    c.IsADuplicateOf( objNewContact ) );

            if( objContactFound != null )
            {
                ReportStatus( "\r\nFound duplicate for provider ", objNewContact.FirstName, " ", objNewContact.LastName
                        , " - RecrID = ", objNewContact.RecruitingID__c.ToString() );

                // if found record doesn't have a physician number and the current one does
                // then skip the duplicate instead of adding it to the list
                if( objNewContact.PhysicianNumber__c == null
                    || ( !objContactFound.PhysicianNumber__c.Equals( 0.0 )
                        && objNewContact.PhysicianNumber__c.Equals( 0.0 ) ) )
                    return null;

                // check whether the found record is better than the current, if it is, skip the current (don't add)
                if( objNewContact.SSN__c.IsNullOrBlank() && !objContactFound.SSN__c.IsNullOrBlank() )
                    return null;
                if( objNewContact.Email.IsNullOrBlank() && !objContactFound.Email.IsNullOrBlank() )
                    return null;
                if( objNewContact.Birthdate.IsNullOrBlank() && !objContactFound.Birthdate.IsNullOrBlank() )
                    return null;
                if( objNewContact.Address_Line_1__c.IsNullOrBlank() && !objContactFound.Address_Line_1__c.IsNullOrBlank() )
                    return null;
                if( objNewContact.OtherStreet.IsNullOrBlank() && !objContactFound.OtherStreet.IsNullOrBlank() )
                    return null;
                if( objNewContact.HomePhone.IsNullOrBlank() && !objContactFound.HomePhone.IsNullOrBlank() )
                    return null;
                if( objNewContact.Work_Phone__c.IsNullOrBlank() && !objContactFound.Work_Phone__c.IsNullOrBlank() )
                    return null;

                ReportStatus( "\r\n* Removing duplicate for provider ", objNewContact.FirstName, " ", objNewContact.LastName
                        , " - RecrID = ", objNewContact.RecruitingID__c.ToString()
                        , " (duplicate RecrID ", objContactFound.RecruitingID__c.ToString(), ")" );

                // found record is not better so we keep this and delete the old one
                objContacts.Remove( objContactFound );
            }

            ReportStatus( "Processed contact ", objNewContact.FirstName, " ", objNewContact.LastName
                , " (", objContacts.Count.ToString(), "/", objDT.Rows.Count.ToString(), ")" );

            return objNewContact;
        }