#pragma once

#include "cell.h"

#include <vector>

#include <boost/tokenizer.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/spirit/include/support_line_pos_iterator.hpp>

namespace qi      = boost::spirit::qi;
namespace phoenix = boost::phoenix;

typedef boost::spirit::line_pos_iterator<std::string::const_iterator> pos_iterator_t;

typedef std::vector<cell> row;
typedef std::vector<cell> cells;

BOOST_FUSION_ADAPT_STRUCT(
    cell,
    (Type, type)
    (Color, color)
)

template<typename It>
struct annotation_f {
    typedef void result_type;

    annotation_f(It first) : first(first) {}
    It const first;

    template<typename Val, typename First, typename Last>
    void operator()(Val& v, First f, Last l) const {
        do_annotate(v, f, l, first);
    }

  private:
    void static do_annotate(cell& c, It f, It l, It first) {
        c.row    = get_line(f) - 1;
        c.column = get_column(first, f) / 2;
    }
    static void do_annotate(...) { std::cerr << "(not having LocationInfo)\n"; }
};

template <typename It>
    struct MinigridParser : qi::grammar<It, row()>
{
  MinigridParser(It first) : MinigridParser::base_type(row_), annotate(first)
  {
    using namespace qi;
    type_.add
      ("W", Type::Wall)
      (" ", Type::Floor)
      ("D", Type::Door)
      ("L", Type::LockedDoor)
      ("K", Type::Key)
      ("A", Type::Ball)
      ("B", Type::Box)
      ("G", Type::Goal)
      ("V", Type::Lava)
      ("n", Type::SlipperyNorth)
      ("e", Type::SlipperyEast)
      ("s", Type::SlipperySouth)
      ("w", Type::SlipperyWest)
      ("a", Type::SlipperyNorthWest)
      ("b", Type::SlipperyNorthEast)
      ("c", Type::SlipperySouthWest)
      ("d", Type::SlipperySouthEast)
      ("X", Type::Agent)
      ("Z", Type::Adversary);
    color_.add
      ("R", Color::Red)
      ("G", Color::Green)
      ("B", Color::Blue)
      ("P", Color::Purple)
      ("Y", Color::Yellow)
      ("W", Color::White)
      (" ", Color::None);

    cell_ = type_ > color_;

    row_ = (cell_ % -qi::char_("\n"));

    auto set_location_info = annotate(_val, _1, _3);
    on_success(cell_, set_location_info);

    BOOST_SPIRIT_DEBUG_NODE(type_);
    BOOST_SPIRIT_DEBUG_NODE(color_);
    BOOST_SPIRIT_DEBUG_NODE(cell_);
  }

  private:
    phoenix::function<annotation_f<It>> annotate;

    qi::symbols<char, Type>  type_;
    qi::symbols<char, Color> color_;

    qi::rule<It, cell()> cell_;
    qi::rule<It, row()>  row_;
};

typedef boost::tokenizer< boost::escaped_list_separator<char> , std::string::const_iterator, std::string> Tokenizer;
//std::ostream& operator<<(std::ostream& os, const row& r);