//          Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef SNOWHOUSE_COLLECTIONCONSTRAINTEVALUATOR_H
#define SNOWHOUSE_COLLECTIONCONSTRAINTEVALUATOR_H

#include <vector>

#include "../constraintoperator.h"

namespace snowhouse
{
  template<typename ConstraintListType, typename ActualType>
  struct CollectionConstraintEvaluator
  {
    static unsigned int Evaluate(const ConstraintOperator& op,
        ConstraintListType& expression, ResultStack& result,
        OperatorStack& operators, const ActualType& actual)
    {
      ConstraintOperator::EvaluateOperatorsWithLessOrEqualPrecedence(op,
          operators, result);

      unsigned int passed_elements = 0;
      for (const auto& member : actual)
      {
        if (ConstraintOperator::EvaluateElementAgainstRestOfExpression(expression, member))
        {
          ++passed_elements;
        }
      }

      return passed_elements;
    }
  };

  struct StringLineParser
  {
    static void Parse(const std::string& str, std::vector<std::string>& res)
    {
      size_t start = 0;
      size_t newline = FindNewline(str, start);

      while (newline != std::string::npos)
      {
        StoreLine(str, start, newline, res);
        start = MoveToNextLine(str, newline);
        newline = FindNewline(str, start);
      }

      if (start < str.size())
      {
        StoreLine(str, start, std::string::npos, res);
      }
    }

  private:
    static size_t FindNewline(const std::string& str, size_t start)
    {
      return str.find_first_of("\r\n", start);
    }

    static void StoreLine(const std::string& str, size_t start, size_t end,
        std::vector<std::string>& res)
    {
      std::string line = str.substr(start, end - start);
      res.push_back(line);
    }

    static size_t MoveToNextLine(const std::string& str, size_t newline)
    {
      if (str.find("\r\n", newline) == newline)
      {
        return newline + 2;
      }

      if (str.find('\n', newline) == newline)
      {
        return newline + 1;
      }

      if (str.find('\r', newline) == newline)
      {
        return newline + 1;
      }

      std::ostringstream stm;
      stm << "This string seems to contain an invalid line ending at position "
          << newline << ":" << std::endl
          << str << std::endl;
      throw InvalidExpressionException(stm.str());
    }
  };

  template<typename ConstraintListType>
  struct CollectionConstraintEvaluator<ConstraintListType, std::string>
  {
    static unsigned int Evaluate(const ConstraintOperator& op,
        ConstraintListType& expression, ResultStack& result,
        OperatorStack& operators, const std::string& actual)
    {
      std::vector<std::string> lines;
      StringLineParser::Parse(actual, lines);
      return CollectionConstraintEvaluator<ConstraintListType, std::vector<std::string>>::Evaluate(op, expression, result, operators, lines);
    }
  };
}

#endif
