public Contact Add(Contact contact)
        {
            int id = contact.ID.HasValue ? contact.ID.Value : nextId++;

            nextId = Math.Max(nextId, id + 1);

            Contact withId = Contact.CreateWithId(id, contact);

            Log.Verbose("Add: adding new contact with ID {0} ({1} {2})", withId.ID, withId.FirstName, withId.LastName);
            contacts.Add(withId);

            // Add to the state-level cache.
            // If the state does not currently exist in the cache - add the binary tree
            if (!stateCache.ContainsKey(withId.State))
            {
                stateCache.Add(withId.State, new BinaryTree <Contact>());
            }

            // now we know the state exists so add the contact
            stateCache[withId.State].Add(withId);

            Log.Verbose("Add: complete ({0})", withId.ID);

            return(withId);
        }
        private bool ReadContact(string line, List<Column> columns, out Contact contact)
        {
            if (string.IsNullOrEmpty(line))
            {
                contact = default;
                return false;
            }

            string[] values = line.Split(new[] { seperator }, StringSplitOptions.None);

            if (values.Length != columns.Count)
            {
                Console.WriteLine(string.Format("Contact line does not contain appropriate number of columns: {0}", line));
                contact = default;
                return false;
            }

            int? id = new Nullable<int>();
            string first = null;
            string last = null;
            string street = null;
            string city = null;
            string state = null;
            string zip = null;

            for (int i = 0; i < values.Length; i++)
            {
                switch (columns[i].ColumnName)
                {
                    case "id":
                        if (!string.IsNullOrEmpty(values[i]) && !string.IsNullOrEmpty(values[i].Trim()))
                        {
                            id = int.Parse(values[i]); break;
                        }
                        break;
                    case "firstname": first = values[i]; break;
                    case "lastname": last = values[i]; break;
                    case "streetaddress": street = values[i]; break;
                    case "city": city = values[i]; break;
                    case "state": state = values[i]; break;
                    case "postalcode": zip = values[i]; break;
                    default:
                        throw new InvalidOperationException("Unknown column name: " + columns[i].ColumnName);
                }
            }

            contact = Contact.Create(first, last, street, city, state, zip);

            if (id.HasValue) contact = Contact.CreateWithId(id.Value, contact);

            return true;
        }