#pragma once

/*******************************************************************************

In a AnchorGraph, each vertex represents a global Anchor
(currently identified with a primary edge of the marker graph).
Edges are generated by following the reads.

*******************************************************************************/

// Shasta.
#include "mode3-Anchor.hpp"
#include "Base.hpp"
#include "MultithreadedObject.hpp"
#include "ReadId.hpp"
#include "shastaTypes.hpp"

// Boost libraries.
#include <boost/graph/adjacency_list.hpp>

// Standard library.
#include "iosfwd.hpp"
#include "memory.hpp"
#include "string.hpp"
#include "utility.hpp"
#include "vector.hpp"

namespace shasta {
    class CompressedMarker;

    namespace mode3 {

        // A connected component of the Anchor graph,
        // in which each vertex represents an Anchor.
        // Edges are created by following the reads on their journeys over Anchors.
        class AnchorGraphVertex;
        class AnchorGraphEdge;
        class AnchorGraph;
        using AnchorGraphBaseClass = boost::adjacency_list<
            boost::listS,
            boost::listS,
            boost::bidirectionalS,
            AnchorGraphVertex,
            AnchorGraphEdge>;

        class AnchorGraphDisplayOptions;

    }
}



// Class to control Graphviz output of AnchorGraph.
class shasta::mode3::AnchorGraphDisplayOptions {
public:
    bool labels = true;
    bool tooltips = true;
    bool colorVertices = true;
    bool colorEdges = true;
    bool showNonTransitiveReductionEdges = true;

    // Thresholds for coloring by corrected Jaccard similarity J'.
    // If J' <= redJ, the edge is drawn red.
    // If J' >= greenJ, the edge is drawn green.
    // For values in between, the color is interpolated.
    double redJ;
    double greenJ;

    AnchorGraphDisplayOptions(double redJ = 0., double greenJ = 1.) :
        redJ(redJ), greenJ(greenJ) {}

    void makeCompact()
    {
        labels = false;
        tooltips = false;
        colorVertices = false;
        colorEdges = false;
    }
};



class shasta::mode3::AnchorGraphVertex {
public:
    uint64_t localAnchorId;
    AnchorGraphVertex() {}
    AnchorGraphVertex(AnchorId anchorId) = delete;

    // The reverse complement vertex.
    // Only used during strand separation.
    AnchorGraphBaseClass::vertex_descriptor vRc = AnchorGraphBaseClass::null_vertex();
};



class shasta::mode3::AnchorGraphEdge {
public:
    AnchorPairInfo info;
    uint64_t coverage;
    bool isNonTransitiveReductionEdge = false;
    AnchorGraphEdge(const AnchorPairInfo& info, uint64_t coverage) :
        info(info), coverage(coverage) {}

    // The reverse complement edge.
    // Only used during strand separation.
    AnchorGraphBaseClass::edge_descriptor eRc;
};



class shasta::mode3::AnchorGraph : public AnchorGraphBaseClass {
public:

    // Create the AnchorGraph and its vertices and edges given the AnchorIds
    // for a conneced component of the global anchor graph.
    AnchorGraph(const Anchors&, span<const AnchorId> anchorIds);

    // The AnchorIds of this AnchorGraph. Each of these corresponds to a vertex.
    // An index in this vector is called a local anchor id.
    span<const AnchorId> anchorIds;

    // Get the AnchorId of a given vertex.
    // The vertex stores the local anchor id (index into the anchorIds vector).
    AnchorId getAnchorId(vertex_descriptor v) const
    {
        const AnchorGraph& anchorGraph = *this;
        const AnchorGraphVertex& vertex = anchorGraph[v];
        return anchorIds[vertex.localAnchorId];
    }

    // The vertex_descirptor corresponding to each local anchor id.
    // Index by the local anchor id.
    vector<vertex_descriptor> vertexDescriptors;

    // Add an edge between the vertices corresponding to given local anchor ids.
    void addEdgeFromLocalAnchorIds(
        uint64_t,
        uint64_t,
        const AnchorPairInfo&,
        uint64_t coverage);

    void writeGraphviz(
        const string& name,
        const AnchorGraphDisplayOptions&,
        const Anchors&) const;

    void writeEdgeCoverageHistogram(const string& fileName) const;

    void localTransitiveReduction(
        uint64_t distance,
        uint64_t maxCoverage);

    // Remove edges with negative estimated offset.
    void removeNegativeOffsetEdges();

    // Remove cross-edges.
    // This removes an edge v0->v1 if the following are all true:
    // - Its coverage is at most lowCoverageThreshold.
    // - Its estimated offset is at least minOffset.
    // - v0 has at least one out-edge with coverage at least highCoverageThreshold.
    // - v1 has at least one in-edge with coverage at least highCoverageThreshold.
    void removeCrossEdges(
        uint64_t lowCoverageThreshold,
        uint64_t highCoverageThreshold,
        uint64_t minOffset,
        bool debug);

    // Remove edges for which loss = (commonCount - coverage) / commonCount > maxLoss
    void removeWeakEdges(double maxLoss, bool debug);

};

