/*********************************************************************
 *  Author  : Tino Weinkauf and Wiebke Koepp
 *  Init    : Friday, March 23, 2018 - 15:58:29
 *
 *  Project : KTH Inviwo Modules
 *
 *  License : Follows the Inviwo BSD license model
 *********************************************************************
 */

#pragma once
#include <random>
#include "util.h"
#include "constraint.h"
#include "temporaltree.h"
#include "treeorder.h"

using namespace constraint;

/** \class TemporalTreeOrderOptimization
\brief VERY_BRIEFLY_DESCRIBE_THE_PROCESSOR

DESCRIBE_THE_PROCESSOR_FROM_A_DEVELOPER_PERSPECTIVE

@author Tino Weinkauf and Wiebke Koepp
*/
class TemporalTreeOrderOptimization {
    // Friends
    // Types
public:
    struct OptimizationState {
        size_t iteration = 0;
        TemporalTree::TTreeOrder order;
        ConstraintsStatistic statistic;
        double value = 0;
    };

    // Construction / Deconstruction
public:
    TemporalTreeOrderOptimization();
    virtual ~TemporalTreeOrderOptimization() = default;

public:
    /// Fill some properties based on the statistics file
    void fillStatistics(const ConstraintsStatistic& statistic);

    void setInitialOrder();

    /// Initalize everything
    void initializeResources();

    double weighUnfulfilledConstraint(Constraint& constraint);

    double evaluateOrder(const TemporalTree::TTreeOrder& order, ConstraintsStatistic* statistic);

    double evaluateOrder(const TemporalTree::TTreeOrder& order);

    /// Reset only statistic things and settings
    virtual void restart();

    /// Is the optimization converged
    virtual bool isConverged() = 0;

    /// Do a single optimization step
    virtual void singleStep() = 0;

    /// Run until convergence criterion is reached
    virtual void runUntilConvergence() = 0;

    /// Update the output
    void updateOutput();

    /// Save the log file
    void saveLog();

    virtual void logStep();

    virtual void initializeLog();

    virtual void logProperties() = 0;

    /// Our main computation function (Does nothing)
    virtual void process() = 0;

    void setFileNames();

    // Ports
public:
    /// Tree for which we compute the order
    std::shared_ptr<const TemporalTree> portInTree;

    /// Tree with an order
    std::shared_ptr<const TemporalTree> portOutTree;

    /// Optimization LogFile
    string portOutLogOptimization;

    /// Settings LogFile
    string portOutLogSettings;

    // Properties
public:

    /// Maximum number of iterations
    int propIterationsMax;

    /// Weight by type only
    bool propWeightByTypeOnly;

    /// Weight for type only, meaning hierarchy constraints will get weight w,
    /// topology constraints get 1-w
    double propWeightTypeOnly;

    /// Give Merge/Split Constraints and Hierarchy Constraints different weights
    bool propWeightByType;

    /// Weight for merge/split
    double propWeightMergeSplit;

    /// Weight for hierarchy
    double propWeightHierarchy;

    /// Weight according to size (number of leaves)
    bool propWeightBySize;

    /// Weight for size
    double propWeightSize;

    /// Weight according to level
    bool propWeightByLevel;

    /// Weight for level
    double propWeightLevel;

    /// Take input order as inital one if there is one
    bool propUseInputOrder;

    /// Randomize the inital order
    bool propRandomizeOrder;

    /// Whether to use the seed for the optimization or start randomly
    bool propRandomOrSeedOrder;

    /// Seed for the optimization
    int propSeedOrder;

    /// Whether to use the seed for the optimization or start randomly
    bool propRandomOrSeedOptimization;

    /// Seed for the optimization
    int propSeedOptimization;

    /* Current State */

    /// Everything regarding constraints
    string propCurrentState;

    /// Display current iteration
    int propCurrentIteration;

    /// Display current iteration
    int propBestIteration;

    /// Write out best or current state
    bool propOutputBestOrder;

    /// Display current number of all constraints fulfilled
    string propFulfilledConstraintsTotal;

    /// Display statistics for merge/split constraints
    string propStatisticsMergeSplit;

    /// Display statistics for hierarchy constraints
    string propStatisticsHierarchy;

    /// Display current number of all constraints fulfilled
    float propObjectiveValue;

    /// Display the time the last action has taken in ms
    float propTimeForLastAction;

    /// Display the time the last action has taken in ms
    float propTimeUntilBest;

    /// Do we create log files or not
    bool propSaveLog;

    // Attributes
public:
    /// Has this processor been initialized or not
    bool initialized;

    /// Input tree
    std::shared_ptr<const TemporalTree> pInputTree;

    /// Random generator used in the optimization
    mutable std::mt19937 randomGen;

    /// Extracted constraints
    std::vector<Constraint> constraints;

    /// Statistics about the extracted constraints
    std::vector<size_t> numByLevelHierarchy;
    std::vector<size_t> numByLevelMergeSplit;

    /// Total number of merge/split constraints
    size_t numConstraintsMergeSplit;

    /// Total number of hierarchy constraints
    size_t numConstraintsHierarchy;

    /// Max constraint level
    size_t maxConstraintLevel;

    /// Max constraint size
    size_t maxConstraintSize;

    /// Current state of the optimization
    OptimizationState currentState;

    /// Best state
    OptimizationState bestState;

    /// Prefix for the type of optimization
    std::string logPrefix;
};