#include "word-tag.hpp"
#include "fast-sprintf.hpp"

extern "C" {
#ifdef DEBUG
#include "dict-common/dict-utils.h"     // prt_exp_mem()
#endif
#include "error.h"
#include "tokenize/tok-structures.h"
#include "utilities.h"
}

#define D_IC 8
void WordTag::insert_connectors(Exp* exp, int& dfs_position,
                                bool& leading_right, bool& leading_left,
                                std::vector<int>& eps_right,
                                std::vector<int>& eps_left,
                                char* var, bool root, double parent_cost,
                                Exp* parent_exp, const X_node *word_xnode)
{
  double cost = parent_cost + exp->cost;

#ifdef DEBUG
  if (0 && verbosity_level(+D_IC)) // Extreme debug
  //if (_word == 2)
  {
    const char*type =
      ((const char *[]) {"OR_type", "AND_type", "CONNECTOR_type"}) [exp->type-1];
    printf("Expression type %s for Word%d, var %s:\n", type, _word, var);
    //printf("parent_exp: %s\n", lg_exp_stringify(parent_exp));
    printf("exp(%s) e=%.2f pc=%.2f %s\n",
           word_xnode->string,exp->cost, parent_cost, lg_exp_stringify(exp));
    if (exp->cost > 0 || root) prt_exp_mem(exp);
  }
#endif

  if (exp->type == CONNECTOR_type) {
    dfs_position++;

    switch (exp->dir) {
    case '+':
      _position.push_back(_right_connectors.size());
      _dir.push_back('+');
      _right_connectors.push_back(
           PositionConnector(parent_exp, exp, '+', _word, dfs_position,
                             cost, parent_cost, leading_right, false,
                             eps_right, eps_left, word_xnode, _opts));
      leading_right = false;
      break;
    case '-':
      _position.push_back(_left_connectors.size());
      _dir.push_back('-');
      _left_connectors.push_back(
           PositionConnector(parent_exp, exp, '-', _word, dfs_position,
                             cost, parent_cost, false, leading_left,
                             eps_right, eps_left, word_xnode, _opts));
      leading_left = false;
      break;
    default:
      throw std::string("Unknown connector direction: ") + exp->dir;
    }
  } else if (exp->type == AND_type) {
    if (exp->operand_first == NULL) {
      /* zeroary and */
      if (cost != 0)
      {
        lgdebug(+D_IC, "EmptyConnector var=%s(%d) cost %.2f pcost %.2f\n",
                var, _variables->string(var), cost, parent_cost);
        _empty_connectors.push_back(EmptyConnector(_variables->string(var),cost));
      }
    } else
      if (exp->operand_first->operand_next == NULL) {
        /* unary and - skip */
        insert_connectors(exp->operand_first, dfs_position, leading_right,
             leading_left, eps_right, eps_left, var, root, cost, parent_exp, word_xnode);
      } else {
        int i;
        Exp* opd;

        char new_var[MAX_VARIABLE_NAME];
        char* last_new_var = new_var;
        char* last_var = var;
        while ((*last_new_var = *last_var)) {
          last_new_var++;
          last_var++;
        }

        for (i = 0, opd = exp->operand_first; opd != NULL; opd = opd->operand_next, i++) {
          char* s = last_new_var;
          *s++ = 'c';
          fast_sprintf(s, i);

          double and_cost = (i == 0) ? cost : 0;
          insert_connectors(opd, dfs_position, leading_right, leading_left,
                eps_right, eps_left, new_var, false, and_cost, parent_exp, word_xnode);

          if (leading_right) {
            eps_right.push_back(_variables->epsilon(new_var, '+'));
          }
          if (leading_left) {
            eps_left.push_back(_variables->epsilon(new_var, '-'));
          }
        }
      }
  } else if (exp->type == OR_type) {
    if (exp->operand_first != NULL && exp->operand_first->operand_next == NULL) {
      /* unary or - skip */
      insert_connectors(exp->operand_first, dfs_position, leading_right, leading_left,
          eps_right, eps_left, var, root, cost, exp->operand_first, word_xnode);
    } else {
      int i;
      Exp* opd;
      bool ll_true = false;
      bool lr_true = false;

      char new_var[MAX_VARIABLE_NAME];
      char* last_new_var = new_var;
      char* last_var = var;
      while ((*last_new_var = *last_var)) {
        last_new_var++;
        last_var++;
      }

#ifdef DEBUG
      if (0 && verbosity_level(+D_IC)) { // Extreme debug
        printf("Word%d, var %s OR_type:\n", _word, var);
        printf("exp mem: "); prt_exp_mem(exp);
      }
#endif

      for (i = 0, opd = exp->operand_first; opd != NULL; opd = opd->operand_next, i++) {
        bool lr = leading_right, ll = leading_left;
        std::vector<int> er = eps_right, el = eps_left;

        char* s = last_new_var;
        *s++ = 'd';
        fast_sprintf(s, i);

        assert(word_xnode != NULL, "NULL X_node for var %s", new_var);
        if (root)
        {
          lgdebug(+D_IC, "Word%d: var: %s; exp%d=%p; X_node: %s\n",
                  _word, new_var, i, opd, word_xnode->word->subword);
        }

        insert_connectors(opd, dfs_position, lr, ll, er, el, new_var, false, cost, opd, word_xnode);

        /* For the toplevel X_node-joined OR_type, get the next X_node. */
        if (root && (word_xnode->next != NULL))
          word_xnode = word_xnode->next;

        if (lr)
          lr_true = true;
        if (ll)
          ll_true = true;
      }
      leading_right = lr_true;
      leading_left = ll_true;
    }
  }
}
#undef D_IC

void WordTag::find_matches(int w, Connector* search_cntr, char dir, std::vector<PositionConnector*>& matches)
{
  // cout << "Look connection on: ." << _word << ". ." << w << ". " << connector_string(search_cntr) << dir << endl;

  std::vector<PositionConnector>* connectors;
  switch(dir) {
  case '+':
    connectors = &_left_connectors;
    break;
  case '-':
    connectors = &_right_connectors;
    break;
  default:
    throw std::string("Unknown connector direction: ") + dir;
  }

  std::vector<PositionConnector>::iterator i;
  for (i = connectors->begin(); i != connectors->end(); i++) {
    if (WordTag::match(w, *search_cntr, dir, (*i).word, ((*i).connector))) {
      matches.push_back(&(*i));
    }
  }
}

void WordTag::add_matches_with_word(WordTag& tag)
{
  std::vector<PositionConnector>::iterator i;
  for (i = _right_connectors.begin(); i != _right_connectors.end(); i++) {
    std::vector<PositionConnector*> connector_matches;
    tag.find_matches(_word, &(*i).connector, '+', connector_matches);
    std::vector<PositionConnector*>::iterator j;
    for (j = connector_matches.begin(); j != connector_matches.end(); j++) {
      i->matches.push_back(*j);
      set_match_possible((*j)->word, (*j)->position);
      (*j)->matches.push_back(&(*i));
      tag.set_match_possible(_word, (*i).position);
    }
  }
}
