// Example 8-1: Case-insenstive character traits
#ifndef CISTRING_H
#define CISTRING_H

#include <algorithm>
#include <cctype>
#include <cstddef>
#include <cstring>
#include <iostream>
#include <istream>
#include <map>
#include <ostream>
#include <string>
#include <utility>

// ci_char_traits has the same interface as char_traits<>, but
// compare() ignores case differences.
template<typename T> struct ci_char_traits {};

template<> struct ci_char_traits<char> {
  typedef char char_type;
  typedef int int_type;
  typedef std::streamoff off_type;
  typedef std::streampos pos_type;
  typedef std::mbstate_t state_type;

  static void assign(char_type& dst, const char_type src) {
    dst = src;
  }
  static char_type* assign(char* dst, std::size_t n, char c) {
    return static_cast<char_type*>(std::memset(dst, n, c));
  }
  static bool eq(const char_type& c1, const char_type& c2) {
    return std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2));
  }
  static bool lt(const char_type& c1, const char_type& c2) {
    return std::tolower(static_cast<unsigned char>(c1)) < std::tolower(static_cast<unsigned char>(c2));
  }
  static std::size_t length(const char_type* str) {
    return std::strlen(str);
  }
  static int compare(const char_type* s1, const char_type* s2, std::size_t n) {
    for (std::size_t i = 0; i < n; ++i)
    {
      char_type lc1 = std::tolower(static_cast<unsigned char>(s1[i]));
      char_type lc2 = std::tolower(static_cast<unsigned char>(s2[i]));
      if (lc1 < lc2)
        return -1;
      else if (lc1 > lc2)
        return 1;
    }
    return 0;
  }
  static const char_type* find(const char_type* str, std::size_t n, const char_type& c) {
    char_type lc = std::tolower(c);
    for (const char_type* p = str; p != str + n; ++p)
      if (std::tolower(*p) == c)
        return p;
    return NULL;
  }
  static char_type* copy(char_type* dst, const char_type* src, std::size_t n) {
    return static_cast<char_type*>(std::memcpy(dst, src, n));
  }
  static char_type* move(char_type* dst, const char_type* src, std::size_t n) {
    return static_cast<char_type*>(std::memmove(dst, src, n));
  }
  static bool eq_int_type(const int_type& i1, const int_type& i2) {
    return std::tolower(i1) == std::tolower(i2);
  }
  static int_type eof() {
    return EOF;
  }
  static int_type not_eof(const int_type& i) {
    if (i == EOF)
      return 0; // EOF is negative, so 0 != EOF
    else
      return i;
  }
  static char_type to_char_type(const int_type& i) {
    return static_cast<char_type>(i);
  }
  static int_type to_int_type(const char_type& c) {
    return static_cast<unsigned char>(c);
  }
};

typedef std::basic_string<char, ci_char_traits<char> > ci_string;

template<typename charT>
std::basic_istream<charT>&
operator>>(std::basic_istream<charT>& in,
           std::basic_string<charT, ci_char_traits<charT> >& ci_str)
{
  std::basic_string<charT> str;
  in >> str;
  ci_str.assign(str.data(), str.size());
  return in;
}

template<typename charT>
std::basic_ostream<charT>&
operator<<(std::basic_ostream<charT>& out,
           const std::basic_string<charT, ci_char_traits<charT> >& ci_str)
{
  std::basic_string<charT> str(ci_str.data(), ci_str.size());
  out << str;
  return out;
}

#endif // CISTRING_H
