#pragma once

#include <vector>
#include <cstddef>

#include "VertexNumber.h"
#include "Edge.h"
#include "Constraint.h"


class Graph {
public:
    class Iterator {
    public:
        typedef ptrdiff_t difference_type;
        typedef VertexNumber value_type;
        typedef VertexNumber& reference;
        typedef VertexNumber* pointer;
        typedef std::input_iterator_tag iterator_category;

        Iterator(const Graph* graph, VertexNumber i);
        Iterator& operator++();
        Iterator operator++(int);
        VertexNumber operator*();
        bool operator!=(const Iterator& other);
        bool operator==(const Iterator& other);
    private:
        void skipDeleted();
        VertexNumber i;
        const Graph* graph;
    };

    Graph();
    Graph(int vertexCount);
    Graph(int vertexCount, std::vector<Edge> edges);
    Iterator begin();
    Iterator end();
    int getSize();
    int getVertexCount() const;
    int getEdgeCount();
    bool containsEdge(VertexNumber u, VertexNumber v);
    void addEdge(VertexNumber u, VertexNumber v);
    void deleteVertex(VertexNumber vertex);
    bool isDeleted(VertexNumber vertex) const;
    VertexList& getNeighbors(VertexNumber vertex);
    void sortNeighborhood(VertexNumber vertex); 
    VertexList getClosedNeighborhood(VertexList& vertexSet);
    VertexList getOpenNeighborhood(VertexList& vertexSet);
    bool isSubNeighborhood(VertexNumber u, VertexNumber v);
    int getNeighborhoodEdgeCount(VertexNumber vertex);
    void addConstraint(const Constraint& constraint);
    void deleteVertexFromConstraints(VertexNumber vertex);
    void fixVertexInConstraints(VertexNumber vertex, bool inVC);
    void replaceVertexInConstraints(VertexNumber vertex, VertexNumber newVertex, bool flipped);
    VertexNumber mergeVertices(VertexNumber u, VertexNumber v);
    void setLabels(VertexList&& labels);
    VertexNumber getLabel(VertexNumber vertex) const;
    void initUsed();
    bool isUsed(const VertexNumber vertex) const;
    void setUsed(const VertexNumber vertex);
    VertexList filterNotUsed(const VertexList& list) const;
    void findReachable(VertexNumber vertex, VertexList& found);
    Graph getInducedSubgraph(const VertexList& vertices);
    std::vector<Graph> splitOnComponents();
    bool dfsMatching(VertexNumber vertex, VertexList& matching);
    bool dfsLayeredMatching(VertexNumber vertex, VertexList& matching, const std::vector<int>& layer);
    VertexList getMaxMatchingOnDuplicate();
    int getCliqueCoverLB();
    int getCliqueCoverSmartLB();
    int getDenseCover(int sizec);
    void getMirrorsSatellitesAndConstraints(VertexNumber v, VertexList& mirrors, VertexList& satellites, std::vector<Constraint>& constraints);
    bool isUnconfined(VertexNumber vertex);

    const static VertexNumber MERGED_LABEL;

    std::vector<Constraint> constraints;
    std::vector<std::vector<int>> vertexConstraints;

protected:
    int vertexCount;
    std::vector<bool> deleted;
    std::vector<int> used;
    int usedValue;
    std::vector<VertexList> neighbors;
    VertexList labels;
};