/*

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/>. 

*/

#ifndef TABLEAUPROOF_HPP
#define TABLEAUPROOF_HPP

#include<iostream>
#include<vector>
#include<unordered_set>

#include "Stack.hpp"
#include "Matrix.hpp"
#include "SimplePath.hpp"

using std::vector;
using std::unordered_set;
using std::filesystem::path;

using PathPair = pair<size_t, size_t>;

/**
* \brief Identification for the various kinds of node in a tableau.
*/
enum class TableauNodeType {
    Start, Extension, Connection, Reduction, Lemma, LemmaExtension, False 
};
/**
* \brief Class for representing nodes in a tableau proof. 
*
* See the documentation for TableauProof for details.
*/
class TableauProofNode {
private:
    TableauNodeType type;
    TableauProofNode* parent;
    /**
    * \brief A node in the tableau is identified 
    *        by `t : l'.
    */
    ClauseNum           t; 
    /**
    * \brief Nodes maintain the t value for children.
    */
    ClauseNum           t_child;
    /**
    * \brief A node in the tableau is identified 
    *        by `t : l'.
    */
    LitNum              l;
    /**
    * \brief Nodes maintain the l value for children.
    */
    LitNum              l_child;
    /**
    * \brief A node is associated with a clause.
    */
    Clause              c_output;
    /**
    * \brief Starts equal to c. At each step in constructing the 
    *        tableau a single literal is dealt with in a child.
    *        This keeps track of the remaining clause.
    */
    Clause              c_remaining;
    /**
    * \brief Some nodes need to produce output referring to 
    *        a clause in the matrix.
    */
    size_t              matrix_id;
    /**
    * \brief Literal associated with the node.
    */
    Literal             lit;
    /** 
    * \brief Paths are stored both as collections of literals and 
    *        collections of t:l pairs. Both are needed for 
    *        construction and producing output.
    */
    vector<PathPair>    p;
    /** 
    * \brief Paths are stored both as collections of literals and 
    *        collections of t:l pairs. Both are needed for 
    *        construction and producing output.
    */
    vector<Literal>     path;
    /**
    * \brief Substitution associated with reduction, extension or lemma.
    */
    Substitution        sub;
    /**
    * \brief Where are we in the tree? Used for backtracking.
    */
    size_t              depth;
    /**
    * \brief Used in producing LaTex output to indicate that a link 
    *        has to be added.
    */
    bool                needs_lemma_link;
    /**
    * \brief Used in producing LaTex output to indicate that a link 
    *        has to be added.
    */
    bool                needs_reduction_link;
    /**
    * \brief Which node is associated with this one?
    */
    PathPair            reduction_node;
    /**
    * \brief Which node is associated with this one?
    */
    size_t              lemma_reduction_node;
    /** 
    * \brief Lemma lists are kept as both numbers and pointer to 
    *        other nodes. Both are needed.
    */
    vector<size_t>      lemma_list;
    vector<TableauProofNode*> lemma_list_p;
    /**
    * \brief Lemma associated with a reduction or extension.
    */
    TableauProofNode*   lemma;
    /**
    * \brief Numerical identifier for a lemma.
    */
    size_t              lemma_number;
    /**
    * \brief Used in producing LaTeX output so that only used lemmas 
    *        are displayed.
    */
    bool                lemma_is_used;
    /**
    * \brief Children of this node.
    */
    vector<TableauProofNode*> children;
public:
    /**
    * \brief Basic constructor.
    */
    TableauProofNode() 
    : type(TableauNodeType::False)
    , parent(nullptr)
    , t(0)
    , t_child(0)
    , l(0)
    , l_child(0)
    , c_output()
    , c_remaining()
    , matrix_id(0)
    , lit()
    , p() 
    , path()
    , sub()
    , depth(0)
    , needs_reduction_link(false)
    , needs_lemma_link(false)
    , reduction_node()
    , lemma_reduction_node(0)
    , lemma_list()
    , lemma_list_p()
    , lemma(nullptr)
    , lemma_number(0)
    , lemma_is_used(false)
    , children() 
    {}
    /**
    * \brief Nearly all the work here is done by constructors.
    */
    TableauProofNode(Literal& _lit) 
    : TableauProofNode() {
        type = TableauNodeType::Connection;
        lit = _lit;
    }
    /**
    * \brief Nearly all the work here is done by constructors.
    */
    TableauProofNode(Clause& _c) 
    : TableauProofNode() {
        type = TableauNodeType::Start;
        c_output = _c;
        c_remaining = _c;
    }
    /**
    * \brief Nearly all the work here is done by constructors.
    */
   TableauProofNode(Clause& _c, Literal& _lit) 
   : TableauProofNode() {
        type = TableauNodeType::Extension;
        lit = _lit;
        c_output = _c;
        c_remaining = _c;
    }
    /**
    * \brief Usual destructor for this sort of thing. Remember 
    *        to delete what needs deleting.
    */
    ~TableauProofNode() { 
        delete lemma;
        for (TableauProofNode* child : children) 
            delete child;
    }
    string path_to_tptp_string();
    string parent_to_tptp_string();
    string make_path_string();
    string lemmas_to_string();
   
    friend class TableauProof;
};
/**
* \brief Class for representing a tableau proof in a format that allows 
*        extraction of TPTP-format proofs, LaTeX representations and 
*        so on.
*
* Some of the methods build the data structure from the stack used by 
* the theorem prover; some convert it to other formats. Doing this 
* directly from the stack representation is actually not a particularly 
* straightforward conversion, hence the use of a separate class rather 
* than just trying to put it in ProofPrinter.
*
* Building this thing is, to be honest, not a straightforward exercise. 
* You should think carefully before messing with the "build" method.
*/
class TableauProof {
private:
    Stack*         stack_p;
    size_t         stack_size;
    Matrix*        matrix_p;
    VariableIndex* vi;
    TermIndex*     ti;
    size_t         t_count;
    size_t         lemma_count;

    TableauProofNode* root;
    TableauProofNode* current_node;

    vector<size_t> right_stack;
    /**
    * \brief When you produce the proof you want to know which clauses 
    *        were actually used so that you can limit the printing of 
    *        the clause conversion process to the parts that are 
    *        actually needed.
    */
    unordered_set<string> clauses_used_in_proof;

    string tableau_to_LaTeX(TableauProofNode*) const;
    string tableau_to_Graphviz(TableauProofNode*, const string& = "") const;
    string tableau_to_tptp_string(TableauProofNode*) const;
    string lit_t_l_to_LaTeX(TableauProofNode*) const;
    pair<string, string> lit_t_l_to_Graphviz(TableauProofNode*, const string&) const;
public:
    TableauProof() = delete;
    TableauProof(Stack* _stack_p, Matrix* _matrix_p, 
                 VariableIndex* _vi, TermIndex* _ti) 
    : stack_p(_stack_p)
    , matrix_p(_matrix_p)
    , vi(_vi)
    , ti(_ti)
    , t_count(0)
    , lemma_count(0)
    , root(nullptr)
    , current_node(nullptr)
    , right_stack()
    , clauses_used_in_proof()
    {
        stack_size = _stack_p->size();
    }
    ~TableauProof() { delete root; }

    void build();
    string make_tptp() {
        return tableau_to_tptp_string(root);
    };
    string make_LaTeX() const;
    string make_Graphviz(const path&) const;
    void show_clauses_used_in_proof() {
        string result;
        for (const string& s : clauses_used_in_proof) {
            result += s;
            result += "\n";
        }
        cout << result << endl;
    }
    unordered_set<string> get_clauses_used_in_proof() const {
        return clauses_used_in_proof;
    }
};

#endif