/*

Copyright © 2023-26 Sean Holden. All rights reserved.

*/
/*

This file is part of Connect++.

Connect++ is free software: you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by the 
Free Software Foundation, either version 3 of the License, or (at your 
option) any later version.

Connect++ is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
more details.

You should have received a copy of the GNU General Public License along 
with Connect++. If not, see <https://www.gnu.org/licenses/>. 

*/

#include "StackItem.hpp"

//----------------------------------------------------------------------
ostream& operator<<(ostream& out, const StackItemType& si) {
  switch (si) {
    case StackItemType::Start:
      out << "Start";
      break;
    case StackItemType::Axiom:
      out << "Axiom";
      break;
    case StackItemType::Reduction:
      out << "Reduction";
      break;
    case StackItemType::LeftBranch:
      out << "Left Branch";
      break;
    case StackItemType::RightBranch:
      out << "Right Branch";
      break;
    default:
      break;
  }
  return out;
}
//----------------------------------------------------------------------
void StackItem::stack_item_setup() {
  path_length = p.length();
  lemma_size = l.size();
  lit = c.get_literal(0);
  neg_lit = lit;
  neg_lit.invert();

  lemma_current_lemma_index = lemma_size;
  lemma_backtrack_restricted = false;
  lemma_no_more_results = true;

  reduction_current_path_index = path_length;
  reduction_backtrack_restricted = false;
  reduction_no_more_results = true;

  extension_backtrack_restricted = false;
  extension_no_more_results = true;
}
//----------------------------------------------------------------------
InferenceItem StackItem::next_lemma() {
    // Default constructor makes InferenceItemType::None.
    if (lemma_backtrack_restricted || lemma_no_more_results) {
      return InferenceItem();
    }
    while (lemma_current_lemma_index > 0) {
        Literal& lit2 = l.examine_literal(--lemma_current_lemma_index);
        if (lit.subbed_equal(&lit2)) {
            lemma_no_more_results = true;
            return InferenceItem(InferenceItemType::Lemma, lit, 0,
                        lemma_current_lemma_index);
        }   
    }
    lemma_no_more_results = true;
    return InferenceItem();
}
//----------------------------------------------------------------------
InferenceItem StackItem::next_reduction(Unifier& _u) {
    // Default constructor makes InferenceItemType::None.
    if (reduction_backtrack_restricted || reduction_no_more_results) {
        return InferenceItem();
    }
    UnificationOutcome outcome = UnificationOutcome::ConflictFail;
    while (outcome != UnificationOutcome::Succeed && 
           reduction_current_path_index > 0) {
        const Literal& lit2 = p.examine_literal(--reduction_current_path_index);
        if (!neg_lit.is_compatible_with(&lit2)) {
            continue;
        }
        outcome = _u(neg_lit, lit2);
        if (outcome == UnificationOutcome::Succeed) {
            InferenceItem result = InferenceItem(InferenceItemType::Reduction, lit, 0,
                        0, 0, _u.get_substitution(), reduction_current_path_index);
            _u.backtrack();
            return result;
        }   
        _u.backtrack();
    }
    reduction_no_more_results = true;
    return InferenceItem();
}
//----------------------------------------------------------------------
InferenceItem StackItem::next_extension(Matrix& _m, Unifier& _u) {
    // Default constructor makes InferenceItemType::None.
    if (extension_backtrack_restricted || extension_no_more_results) {
        return InferenceItem();
    }
    UnificationOutcome outcome = UnificationOutcome::ConflictFail;
    while (outcome != UnificationOutcome::Succeed && 
           extension_current_index_entry > 0) {
        size_t new_index = --extension_current_index_entry;
        outcome = _u(neg_lit, _m.inspect_index_literal(extension_i, new_index));
        if (outcome == UnificationOutcome::Succeed) {
            const MatrixPairType p = _m.get_index_entry(extension_i, new_index); 
            _u.backtrack();
            // DON'T store the substitution. You're avoiding making a copy of a clause 
            // here. Later you will make one, and then you need to find the relevant 
            // substitution.
            return InferenceItem(InferenceItemType::Extension, 
                        lit, 0,
                        p.first, p.second, extension_i, new_index);
        }   
        _u.backtrack();
    }
    extension_no_more_results = true;
    return InferenceItem();
}
//----------------------------------------------------------------------
void StackItem::initialize(Matrix& _m) {
  if (params::use_lemmata) {
    lemma_no_more_results = false;
  } 

  reduction_no_more_results = false; 

  extension_no_more_results = false;
  extension_i = neg_lit.get_pred_as_index();  
  extension_current_index_entry = _m.get_index_entry_size(extension_i);
}
//----------------------------------------------------------------------
void StackItem::restrict_backtrack() {
  /*
  * Implement the leanCop style restriction on backtracking.
  */
  lemma_backtrack_restricted = true; 
  reduction_backtrack_restricted = true; 
  extension_backtrack_restricted = true; 
}
//----------------------------------------------------------------------
 InferenceItem StackItem::get_next_inference(Matrix& _m, Unifier& _u) {
  InferenceItem result;
  if (!(lemma_backtrack_restricted || lemma_no_more_results)) {
    result = next_lemma();
    if (result.T == InferenceItemType::Lemma) {
      return result;
    }
  } 
  if (!(reduction_backtrack_restricted || reduction_no_more_results)) {
    result = next_reduction(_u);
    if (result.T == InferenceItemType::Reduction) {
      return result;
    }
  }
  if (!(extension_backtrack_restricted || extension_no_more_results)) {
    result = next_extension(_m, _u);
    if (result.T == InferenceItemType::Extension) {
      return result;
    }
  }
  return result;
}
//----------------------------------------------------------------------
string StackItem::to_string_unsubbed() const {
  string result;
  result += "Stack item: ";
  switch (item_type) {
    case StackItemType::Start:
      result += "Start";
      break;
    case StackItemType::Axiom:
      result += "Axiom";
      break;
    case StackItemType::Reduction:
      result += "Reduction";
      break;
    case StackItemType::LeftBranch:
      result += "Left Branch";
      break;
    case StackItemType::RightBranch:
      result += "Right Branch";
      break;
    default:
      break;
  }
  result += " with ";
  result += "C = ";
  result += c.to_string() += "\n";
  result += "P = ";
  result += p.to_string() += "\n";
  result += "L = ";
  result += l.to_string() += "\n";
  result += "Sub = ";
  result += sub.to_string() += "\n";
  result += "Depth: ";
  result += std::to_string(depth);
  return result;
}
//----------------------------------------------------------------------
ostream& operator<<(ostream& out, const StackItem& si) {
  out << si.to_string_unsubbed() << endl;
  return out;
}