#include <stdio.h>
#include "string_to_const_string.h"

/*
  Only one map type has been defined in the header, so all of the macros will
  point to these definitions. We can therefore safely use the macro names for
  all functions.

  `string_to_const_string.expanded.c` is identical to this file, except that
  the macros have been expanded there.
*/


#define FILTER_CHAR 'T'

/*
  Example of a traversal function. This will print each key-value pair in the
  trie on a separate line in the format "key -> value". No additional arguments
  are given when the traverse function is called va_args is ignored.
*/
int
print_key_and_value_pairs(
  RBT_KEY_DATA_T * key_data,
  RBT_KEY_SIZE_T height,
  va_list args
)
{
  if (! RBT_VALUE_IS_NULL(key_data->node->value))
  {
    // We can use our own single statement here.
    printf("%.*s -> %s\n", (int) key_data->bytes, (char *) key_data->key, key_data->node->value);

    // Or we can use the previously defined macros to do the same thing.
//     RBT_KEY_FPRINT(stdout, key_data->key, key_data->bytes);
//     printf(" -> ");
//     RBT_VALUE_FPRINT(stdout, key_data->node->value);
//     printf("\n");
  }
  return 0;
}

/*
  Another example of a traversal function. Here the key-value pairs with values
  beginning with a given character are inserted into a target node.
*/
int
insert_by_value_first_char(
  RBT_KEY_DATA_T * key_data,
  RBT_KEY_SIZE_T height,
  va_list args
)
{
  RBT_NODE_T * target_node;
  char c;

  target_node = va_arg(args, RBT_NODE_T *);
  // char is promoted to int when passed through a variadic function
  c = va_arg(args, int);
  if (! RBT_VALUE_IS_NULL(key_data->node->value) && (key_data->node->value)[0] == c)
  {
    RBT_NODE_QUERY(target_node, key_data->key, key_data->bits, RBT_QUERY_ACTION_INSERT, key_data->node->value);
  }
  return 0;
}




/*
  Example of a filter function. This filters by values.
*/
int
filter_by_value_first_char(RBT_VALUE_T * string_ptr, va_list args)
{
  char c;
  // char is promoted to int when passed through a variadic function
  c = (char) va_arg(args, int);
  return ((* string_ptr)[0] != c);
}



/*
  Example of a filter_with_key function. This filters by keys.
*/
int
filter_key_by_first_char(
  RBT_KEY_DATA_T * key_data,
  va_list args
)
{
  char c;
  // char is promoted to int when passed through a variadic function
  c = (char) va_arg(args, int);
  /*
    The filter_with_key function passes the internal key array to the filter
    function, so we must cast it to a string to compare characters.
  */
  return (((RBT_KEY_T) key_data->key)[0] != c);
}


/*
  Example of a RBT_NODE_WITH_PREFIX_SUBTREE_DO function.

  This function will receive a subtree of the tree passed to that function.
*/
void
count_subtree(
  RBT_NODE_T * node,
  va_list args
)
{
  const char * fmt;
  fmt = va_arg(args, const char *);
  printf(fmt, RBT_NODE_COUNT(node));
}


int
main(int argc, char * * argv)
{
  int i;
  char prefix[1];

  RBT_VALUE_T value;
  RBT_NODE_T * node, * copy, * new, * copy2;

  // Create the node.
  node = RBT_NODE_NEW();

  // Insert some values (countries mapped to capital cities).
  RBT_INSERT(node, "Georgia", "Tbilisi");
  RBT_INSERT(node, "Switzerland", "Bern");
  RBT_INSERT(node, "Estonia", "Tallinn");
  RBT_INSERT(node, "Liberia", "Monrovia");
  RBT_INSERT(node, "Togo", "Lome");
  RBT_INSERT(node, "Russia", "Moscow");
  RBT_INSERT(node, "Uganda", "Kampala");
  RBT_INSERT(node, "Greece", "Athens");
  RBT_INSERT(node, "Albania", "Tirane");
  RBT_INSERT(node, "Tanzania", "Dar es Salaam; Dodoma (legislative)");
  RBT_INSERT(node, "Belgium", "Brussels");
  RBT_INSERT(node, "Poland", "Warsaw");
  RBT_INSERT(node, "Grenada", "Saint George's");
  RBT_INSERT(node, "Vietnam", "Hanoi");
  RBT_INSERT(node, "Brazil", "Brasilia");
  RBT_INSERT(node, "Rwanda", "Kigali");
  RBT_INSERT(node, "Ecuador", "Quito");
  RBT_INSERT(node, "Algeria", "Algiers");
  RBT_INSERT(node, "Kiribati", "Tarawa Atoll");
  RBT_INSERT(node, "Finland", "Helsinki");
  RBT_INSERT(node, "Monaco", "Monaco");
  RBT_INSERT(node, "Nepal", "Kathmandu");
  RBT_INSERT(node, "Denmark", "Copenhagen");
  RBT_INSERT(node, "Kuwait", "Kuwait City");
  RBT_INSERT(node, "Argentina", "Buenos Aires");
  RBT_INSERT(node, "Nigeria", "Abuja");
  RBT_INSERT(node, "Montenegro", "Podgorica");
  RBT_INSERT(node, "Morocco", "Rabat");
  RBT_INSERT(node, "Somalia", "Mogadishu");
  RBT_INSERT(node, "Chile", "Santiago");
  RBT_INSERT(node, "Bosnia and Herzegovina", "Sarajevo");
  RBT_INSERT(node, "Hungary", "Budapest");
  RBT_INSERT(node, "The Gambia", "Banjul");
  RBT_INSERT(node, "Uruguay", "Montevideo");
  RBT_INSERT(node, "Chad", "N'Djamena");
  RBT_INSERT(node, "United Arab Emirates", "Abu Dhabi");
  RBT_INSERT(node, "Cape Verde", "Praia");
  RBT_INSERT(node, "Trinidad and Tobago", "Port-of-Spain");
  RBT_INSERT(node, "San Marino", "San Marino");
  RBT_INSERT(node, "Micronesia, Federated States of", "Palikir");
  RBT_INSERT(node, "Djibouti", "Djibouti");
  RBT_INSERT(node, "United States of America", "Washington D.C.");
  RBT_INSERT(node, "Congo, Democratic Republic of the", "Kinshasa");
  RBT_INSERT(node, "Italy", "Rome");
  RBT_INSERT(node, "Honduras", "Tegucigalpa");
  RBT_INSERT(node, "Costa Rica", "San Jose");
  RBT_INSERT(node, "Philippines", "Manila");
  RBT_INSERT(node, "Zimbabwe", "Harare");
  RBT_INSERT(node, "Venezuela", "Caracas");
  RBT_INSERT(node, "Qatar", "Doha");
  RBT_INSERT(node, "Turkey", "Ankara");
  RBT_INSERT(node, "Gabon", "Libreville");
  RBT_INSERT(node, "Spain", "Madrid");
  RBT_INSERT(node, "Luxembourg", "Luxembourg");
  RBT_INSERT(node, "Solomon Islands", "Honiara");
  RBT_INSERT(node, "Suriname", "Paramaribo");
  RBT_INSERT(node, "Korea, South", "Seoul");
  RBT_INSERT(node, "Mongolia", "Ulaanbaatar");
  RBT_INSERT(node, "Palau", "Melekeok");
  RBT_INSERT(node, "Croatia", "Zagreb");
  RBT_INSERT(node, "Mexico", "Mexico City");
  RBT_INSERT(node, "Bhutan", "Thimphu");
  RBT_INSERT(node, "Serbia", "Belgrade");
  RBT_INSERT(node, "Saudi Arabia", "Riyadh");
  RBT_INSERT(node, "Andorra", "Andorra la Vella");
  RBT_INSERT(node, "Cambodia", "Phnom Penh");
  RBT_INSERT(node, "Kosovo", "Pristina");
  RBT_INSERT(node, "Saint Lucia", "Castries");
  RBT_INSERT(node, "Romania", "Bucharest");
  RBT_INSERT(node, "Eritrea", "Asmara");
  RBT_INSERT(node, "El Salvador", "San Salvador");
  RBT_INSERT(node, "Slovenia", "Ljubljana");
  RBT_INSERT(node, "Comoros", "Moroni");
  RBT_INSERT(node, "Tunisia", "Tunis");
  RBT_INSERT(node, "Lesotho", "Maseru");
  RBT_INSERT(node, "Iceland", "Reykjavik");
  RBT_INSERT(node, "Liechtenstein", "Vaduz");
  RBT_INSERT(node, "Moldova", "Chisinau");
  RBT_INSERT(node, "United Kingdom", "London");
  RBT_INSERT(node, "Angola", "Luanda");
  RBT_INSERT(node, "Turkmenistan", "Ashgabat");
  RBT_INSERT(node, "Belize", "Belmopan");
  RBT_INSERT(node, "Korea, North", "Pyongyang");
  RBT_INSERT(node, "Nauru", "no official capital; government offices in Yaren District");
  RBT_INSERT(node, "Brunei", "Bandar Seri Begawan");
  RBT_INSERT(node, "France", "Paris");
  RBT_INSERT(node, "Iraq", "Baghdad");
  RBT_INSERT(node, "Burundi", "Bujumbura");
  RBT_INSERT(node, "Marshall Islands", "Majuro");
  RBT_INSERT(node, "Iran", "Tehran");
  RBT_INSERT(node, "Peru", "Lima");
  RBT_INSERT(node, "Ethiopia", "Addis Ababa");
  RBT_INSERT(node, "Haiti", "Port-au-Prince");
  RBT_INSERT(node, "Latvia", "Riga");
  RBT_INSERT(node, "Sao Tome and Principe", "Sao Tome");
  RBT_INSERT(node, "Bahrain", "Manama");
  RBT_INSERT(node, "Armenia", "Yerevan");
  RBT_INSERT(node, "Jordan", "Amman");
  RBT_INSERT(node, "Tajikistan", "Dushanbe");
  RBT_INSERT(node, "Samoa", "Apia");
  RBT_INSERT(node, "The Bahamas", "Nassau");
  RBT_INSERT(node, "Central African Republic", "Bangui");
  RBT_INSERT(node, "Cuba", "Havana");
  RBT_INSERT(node, "Barbados", "Bridgetown");
  RBT_INSERT(node, "Austria", "Vienna");
  RBT_INSERT(node, "Zambia", "Lusaka");
  RBT_INSERT(node, "Dominican Republic", "Santo Domingo");
  RBT_INSERT(node, "Malawi", "Lilongwe");
  RBT_INSERT(node, "Laos", "Vientiane");
  RBT_INSERT(node, "Malaysia", "Kuala Lumpur");
  RBT_INSERT(node, "Guatemala", "Guatemala City");
  RBT_INSERT(node, "Czech Republic", "Prague");
  RBT_INSERT(node, "Cote d'Ivoire", "Yamoussoukro (official); Abidjan (de facto)");
  RBT_INSERT(node, "Equatorial Guinea", "Malabo");
  RBT_INSERT(node, "Guinea-Bissau", "Bissau");
  RBT_INSERT(node, "Belarus", "Minsk");
  RBT_INSERT(node, "Sierra Leone", "Freetown");
  RBT_INSERT(node, "Senegal", "Dakar");
  RBT_INSERT(node, "Australia", "Canberra");
  RBT_INSERT(node, "Singapore", "Singapore");
  RBT_INSERT(node, "Norway", "Oslo");
  RBT_INSERT(node, "East Timor (Timor-Leste)", "Dili");
  RBT_INSERT(node, "Bolivia", "La Paz (administrative); Sucre (judicial)");
  RBT_INSERT(node, "Malta", "Valletta");
  RBT_INSERT(node, "Kazakhstan", "Astana");
  RBT_INSERT(node, "Cameroon", "Yaounde");
  RBT_INSERT(node, "Slovakia", "Bratislava");
  RBT_INSERT(node, "Japan", "Tokyo");
  RBT_INSERT(node, "Fiji", "Suva");
  RBT_INSERT(node, "Indonesia", "Jakarta");
  RBT_INSERT(node, "Saint Kitts and Nevis", "Basseterre");
  RBT_INSERT(node, "Antigua and Barbuda", "Saint John's");
  RBT_INSERT(node, "Guyana", "Georgetown");
  RBT_INSERT(node, "Ireland", "Dublin");
  RBT_INSERT(node, "Dominica", "Roseau");
  RBT_INSERT(node, "Netherlands", "Amsterdam; The Hague (seat of government)");
  RBT_INSERT(node, "Nicaragua", "Managua");
  RBT_INSERT(node, "Kyrgyzstan", "Bishkek");
  RBT_INSERT(node, "Libya", "Tripoli");
  RBT_INSERT(node, "Saint Vincent and the Grenadines", "Kingstown");
  RBT_INSERT(node, "Niger", "Niamey");
  RBT_INSERT(node, "Myanmar (Burma)", "Rangoon (Yangon); Naypyidaw or Nay Pyi Taw (administrative)");
  RBT_INSERT(node, "Botswana", "Gaborone");
  RBT_INSERT(node, "Maldives", "Male");
  RBT_INSERT(node, "Oman", "Muscat");
  RBT_INSERT(node, "Macedonia", "Skopje");
  RBT_INSERT(node, "Vanuatu", "Port-Vila");
  RBT_INSERT(node, "Bangladesh", "Dhaka");
  RBT_INSERT(node, "Panama", "Panama City");
  RBT_INSERT(node, "Tonga", "Nuku'alofa");
  RBT_INSERT(node, "Bulgaria", "Sofia");
  RBT_INSERT(node, "Mauritania", "Nouakchott");
  RBT_INSERT(node, "Lithuania", "Vilnius");
  RBT_INSERT(node, "Jamaica", "Kingston");
  RBT_INSERT(node, "Paraguay", "Asuncion");
  RBT_INSERT(node, "Israel", "Jerusalem*");
  RBT_INSERT(node, "Mauritius", "Port Louis");
  RBT_INSERT(node, "Ukraine", "Kyiv");
  RBT_INSERT(node, "New Zealand", "Wellington");
  RBT_INSERT(node, "Germany", "Berlin");
  RBT_INSERT(node, "China", "Beijing");
  RBT_INSERT(node, "Papua New Guinea", "Port Moresby");
  RBT_INSERT(node, "Mozambique", "Maputo");
  RBT_INSERT(node, "Colombia", "Bogota");
  RBT_INSERT(node, "Syria", "Damascus");
  RBT_INSERT(node, "Burkina Faso", "Ouagadougou");
  RBT_INSERT(node, "Pakistan", "Islamabad");
  RBT_INSERT(node, "Madagascar", "Antananarivo");
  RBT_INSERT(node, "Mali", "Bamako");
  RBT_INSERT(node, "Kenya", "Nairobi");
  RBT_INSERT(node, "Yemen", "Sanaa");
  RBT_INSERT(node, "Benin", "Porto-Novo");
  RBT_INSERT(node, "Lebanon", "Beirut");
  RBT_INSERT(node, "Sudan", "Khartoum");
  RBT_INSERT(node, "Taiwan", "Taipei");
  RBT_INSERT(node, "Namibia", "Windhoek");
  RBT_INSERT(node, "Cyprus", "Nicosia");
  RBT_INSERT(node, "Canada", "Ottawa");
  RBT_INSERT(node, "Ghana", "Accra");
  RBT_INSERT(node, "Portugal", "Lisbon");
  RBT_INSERT(node, "Afghanistan", "Kabul");
  RBT_INSERT(node, "Vatican City (Holy See)", "Vatican City");
  RBT_INSERT(node, "Swaziland", "Mbabane");
  RBT_INSERT(node, "Seychelles", "Victoria");
  RBT_INSERT(node, "South Sudan", "Juba (Relocating to Ramciel)");
  RBT_INSERT(node, "Sri Lanka", "Colombo; Sri Jayewardenepura Kotte (legislative)");
  RBT_INSERT(node, "Sweden", "Stockholm");
  RBT_INSERT(node, "Tuvalu", "Vaiaku village, Funafuti province");
  RBT_INSERT(node, "South Africa", "Pretoria (administrative); Cape Town (legislative); Bloemfontein (judiciary)");
  RBT_INSERT(node, "Thailand", "Bangkok");
  RBT_INSERT(node, "Congo, Republic of the", "Brazzaville");
  RBT_INSERT(node, "Egypt", "Cairo");
  RBT_INSERT(node, "Azerbaijan", "Baku");
  RBT_INSERT(node, "Uzbekistan", "Tashkent");
  RBT_INSERT(node, "Guinea", "Conakry");
  RBT_INSERT(node, "India", "New Delhi");

  // Print a representation of the tree.
  printf("Representation of the tree:\n");
  RBT_NODE_FPRINT(stdout, node, 0, 0);


  // Traverse all key-value pairs.
  printf("\nTraversal of all key-value pairs in the tree:\n");
  RBT_NODE_TRAVERSE_WITH_KEY(node, print_key_and_value_pairs);

  // Count the number of nodes.
  printf("\nCounting the number of insertions: %lu\n", RBT_NODE_COUNT(node));

  // Make a copy of the tree and filter by *capitals* beginning with 'T', i.e.
  // by *value*.
  copy = RBT_NODE_COPY(node);
  printf("\nCreating a copy of the tree and filtering for capitals beginning with '%c'.\n", FILTER_CHAR);
  RBT_NODE_FILTER(copy, filter_by_value_first_char, 0, FILTER_CHAR);

  // Print the filtered tree and its key-value pairs.
  RBT_NODE_FPRINT(stdout, copy, 0, 0);
  printf("\n");
  RBT_NODE_TRAVERSE_WITH_KEY(copy, print_key_and_value_pairs);

  // Count the number of nodes in the filtered tree.
  printf("\nCounting the number of insertions: %lu\n", RBT_NODE_COUNT(copy));


  // Create a new tree and selectively insert countries beginning with 'T'.
  new = RBT_NODE_NEW();
  printf("\nCreating new tree for capitals beginning with '%c' using a traversal function.\nThe results should be the same as the filtered copy above.\n", FILTER_CHAR);
  RBT_NODE_TRAVERSE_WITH_KEY(node, insert_by_value_first_char, new, FILTER_CHAR);

  // Print this new tree and its key-value pairs.
  RBT_NODE_FPRINT(stdout, new, 0, 0);
  printf("\n");
  RBT_NODE_TRAVERSE_WITH_KEY(new, print_key_and_value_pairs);

  // Count the number of insertions.
  printf("\nCounting the number of insertions: %lu\n", RBT_NODE_COUNT(new));


  // Compare the last two trees, which should be the same.
  printf("\nDoing a node by node comparison of the two previous trees: ");
  if (RBT_NODE_IS_COPY(copy, new))
  {
    printf("they are equivalent.\n");
  }
  else
  {
    printf("they are not equivalent. [error]\n");
  }


  // Show the actual bits stored in the node keys.
  printf("\nA representation of the previous tree showing key bits:\n");
  RBT_NODE_FPRINT(stdout, copy, 1, 0);


  // Look up the capitals of countries entered on the command line.
  if (argc > 1)
  {
    printf("\nLooking up requested capitals.\n");
  }
  for (i = 1; i < argc; i++)
  {
    value = string_to_const_string_retrieve(node, argv[i]);
    if (RBT_VALUE_IS_NULL(value))
    {
      printf("Unrecognized country: %s\n", argv[i]);
    }
    else
    {
      printf("The capital of %s is %s\n", argv[i], value);
    }
  }

  // Make another copy and filter countries beginning with 'T', i.e. filter by
  // key.
  copy2 = RBT_NODE_COPY(node);
  printf("\nCreating another copy of the tree and filtering for countries beginning with '%c'.\n", FILTER_CHAR);
  RBT_NODE_FILTER_WITH_KEY(copy2, filter_key_by_first_char, 0, FILTER_CHAR);
  RBT_NODE_TRAVERSE_WITH_KEY(copy2, print_key_and_value_pairs);

  printf("\nCounting the number of countries that begin with '%c': %lu\n", FILTER_CHAR, RBT_NODE_COUNT(copy2));



  /*
    Do essentially the same thing using a prefix-defined subtree. This will find
    the subtree of all keys that begin with 'T' and copy it to a separate tree.

    The first argument is the tree.

    The second is the prefix to find (the filter char, passed as a string).

    The third is the number of *bits* in the key to match.

    The fourth is the function to call with this subtree. Here will will count
    the number of entries in the subtree, i.e the number of countries beginning
    with 'T'.

    Arguments after the fourth are additional arguments to pass to that function
    along with the subtree.

    This could be used to copy the the subtree to another tree as well.
  */
  prefix[0] = FILTER_CHAR;
  if (
    ! RBT_NODE_WITH_PREFIX_SUBTREE_DO(
        node,
        (RBT_PIN_T *) prefix, BITS_PER_BYTE,
        count_subtree, "\nCounting again using a prefix filter: %u\n"
      )
  )
  {
    printf("\nPrefix filter failed\n");
  }



  /*
    Print trees for each character filter. This was used to select the
    narrowest tree representation.
  char c;
  for (c = 'A'; c <= 'Z'; c ++)
  {
    copy = RBT_NODE_COPY(node);
    RBT_NODE_FILTER(copy, filter_by_value_first_char, 0, c);
    printf("\nchar: %c\n", c);
    RBT_NODE_FPRINT(stdout, copy, 1, 0);
  }
  */

  /*
    Free everything.
  */
  RBT_NODE_FREE(node);
  RBT_NODE_FREE(copy);
  RBT_NODE_FREE(copy2);
  RBT_NODE_FREE(new);
  printf("done\n");
  return EXIT_SUCCESS;
}
