#pragma once

#include "data_model.h"

namespace maps::mrc::gen_targets {

// Splits loop route into smaller loop routes
class TaskSplitter {
public:

    // Different modes of splitter.
    // More overhead -> more chances to split
    enum class Overhead {

        // Splits an aggregated loop (which consists of multiple small
        // loops joined together) at loop joints (crossroads) into a
        // set of smaller ones (tasks), so that the resulting loops
        // remain strongly connected w\o adding any
        // additional edges (aka overhead).
        NoOverhead,

        // Splits into tasks at loop joints even if some of the
        // resulting loops (tasks) require small overhead to preserve
        // their connnectivity.
        SmallOverhead,

        // Split path only into 2 tasks: take some loop, select two
        // good point on it and split the loop into 2 parts. Connect
        // each part tails to create 2 loops. All the
        // nested loops of the modified loop stay on the
        // corresponding part of it
        BigOverhead
    };

    TaskSplitter(const RoadNetworkData& roadNetwork,
                 const LoopsPath& path,
                 Meters minTaskLength,
                 Overhead allowedOverhead, // more overhead -> more
                                           // chances to split
                 const std::unordered_set<EdgeId>& allTargetEdges);
    LoopsPaths getResult() { return resultPaths_; }

private:
    void splitPath();

    // extract [curPath[loopStart], curPath.end()] as separated task
    // if neccessary
    void tryExtractTask(size_t loopStart, EdgeId nextEdgeInPath);

    // extract [curPath[loopStart], curPath.end()] as separated task
    void extractTask(size_t loopStart, EdgeId nextEdgeInSourcePath);

    // connects task end and begin if neccessary
    void fixTask(LoopsPath& task);

    // reconnects curPath with remaining sourcePath
    void fixCurPath(EdgeId nextEdgeInSourcePath);

    void extractOneTaskWithAnyOverhead(int taskStartOffset);
    void splitPathInto2TasksWithMinOverhead();

    // resize curPath_ and curPathPrefixLength_
    void resizeCurPath(size_t newSize);

private:
    const RoadNetworkData& roadNetwork_;
    LoopsPath sourcePath_;
    Meters minTaskLength_;
    Overhead allowedOverhead_;
    LoopsPaths resultPaths_;
    LoopsPath curPath_;
    Meters curPathLength_;
    std::vector<Meters> curPathPrefixLength_;
    Meters remainingPathLength_;
    std::unordered_map<EdgeId, size_t> numberOfEdgeVisits_;
    const std::unordered_set<EdgeId>& allTargetEdges_;
};

// splits loop route into smaller tasks
LoopsPaths splitLoopRouteIntoTasks(const RoadNetworkData& roadNetwork,
                                   const LoopsPath& path,
                                   Meters minTaskLength);

} // namespace maps::mrc::gen_targets
