#ifndef HEURISTICS_TIME_HEURISTIC_H
#define HEURISTICS_TIME_HEURISTIC_H

#include "../heuristic.h"
#include "../lp/lp_solver.h"
#include "map"
#include <chrono>

namespace time_heuristic {
class TimeHeuristic : public Heuristic {
    unsigned int timeSteps;
    unsigned int synchronizationSteps;
    bool withRepetition;
    bool iterativeApproach;
    bool integerProgram;
    bool debugOption;
    bool firstRun;
    bool firstOptimalSolution;
    bool goalsOnlyInLastTimeLayer;
    bool restrictFlowInLastLayer;
    bool transitionVariableOptimization;
    bool debugHeuristicValues;
    bool removeDeadStates;
    bool limitIterative;
    bool debugConstraints;
    bool debugFlow;
    lp::LPSolver solver;
    
    unsigned int initial_timesteps_required;

    int minOperatorCost;
    
    std::vector<unsigned int> lpInitTimes;
    std::vector<unsigned int> lpSolvingTimes;
    
    // For debugging.
    std::chrono::steady_clock::time_point start;
    std::chrono::steady_clock::time_point end;

    std::vector<lp::LPVariable> variables;
    std::vector<lp::LPConstraint> constraints;

    double infty = solver.get_infinity();
    
    // For transition-variable optimization:
    // Map operator index and equation index to boolean.
    std::map<std::tuple<unsigned int, unsigned int>, unsigned int> skippableSynchronizationEquations;
    // Map operator index and time step to variable index.
    std::map<std::tuple<unsigned int, unsigned int>, unsigned int> indicesForOptimization;
    // Map operator index and variable index to boolean.
    std::map<std::tuple<unsigned int, unsigned int>, bool> operatorHasVariableInPreconditions;
    // Map operator index to boolean.
//    std::vector<bool> operatorHasAtLeastOneVariableInPreconditions;
    
    // Map to the indices of the two restriction constraints.
    std::vector<unsigned int> restrictionConstraintIndeces;
    unsigned int boolVarIndex;
    
    // Hold incoming transitions for variable value pair.
    std::map<std::tuple<unsigned int, unsigned int>, std::vector<std::tuple<unsigned int, unsigned int, unsigned int>>> in;
    // Hold outgoing transitions (operatorIndex, startValue, endValue) for variable value pair.
    std::map<std::tuple<unsigned int, unsigned int>, std::vector<std::tuple<unsigned int, unsigned int, unsigned int>>> out;
    
    // Map var index, time step and variable value to reachable or non-reachable.
    std::vector<std::vector<std::vector<bool>>> forwardReachable;
    std::vector<std::vector<std::vector<bool>>> backwardReachable;
    
    // Map var index, time step and var value to constraint indices.
    std::map<std::tuple<unsigned int, unsigned int, unsigned int>, unsigned int> constraintIndices;
    
    // Map var index to goal variable indeces.
    std::vector<std::vector<unsigned int>> goalVariableIndices;
    
    // Map var index, var value, operator index and time to LP variable index.
    std::map<std::tuple<unsigned int, unsigned int, unsigned int, unsigned int>, unsigned int> transitionVariableIndices;

    // Map var index, time step and operator index to indices vector.
    std::map<std::tuple<unsigned int, unsigned int, unsigned int>, std::vector<unsigned int>> operatorTimeVariableIndices;
    
    // Map var index and var value to constraint index.
    std::map<std::tuple<unsigned int, unsigned int>, unsigned int> firstLayerConstraintIndices;
    
    // Map var index to var value.
    std::map<unsigned int, unsigned int> oldInitialValues;
    
    // Print times spent with initializing and solving the LP.
    void printStatistics();
protected:
    virtual int compute_heuristic(const GlobalState &global_state);
public:
    TimeHeuristic(const options::Options &options);
    ~TimeHeuristic();
};
}

#endif
