//
//  C++ Views and Pipes
//  by John Ryland
//  (c) Copyright 2019
//


///////////
// BRIEF //
///////////

/*

  Requires C++14, but would not be impossible to support C++11 with effort.

  Compile with:

      g++ --std=c++14 pipe-tests.cpp -o pipe-tests

  Examples of what it can do:

      to_array({"one","two","three","four","five"}) | reverse | filter([](std::string a){ return a == "two"; }) | print;
    
    This is C++ code, and it takes the list of strings and turns them in to a std::array, then pipes it through
    a reverse function which causes the iteration to happen in reverse, then pipes this through a filter which
    is defined with an inline lambda which removes any items which equal "two", and finally it prints out the result.
      
    The lambdas don't need to be inline, it can be nicer to define a function like this:

      auto odd = [](int x) -> bool { return (x & 1) == 1; };

    Then it can be used like this:
        
      std::vector<int> v = {1,2,3,4,5};
      container_view(v) | match(odd) | print;

    This takes as input a std::vector of items, and wraps it in a view which is piped to a matching function which
    will only pass through items where the lambda returns true for the item. Internally the vector is not copied, simply
    the iteration is what is varied. Next it prints out the result.

    More complicated examples can be made:

      range(1,101) | reverse | filter(odd) | first(5) | print;

    This one uses the range function which generates items. At no point is an actual list or vector of numbers made in
    memory. However it will print the last 5 odd numbers working backwards from 101 as might be expected from the commands.

*/


//////////////
// INCLUDES //
//////////////

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <sstream>
#include <array>
#include <regex>


/////////////////
// TYPE TRAITS //
/////////////////

// Container Traits ///////////////////////////////////////////////////////////

template <typename T, typename _ = void>
struct has_container_traits : std::false_type {};


template <typename... Ts>
struct has_container_traits_helper {};


template <typename T>
struct has_container_traits<T, std::conditional_t<false, has_container_traits_helper<
       // Const Container Trails:
       typename T::value_type,
       typename T::const_iterator,
       typename T::const_reverse_iterator,
       decltype(std::declval<T>().crbegin()),
       decltype(std::declval<T>().crend()),
       decltype(std::declval<T>().cbegin()),
       decltype(std::declval<T>().cend())
>, void>> : public std::true_type {};


template <typename Container,
          typename std::enable_if<has_container_traits<Container>{}, bool>::type = true>
struct is_container
{
  using type = bool;
};


template <typename Container>
using is_container_t = typename is_container<Container>::type;


// View Traits ////////////////////////////////////////////////////////////////

template <typename T, typename _ = void>
struct has_view_traits : std::false_type {};


template <typename... Ts>
struct has_view_traits_helper {};


template <typename T>
struct has_view_traits<T, std::conditional_t<false, has_view_traits_helper<
       // View Traits:
       typename T::element_type,
       decltype(std::declval<T>().first()),
       decltype(std::declval<T>().last()),
       decltype(std::declval<T>().next()),
       decltype(std::declval<T>().previous()),
       decltype(std::declval<T>().current())
>, void>> : public std::true_type {};


template <typename View,
          typename std::enable_if<has_view_traits<View>{}, bool>::type = true>
struct is_view
{
  using type = bool;
};


template <typename View>
using is_view_t = typename is_view<View>::type;


////////////////
// VIEW TYPES //
////////////////

// Container View /////////////////////////////////////////////////////////////

template <typename Container, typename BaseType, is_container_t<BaseType> = true>
class container_wrapper_view
{
public:
  container_wrapper_view(Container a_container)
    : m_container(a_container)
  {
  }

  // implements View
  using element_type = typename BaseType::value_type;
  bool first()         { m_it = m_container.cbegin();          return m_it != m_container.cend(); }
  bool last()          { m_it = std::prev(m_container.cend()); return m_it != std::prev(m_container.cbegin()); }
  bool next()          { m_it = std::next(m_it);               return m_it != m_container.cend(); }
  bool previous()      { m_it = std::prev(m_it);               return m_it != std::prev(m_container.cbegin()); }
  const element_type& current() { return *m_it; }
private:
  Container                                 m_container;
  typename BaseType::const_iterator         m_it;
};


// Range View /////////////////////////////////////////////////////////////////

template <typename T>
class range_view
{
public:
  range_view(T start, T end)
    : m_start(start)
    , m_end(end)
  {
  }

  // implements View
  using element_type = T;
  bool first()         { m_current = m_start; return m_start <= m_end; }
  bool last()          { m_current = m_end;   return m_start <= m_end; }
  bool next()          { ++m_current; return m_current <= m_end;       }
  bool previous()      { --m_current; return m_current >= m_start;     }
  const T& current()   { return m_current; }
private:
  T m_current;
  T m_start;
  T m_end;
};


// Match View ////////////////////////////////////////////////////////////////

template <typename ParentView, typename Functor, is_view_t<ParentView> = true>
class match_view
{
public:
  using element_type = typename ParentView::element_type;
  match_view(const ParentView& a_parentView, const Functor& a_match)
    : m_parentView(a_parentView)
    , m_match(a_match)
  {
  }

  // implements View
  bool first()                  { if (!m_parentView.first())    return false; return nextValid();     }
  bool last()                   { if (!m_parentView.last())     return false; return previousValid(); }
  bool next()                   { if (!m_parentView.next())     return false; return nextValid();     }
  bool previous()               { if (!m_parentView.previous()) return false; return previousValid(); }
  const element_type& current() { return m_parentView.current(); }
private:
  bool nextValid()              { while (!m_match(m_parentView.current())) { if (!m_parentView.next())     return false; } return true; }
  bool previousValid()          { while (!m_match(m_parentView.current())) { if (!m_parentView.previous()) return false; } return true; }
  ParentView    m_parentView;
  Functor       m_match;
};


// Filter View ////////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class filter_view : public match_view<ParentView, std::function<bool(typename ParentView::element_type)>>
{
public:
  // inherits View
  using element_type = typename ParentView::element_type;
  using function_type = std::function<bool(element_type)>;

  filter_view(const ParentView& a_parentView, const function_type& a_filter)
    : match_view<ParentView, function_type>(a_parentView, [a_filter](element_type a_item) { return !a_filter(a_item); })
  {
  }
};


// Transform View /////////////////////////////////////////////////////////////

template <typename ParentView, typename Functor, typename ReturnType, is_view_t<ParentView> = true>
class transform_view
{
public:
  using parent_element_type = typename ParentView::element_type;
  transform_view(const ParentView& a_parentView, const Functor& a_transform)
    : m_parentView(a_parentView)
    , m_transform(a_transform)
  {
  }

  // implements View
  using element_type = ReturnType;
  bool first()                  { return m_parentView.first(); }
  bool last()                   { return m_parentView.last(); }
  bool next()                   { return m_parentView.next(); }
  bool previous()               { return m_parentView.previous(); }
  element_type current()        { return m_transform(m_parentView.current()); }   // NOTE: Transformed elements are returned by value
private:
  ParentView       m_parentView;
  Functor          m_transform;
};


// Reverse View ///////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class reverse_view
{
public:
  reverse_view(const ParentView& view)
    : m_parent(view)
  {
  }

  // implements View
  using element_type = typename ParentView::element_type;
  bool first()                  { return m_parent.last(); }
  bool last()                   { return m_parent.first(); }
  bool next()                   { return m_parent.previous(); }
  bool previous()               { return m_parent.next(); }
  const element_type& current() { return m_parent.current(); }
private:
  element_type m_current;
  ParentView   m_parent;
};


// Skip View //////////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class skip_view
{
public:
  using element_type = typename ParentView::element_type;
  skip_view(const ParentView& a_parentView, size_t a_count)
    : m_parentView(a_parentView)
    , m_count(a_count)
  {
  }

  // implements View
  bool first()                  { if (!m_parentView.first()) return false; return nextValid();     }
  bool last()                   { if (!m_parentView.last())  return false; return previousValid(); }
  bool next()                   { return m_parentView.next();     }
  bool previous()               { return m_parentView.previous(); }
  const element_type& current() { return m_parentView.current();  }
private:
  bool nextValid()              { while (m_count >= 1) { if (!m_parentView.next())     return false; m_count--; } return true; }
  bool previousValid()          { while (m_count >= 1) { if (!m_parentView.previous()) return false; m_count--; } return true; }
  ParentView      m_parentView;
  size_t          m_count;
};


// Take View //////////////////////////////////////////////////////////////////

template <typename ParentView, is_view_t<ParentView> = true>
class take_view
{
public:
  using element_type = typename ParentView::element_type;
  take_view(const ParentView& a_parentView, size_t a_count)
    : m_parentView(a_parentView)
    , m_count(a_count)
  {
  }

  // implements View
  bool first()                  { return m_parentView.first(); }
  bool last()                   {
                                  size_t count = m_count - 1;
                                  if (count <= 0 || !m_parentView.first()) return false;
                                  while (count)
                                  {
                                    if (!m_parentView.next()) return false;
                                    count--;
                                  }
                                  return true;
                                } 
  bool next()                   { m_count--; return (m_count > 0) ? m_parentView.next()     : false; }
  bool previous()               { m_count--; return (m_count > 0) ? m_parentView.previous() : false; }
  const element_type& current() { return m_parentView.current();  }
private:
  ParentView      m_parentView;
  size_t          m_count;
};


//////////////
// WRAPPERS //
//////////////

// Container Wrapper //////////////////////////////////////////////////////////

// creates a const reference container view
template <typename Container, is_container_t<Container> = true>
auto container_view(const Container& a_container)
{
  return container_wrapper_view<const Container&, Container>(a_container);
}


// creates a container view by value
template <typename Container, is_container_t<Container> = true>
auto container_copy_view(const Container& a_container)
{
  return container_wrapper_view<Container, Container>(a_container);
}


// Range Wrapper //////////////////////////////////////////////////////////////

template <typename T>
auto range(T start, T end)
{
  return range_view<T>(start, end);
}


// Array Wrapper //////////////////////////////////////////////////////////////

template <class T, std::size_t N, std::size_t... I>
constexpr auto to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
  return std::array<std::remove_cv_t<T>, N>{ { a[I]... } };
}

  
template <class T, std::size_t N, std::size_t... I>
constexpr auto to_array_impl(T (&&a)[N], std::index_sequence<I...>)
{
  return std::array<std::remove_cv_t<T>, N>{ { std::move(a[I])... } };
}
 
 
template <class T, std::size_t N>
constexpr auto to_array(T (&a)[N])
{
  return to_array_impl(a, std::make_index_sequence<N>{});
}


template <class T, std::size_t N>
constexpr auto to_array(T (&&a)[N])
{
  return to_array_impl(std::move(a), std::make_index_sequence<N>{});
}


// Print Wrapper /////////////////////////////////////////////////////////////

class print_class
{
  // No state, just the class as a tag
};


// Reverse Wrapper ////////////////////////////////////////////////////////////

class reverse_class
{
  // No state, just the class as a tag
};


// Match Wrapper //////////////////////////////////////////////////////////////

template <typename Functor>
class match_class
{
public:
  match_class(Functor&& a_m)
    : m_m(a_m)
  {
  }
  Functor m_m;
};


// Filter Wrapper /////////////////////////////////////////////////////////////

template <typename Functor>
class filter_class
{
public:
  filter_class(Functor&& a_f)
    : m_f(a_f)
  {
  }
  Functor m_f;
};


// Transform Wrapper //////////////////////////////////////////////////////////

template <typename Functor>
class transform_class
{
public:
  transform_class(Functor&& t)
    : m_t(t)
  {
  }
  Functor m_t;
};


//////////////
// COMMANDS //
//////////////

// Print Command //////////////////////////////////////////////////////////////

print_class print;


// Reverse Command ////////////////////////////////////////////////////////////

reverse_class reverse;


// Match Command //////////////////////////////////////////////////////////////

template <typename Functor>
auto match(Functor&& a_m)
{
  return match_class<Functor>(std::forward<Functor>(a_m));
}


// Grep Command ///////////////////////////////////////////////////////////////

// Match a regular expression
auto grep(const std::string& a_re)
{
  std::regex re(a_re);
  auto f = [re](const std::string& a_s) { return std::regex_search(a_s, re); };
  return match_class<decltype(f)>(std::move(f));
}


// Filter Command /////////////////////////////////////////////////////////////

template <typename Functor>
auto filter(Functor&& a_f)
{
  return filter_class<Functor>(std::forward<Functor>(a_f));
}


// Take Command ///////////////////////////////////////////////////////////////

class take
{
public:
  take(size_t a_count)
    : m_count(a_count)
  {
  }
  size_t m_count;
};


// Head Command ///////////////////////////////////////////////////////////////

using head = take;


// First Command //////////////////////////////////////////////////////////////

using first = take;


// Last Command ///////////////////////////////////////////////////////////////

class last
{
public:
  last(size_t a_count)
    : m_count(a_count)
  {
  }
  size_t m_count;
};


// Tail Command ///////////////////////////////////////////////////////////////

using tail = last;


// Skip Command ///////////////////////////////////////////////////////////////

class skip
{
public:
  skip(size_t a_count)
    : m_count(a_count)
  {
  }
  size_t m_count;
};


// Drop Command ///////////////////////////////////////////////////////////////

using drop = skip;


// Transform Command //////////////////////////////////////////////////////////

template <typename Functor>
auto transform(Functor&& a_f)
{
  return transform_class<Functor>(std::forward<Functor>(a_f));
}


// Convert Command ////////////////////////////////////////////////////////////

template <typename Functor>
auto convert(Functor&& a_f)
{
  return transform_class<Functor>(std::forward<Functor>(a_f));
}


// Join Command ///////////////////////////////////////////////////////////////

class join
{
public:
  join(const std::string& s)
    : m_s(s)
  {
  }
  std::string m_s;
};


// Split Command //////////////////////////////////////////////////////////////

class split
{
public:
  split(const std::string& a_delimiters)
    : m_delimiters(a_delimiters)
  {
  }
  std::string m_delimiters;
};


///////////
// PIPES //
///////////

// Container Pipe /////////////////////////////////////////////////////////////
  
template <typename Container, typename View, is_container_t<Container> = true>
auto operator|(const Container& c, const View& v)
{
  return container_view(c) | v;
}


// Array Pipe /////////////////////////////////////////////////////////////////

template <typename ArrayT, std::size_t ArrayN, typename View>
auto operator|(const ArrayT (&a)[ArrayN], const View& v)
{
  auto c = to_array(a);
  return container_view(c) | v;
}


// Reverse Pipe ///////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const reverse_class& r)
{
  return reverse_view<View>(v1);
}


// Match Pipe ////////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const match_class<Functor>& m)
{
  return match_view<View, Functor>(v1, m.m_m);
}


// Filter Pipe ////////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const filter_class<Functor>& f)
{
  return filter_view<View>(v1, f.m_f);
}


// Take Pipe //////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const take& t)
{
  return take_view<View>(v1, t.m_count);
}


// Last Pipe //////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const last& l)
{
  return v1 | reverse | take(l.m_count) | reverse;
}


// Skip Pipe //////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const skip& s)
{
  return skip_view<View>(v1, s.m_count);
}


// Transform Pipe /////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const transform_class<Functor>& t)
{
  return transform_view<View, Functor, decltype(t.m_t(typename View::element_type()))>(v1, t.m_t);
}


// Join Pipe //////////////////////////////////////////////////////////////////

std::string operator|(const std::string& s, const join& j)
{
  return s;
}


std::string operator|(const char* s, const join& j)
{
  return s;
}


template <typename View, is_view_t<View> = true>
std::string operator|(const View& c, const join& j)
{
  std::stringstream ss;
  std::string deliminator = j.m_s;
  apply(c, [&ss, deliminator](const auto& a, bool f, bool l) { ss << a; if (!l) ss << deliminator; } );
  return ss.str();
}


// Split Pipe /////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const split& s)
{
  // https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
  std::string str = v1 | join("");
  std::string delimiters = s.m_delimiters;
  std::vector<std::string> tokens;
  std::size_t start = 0, end, length = str.length();
  while (length && start < length + 1)
  {
    end = str.find_first_of(delimiters, start);
    if (end == std::string::npos)
    {
      end = length;
    }
    std::string token = std::string(str.data() + start, end - start);
    tokens.push_back(token);
    start = end + 1;
  }

  // Unfortunately we need to make a copy here as 'tokens' is on the stack. Possibly this
  // could be improved if have a string_spans implementation as a 'view' in to the original data.
  return container_copy_view<std::vector<std::string>>(tokens);
}


// Print Pipe /////////////////////////////////////////////////////////////////

template <typename View, is_view_t<View> = true>
void operator|(const View& v1, const print_class& r)
{
  apply(v1, [](const auto& a, bool f, bool l) { std::cout << a; if (!l) std::cout << ","; } );
  std::cout << std::endl;
}


void operator|(const std::string& s, const print_class& r)
{
  std::cout << s << std::endl;
}


// Apply Pipe /////////////////////////////////////////////////////////////////

template <typename View, typename Functor, is_view_t<View> = true>
void for_each(const View& a_iterable, Functor&& f)
{
  View it = a_iterable; // takes a copy of the view, views should be cheap to copy
  if (it.first())
  {
    do
    {
      f(it.current());
    } while (it.next());
  }
}


template <typename View, typename Functor, is_view_t<View> = true>
void apply(const View& a_iterable, Functor&& f)
{
  bool first = true;
  bool last = false;
  View it = a_iterable; // takes a copy of the view, views should be cheap to copy
  if (it.first())
  {
    do
    {
      auto current = it.current();
      last = !it.next();
      f(current, first, last);
      first = false;
    } while (!last);
  }
}


template <typename View, is_view_t<View> = true>
void operator|(const View& v, const std::function<void(typename View::element_type)>& f)
{
  for_each(v, f);
}


template <typename Container, is_container_t<Container> = true>
void operator|(const Container& c, const std::function<void(typename Container::value_type)>& f)
{
  std::for_each(c.begin(), c.end(), f);
}


///////////
// DEMOS //
///////////

void demo1()
{
  std::cout << "Demo" << std::endl;

  auto strings = to_array({"one","two","three","four","five"});

  std::cout << "Printing strings" << std::endl;
  strings | print;

  std::cout << "Reversing strings" << std::endl;
  strings | reverse | print;

  std::cout << "Matching strings with 'o'" << std::endl;
  strings | match([](std::string a){ return a.find("o") != std::string::npos; }) | print;

  std::cout << "Grepping strings for 'one|two|four'" << std::endl;
  strings | grep("one|two|four") | print;

  std::cout << "Filtering strings without 'o'" << std::endl;
  strings | filter([](std::string a){ return a.find("o") == std::string::npos; }) | print;

  std::cout << "Taking 3 strings" << std::endl;
  strings | take(3) | print;
  
  std::cout << "Head 3 strings" << std::endl;
  strings | head(3) | print;

  std::cout << "First 3 strings" << std::endl;
  strings | first(3) | print;

  std::cout << "Last 3 strings" << std::endl;
  strings | last(3) | print;

  std::cout << "Tail 3 strings" << std::endl;
  strings | tail(3) | print;

  std::cout << "Skipping 2 strings" << std::endl;
  strings | skip(2) | print;

  std::cout << "Dropping 2 strings" << std::endl;
  strings | drop(2) | print;

  std::cout << "Transforming strings to lengths" << std::endl;
  strings | transform([](std::string a) { return a.length(); }) | print;

  std::cout << "Converting strings to lengths" << std::endl;
  strings | convert([](std::string a) { return a.size(); }) | print;

  std::cout << "Joining strings with '.'" << std::endl;
  strings | join(".") | print;

  std::cout << "Splitting strings with '.'" << std::endl;
  strings | join(".") | split(".") | print;
}


void demo2()
{
  // Functions
  auto printer = [](auto x) { std::cout << x << ","; };
  auto newline = []() { std::cout << std::endl; };

  // Input
  std::vector<int> v = {1,2,3,4,5};

  // Normal way
  for (auto i : v)
    printer(i);
  std::cout << std::endl;

  // C++11 functional way
  std::for_each(v.begin(), v.end(), printer);
  std::cout << std::endl;

  // Improved way
  v | printer;
  newline();

  // Best way
  v | print;

  // More interesting options 
  v | reverse | print;
}


//////////
// MAIN //
//////////

int main()
{
  demo1();
  demo2();


  std::stringstream ss;
  auto printer = [](auto x) { std::cout << x << ","; };
  auto newline = []() { std::cout << std::endl; };
  auto odd = [](int x) -> bool { return (x & 1) == 1; };
  auto as_string = [](auto x) -> std::string { return std::to_string(x); };

  
  int v2[] = {1,2,3,4,5};
  v2 | print;

  int v3[]{1,2,3,4,5};
  v3 | print;
  
  auto v4 = to_array<int>({1,2,3,4,5});
  v4 | print;
  
  to_array({1,2,3,4,5}) | print;

  std::string("13,15,66,42,23") | split(",") | join(".") | print;
  "13,15,66,42,23" | split(",") | join(".") | print;
  "13,15,66,42,23" | split(",") | grep("3") | join(".") | print;

  auto sl = "13,15,66,42,23,123" | split(",");
  auto s = sl | join(".");
  std::cout << s << std::endl;
  newline();

  range(10,15) | print;
  
  std::cout << (range(10,15) | transform(as_string) | join(",")) << std::endl;
 

  for_each(range(10,15), printer);
  newline();
  for_each(reverse_view<range_view<int>>(range(10,15)), printer);
  newline();
  for_each(range(10,14) | reverse | filter(odd), printer);
  newline();

  
  std::vector<int> v = {1,2,3,4,5};
  container_view(v) | print;
  container_view(v) | match(odd) | print;
  container_view(v) | filter(odd) | print;
  container_view(v) | reverse | print;
  container_view(v) | reverse | reverse | print;
  container_view(v) | reverse | reverse | filter(odd) | reverse | reverse | print;
  container_view(v) | reverse | filter(odd) | reverse | print;
  container_view(v) | filter(odd) | reverse | print;
  container_view(v) | reverse | filter(odd) | print;

  
  range(10,14) | reverse | filter(odd) | print;
  range(10,15) | reverse | print;
  range(10,15) | reverse | reverse | print;
  range(10,15) | filter(odd) | print;
  range(10,15) | reverse | filter(odd) | print;
  range(10,15) | reverse | filter(odd) | reverse | print;
  range(10,14) | reverse | filter(odd) | print;
  range(1,101) | reverse | filter(odd) | take(5) | print;
  range(1,101) | reverse | filter(odd) | head(5) | print;
  range(1,101) | reverse | filter(odd) | first(5) | print;
  range(1,101) | reverse | first(5) | reverse | print;
  range(1,101) | last(5) | print;
  range(1,101) | tail(5) | print;
  range(1,6) | print;
  range(1,6) | skip(3) | print;

  auto filt = [](std::string a){ return a == "BAD"; };

  to_array({"five","BAD","four","three","BAD","two","one"}) | reverse | filter(filt) | print;
  to_array({"one","two","three","four","five"}) | reverse | filter([](std::string a){ return a == "two"; }) | print;
}


