#include <vector>
#include <iostream>
#include "modules.h"
#include "cdo_output.h"

static int errState = 0;

static std::string
parseArg(const std::string &oper)
{
  if (oper == "")
    {
      printf("cdo apply: no operator given for apply.\n");
      errState = -1;
    }

  const auto mod = getModule(get_original(oper.c_str()));
  if (mod.streamInCnt <= 0)
    {
      printf("cdo apply: operator %s can not be used with apply.\n", oper.c_str());
      if (mod.streamInCnt == -1) printf("           %s has variable input.\n", oper.c_str());

      if (mod.streamInCnt == 0) printf("           %s has no input.\n", oper.c_str());

      errState = -1;
    }

  return oper;
}

static std::vector<std::string>
generateTokens(const std::string &p_oper)
{
  std::vector<std::string> result;

  auto end = p_oper.find(' ');
  auto start = 0;
  while (end != std::string::npos)
    {
      auto oper = p_oper.substr(start, end - start);
      if (oper[0] == '-') oper = parseArg(oper);
      result.push_back(oper);
      start = end + 1;
      end = p_oper.find(' ', start);
    }
  auto oper = p_oper.substr(start, end - start);
  if (oper[0] == '-') oper = parseArg(oper);
  result.push_back(oper);

  return result;
}

static std::vector<std::string>::iterator
expand(std::vector<std::string> p_oper, std::vector<std::string> &p_result, std::vector<std::string> &p_argv,
       std::vector<std::string>::iterator p_start)
{
  auto argvIter = p_start;
  while (argvIter != p_argv.end() && (*(argvIter))[0] != ']')
    {
      auto currentArg = (*(argvIter));
      if (currentArg[0] == '[')
        {
          printf("cdo apply: brackets not allowed for command apply.\n");
          errState = -1;
          return argvIter;
        }
      if (currentArg[0] == '-' && false)
        {
          printf("cdo apply: operators not allowed for command apply.\n");
          errState = -1;
          return argvIter;
        }
      for (auto &oper : p_oper)
        {
          p_result.push_back(oper);
        }
      p_result.push_back(*argvIter);
      ++argvIter;
    }

  if ((*(argvIter))[0] != ']')
    {
      printf("cdo apply: missing closing bracket.\n");
      errState = -1;
    }

  return argvIter;
}

static std::vector<std::string>
scan(std::vector<std::string> p_argv)
{
  bool firstIsNotApply = false;
  std::vector<std::string> newArgv;
  for (auto argvIter = p_argv.begin(); argvIter < p_argv.end(); argvIter++)
    {
      std::string currentArgv = *argvIter;
      if (currentArgv.compare(0, 7, "-apply,") == 0)
        {
          if (firstIsNotApply == false)
            {
              printf("cdo apply: apply can not be first.\n");
              errState = -1;
              break;
            }
          ++argvIter;
          if ((*(argvIter))[0] == '[')
            {
              const auto pos = currentArgv.find(',');
              if (pos != std::string::npos)
                {
                  auto parameter = currentArgv.substr(pos + 1);
                  // std::cout << parameter << std::endl;
                  if (parameter.empty())
                    {
                      printf("cdo apply: missing argument for apply.\n");
                      errState = -1;
                    }
                  else if (parameter[0] != '-')
                    {
                      printf("cdo apply: argument '%s' must start with minus sign (-%s).\n", parameter.c_str(), parameter.c_str());
                      errState = -1;
                    }
                  else
                    {
                      auto tokens = generateTokens(parameter);
                      argvIter = expand(tokens, newArgv, p_argv, ++argvIter);
                    }
                }
              else
                {
                  printf("cdo apply: missing argument for apply.\n");
                  errState = -1;
                }
            }
          else
            {
              printf("cdo apply: Missing bracket after apply, apply can only be used with [].\n");
              errState = -1;
            }
        }
      else
        {
          newArgv.push_back(*argvIter);
          firstIsNotApply = true;
        }
    }

  return newArgv;
}

std::vector<std::string>
expandApply(const std::vector<std::string> &p_argv)
{
  auto result = scan(p_argv);
  if (errState == -1) exit(EXIT_FAILURE);
  return result;
}
