/****************************************************************************
 * Copyright (C) from 2009 to Present EPAM Systems.
 *
 * This file is part of Indigo toolkit.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

#include <stdarg.h>
#include <stdio.h>

#include "base_c/defs.h"
#include "base_cpp/tlscont.h"
#include "graph/cycle_basis.h"
#include "graph/graph.h"
#include "graph/graph_decomposer.h"
#include "graph/spanning_tree.h"

using namespace indigo;

NeighborsAuto Vertex::neighbors() const
{
    return NeighborsAuto(*this);
}

int Vertex::findNeiVertex(int idx) const
{
    for (int i = neighbors_list.begin(); i < neighbors_list.end(); i = neighbors_list.next(i))
        if (neighbors_list[i].v == idx)
            return i;

    return -1;
}

int Vertex::findNeiEdge(int idx) const
{
    for (int i = neighbors_list.begin(); i < neighbors_list.end(); i = neighbors_list.next(i))
        if (neighbors_list[i].e == idx)
            return i;

    return -1;
}

IMPL_ERROR(Graph, "graph");

Graph::Graph()
{
    _vertices = new ObjPool<Vertex>();
    _neighbors_pool = new Pool<List<VertexEdge>::Elem>();
    _sssr_pool = 0;
    _components_valid = false;
}

Graph::~Graph()
{
    delete _vertices;
    delete _neighbors_pool;
    if (_sssr_pool != 0)
    {
        _sssr_vertices.clear();
        _sssr_edges.clear();
        delete _sssr_pool;
    }
}

int Graph::addVertex()
{
    return _vertices->add(*_neighbors_pool);
}

int Graph::findEdgeIndex(int beg, int end) const
{
    const Vertex& vbeg = getVertex(beg);

    for (int i = vbeg.neiBegin(); i != vbeg.neiEnd(); i = vbeg.neiNext(i))
        if (vbeg.neiVertex(i) == end)
            return vbeg.neiEdge(i);

    return -1;
}

bool Graph::haveEdge(int beg, int end) const
{
    return findEdgeIndex(beg, end) != -1;
}

bool Graph::hasEdge(int idx) const
{
    return _edges.hasElement(idx);
}

bool Graph::hasVertex(int idx) const
{
    return _vertices->hasElement(idx);
}

int Graph::getEdgeEnd(int beg, int edge) const
{
    const Edge& e = getEdge(edge);
    if (e.beg == beg)
        return e.end;
    if (e.end == beg)
        return e.beg;
    return -1;
}

int Graph::addEdge(int beg, int end)
{
    if (beg == end)
        throw Error("can't have loop-edge on vertex %d", beg);

    if (findEdgeIndex(beg, end) != -1)
        throw Error("already have edge between vertices %d and %d", beg, end);

    int edge_idx = _edges.add();

    Vertex& vbeg = _vertices->at(beg);
    Vertex& vend = _vertices->at(end);

    int ve1_idx = vbeg.neighbors_list.add();
    int ve2_idx = vend.neighbors_list.add();

    VertexEdge& ve1 = vbeg.neighbors_list[ve1_idx];
    VertexEdge& ve2 = vend.neighbors_list[ve2_idx];

    ve1.v = end;
    ve2.v = beg;
    ve1.e = edge_idx;
    ve2.e = edge_idx;

    _edges[edge_idx].beg = beg;
    _edges[edge_idx].end = end;

    _topology_valid = false;
    _sssr_valid = false;
    _components_valid = false;

    return edge_idx;
}

void Graph::swapEdgeEnds(int edge_idx)
{
    int tmp;

    __swap(_edges[edge_idx].beg, _edges[edge_idx].end, tmp);
}

void Graph::removeEdge(int idx)
{
    Edge edge = _edges[idx];

    Vertex& beg = _vertices->at(edge.beg);
    Vertex& end = _vertices->at(edge.end);

    _edges.remove(idx);

    beg.neighbors_list.remove(beg.findNeiEdge(idx));
    end.neighbors_list.remove(end.findNeiEdge(idx));

    _topology_valid = false;
    _sssr_valid = false;
    _components_valid = false;
}

void Graph::removeAllEdges()
{
    for (int i = _vertices->begin(); i != _vertices->end(); i = _vertices->next(i))
        _vertices->at(i).neighbors_list.clear();

    _edges.clear();
    _topology_valid = false;
    _sssr_valid = false;
    _components_valid = false;
}

void Graph::removeVertex(int idx)
{
    QS_DEF(Array<int>, edges);

    const Vertex& vertex = getVertex(idx);

    int i;

    edges.clear();

    for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
        edges.push(vertex.neiEdge(i));

    for (i = 0; i < edges.size(); i++)
        removeEdge(edges[i]);

    _vertices->remove(idx);

    _topology_valid = false;
    _sssr_valid = false;
    _components_valid = false;
}

const Vertex& Graph::getVertex(int idx) const
{
    return _vertices->at(idx);
}

const Edge& Graph::getEdge(int idx) const
{
    return _edges[idx];
}

bool Graph::isConnected(Graph& graph)
{
    return graph.countComponents() == 1;
}

struct BfsState
{
    int state;
    int prev;
    int edge;
};

/* Finds path, writes edge indices into path_out. Returns false if no path. */
bool Graph::findPath(int from, int where, Array<int>& path_out) const
{
    path_out.clear();

    QS_DEF(Array<int>, queue);
    QS_DEF(Array<BfsState>, states);

    queue.clear_resize(_vertices->size());
    states.clear_resize(_vertices->end());

    states.zerofill();

    int top = 1, bottom = 0;
    bool have_path = false;

    states[where].state = 1;
    queue[0] = where;

    while (top != bottom)
    {
        if (queue[bottom] == from)
        {
            have_path = true;
            break;
        }

        const Vertex& vertex = getVertex(queue[bottom]);

        states[queue[bottom]].state = 2;

        for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
        {
            int other = vertex.neiVertex(i);

            if (states[other].state == 0)
            {
                queue[top++] = other;
                states[other].state = 1;
                states[other].prev = queue[bottom];
                states[other].edge = vertex.neiEdge(i);
            }
        }
        bottom++;
    }

    if (have_path)
    {
        while (from != where)
        {
            path_out.push(states[from].edge);
            from = states[from].prev;
        }
        return true;
    }

    return false;
}

VerticesAuto Graph::vertices()
{
    return VerticesAuto(*this);
}

EdgesAuto Graph::edges()
{
    return EdgesAuto(*this);
}

void Graph::clear()
{
    _vertices->clear();
    _edges.clear();
    _topology_valid = false;
    _sssr_valid = false;
    _components_valid = false;
}

bool Graph::isChain_AssumingConnected(const Graph& graph)
{
    // ensure it is a tree
    if (graph.vertexCount() - graph.edgeCount() != 1)
        return false;

    for (int i = graph.vertexBegin(); i < graph.vertexEnd(); i = graph.vertexNext(i))
        if (graph.getVertex(i).degree() > 2)
            return false;

    return true;
}

bool Graph::isTree(Graph& graph)
{
    if (!Graph::isConnected(graph))
        return false;

    if (graph.vertexCount() != graph.edgeCount() + 1)
        return false;

    return true;
}

void Graph::filterVertices(const Graph& graph, const int* filter, int filter_type, int filter_value, Array<int>& result)
{
    result.clear();

    for (int i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i))
    {
        if (filter != 0)
        {
            if (filter_type == FILTER_EQ && filter_value != filter[i])
                continue;
            if (filter_type == FILTER_NEQ && filter_value == filter[i])
                continue;
        }
        result.push(i);
    }
}

void Graph::filterEdges(const Graph& graph, const int* filter, int filter_type, int filter_value, Array<int>& result)
{
    result.clear();

    for (int i = graph.edgeBegin(); i != graph.edgeEnd(); i = graph.edgeNext(i))
    {
        if (filter != 0)
        {
            if (filter_type == FILTER_EQ && filter_value != filter[i])
                continue;
            if (filter_type == FILTER_NEQ && filter_value == filter[i])
                continue;
        }
        result.push(i);
    }
}

void Graph::_mergeWithSubgraph(const Graph& other, const Array<int>& vertices, const Array<int>* edges, Array<int>* vertex_mapping, Array<int>* edge_mapping)
{
    QS_DEF(Array<int>, tmp_mapping);
    int i;

    if (vertex_mapping == 0)
        vertex_mapping = &tmp_mapping;

    vertex_mapping->clear_resize(other.vertexEnd());
    vertex_mapping->fffill();

    if (edge_mapping != 0)
    {
        edge_mapping->clear_resize(other.edgeEnd());
        edge_mapping->fffill();
    }

    for (i = 0; i < vertices.size(); i++)
    {
        int idx = vertices[i];

        if (vertex_mapping->at(idx) != -1)
            throw Error("makeSubgraph(): repeated vertex #%d", idx);

        vertex_mapping->at(idx) = addVertex();
    }

    if (edges != 0)
    {
        for (i = 0; i != edges->size(); i++)
        {
            const Edge& edge = other.getEdge(edges->at(i));

            int beg = vertex_mapping->at(edge.beg);
            int end = vertex_mapping->at(edge.end);

            if (beg == -1 || end == -1)
                throw Error("_mergeWithSubgraph: edge %d maps to (%d, %d)", edges->at(i), beg, end);

            int idx = addEdge(beg, end);

            if (edge_mapping != 0)
                edge_mapping->at(edges->at(i)) = idx;
        }
    }
    else
        for (i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i))
        {
            const Edge& edge = other.getEdge(i);
            int beg = vertex_mapping->at(edge.beg);
            int end = vertex_mapping->at(edge.end);

            if (beg != -1 && end != -1)
            {
                int idx = addEdge(beg, end);
                if (edge_mapping != 0)
                    edge_mapping->at(i) = idx;
            }
        }
}

void Graph::buildEdgeMapping(const Graph& other, Array<int>* mapping, Array<int>* edge_mapping)
{
    for (int i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i))
    {
        const Edge& edge = other.getEdge(i);
        int beg = mapping->at(edge.beg);
        int end = mapping->at(edge.end);

        if (beg != -1 && end != -1)
        {
            int idx = findEdgeIndex(beg, end);
            if (edge_mapping != 0)
                edge_mapping->at(i) = idx;
        }
    }
}

void Graph::mergeWith(const Graph& other, Array<int>* mapping)
{
    QS_DEF(Array<int>, vertices);
    int i;

    vertices.clear();

    for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i))
        vertices.push(i);

    _mergeWithSubgraph(other, vertices, 0, mapping, 0);
}

void Graph::makeSubgraph(const Graph& other, const Array<int>& vertices, Array<int>* vertex_mapping)
{
    clear();
    _mergeWithSubgraph(other, vertices, 0, vertex_mapping, 0);
}

void Graph::makeSubgraph(const Graph& other, const Array<int>& vertices, Array<int>* vertex_mapping, const Array<int>* edges, Array<int>* edge_mapping)
{
    clear();
    _mergeWithSubgraph(other, vertices, edges, vertex_mapping, edge_mapping);
}

void Graph::makeSubgraph(const Graph& other, const Filter& filter, Array<int>* mapping_out, Array<int>* inv_mapping)
{
    QS_DEF(Array<int>, vertices);

    if (mapping_out == 0)
        mapping_out = &vertices;

    filter.collectGraphVertices(other, *mapping_out);

    makeSubgraph(other, *mapping_out, inv_mapping);
}

void Graph::cloneGraph(const Graph& other, Array<int>* mapping)
{
    QS_DEF(Array<int>, vertices);
    vertices.clear();

    for (int i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i))
        vertices.push(i);
    makeSubgraph(other, vertices, mapping);
}

void Graph::makeEdgeSubgraph(const Graph& other, const Array<int>& vertices, const Array<int>& edges, Array<int>* v_mapping, Array<int>* e_mapping)
{
    QS_DEF(Array<int>, tmp_mapping);
    int i;

    if (v_mapping == 0)
        v_mapping = &tmp_mapping;

    v_mapping->clear_resize(other.vertexEnd());

    for (i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i))
        v_mapping->at(i) = -1;

    if (e_mapping != 0)
        e_mapping->clear_resize(other.edgeEnd());

    clear();

    for (i = 0; i < vertices.size(); i++)
    {
        int idx = vertices[i];

        if (v_mapping->at(idx) != -1)
            throw Error("makeEdgeSubgraph(): repeated vertex #%d", idx);

        v_mapping->at(idx) = addVertex();
    }

    for (i = 0; i < edges.size(); i++)
    {
        int edge_idx = edges[i];
        const Edge& edge = other.getEdge(edge_idx);
        int beg = v_mapping->at(edge.beg);
        int end = v_mapping->at(edge.end);

        int new_edge_idx = addEdge(beg, end);
        if (e_mapping != 0)
            e_mapping->at(edge_idx) = new_edge_idx;
    }
}

int Graph::findMappedEdge(const Graph& graph, const Graph& mapped_graph, int edge_idx, const int* mapping)
{
    const Edge& edge = graph.getEdge(edge_idx);

    int beg = mapping[edge.beg];
    int end = mapping[edge.end];

    if (beg == -1 || end == -1)
        return -1;

    return mapped_graph.findEdgeIndex(beg, end);
}

int Graph::getEdgeTopology(int idx)
{
    if (!_topology_valid)
        _calculateTopology();

    return _topology[idx];
}

bool Graph::vertexInRing(int idx)
{
    const Vertex& vertex = getVertex(idx);
    int i;

    for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
        if (getEdgeTopology(vertex.neiEdge(i)) == TOPOLOGY_RING)
            return true;

    return false;
}

void Graph::_calculateTopology()
{
    SpanningTree spt(*this, 0);
    int i;

    _topology.clear_resize(_edges.end());

    for (i = _edges.begin(); i != _edges.end(); i = _edges.next(i))
        _topology[i] = TOPOLOGY_CHAIN;

    spt.markAllEdgesInCycles(_topology.ptr(), TOPOLOGY_RING);
    _topology_valid = true;
}

void Graph::setEdgeTopology(int idx, int topology)
{
    _topology.expandFill(idx + 1, -1);
    _topology[idx] = topology;
}

void Graph::validateEdgeTopologies()
{
    _topology_valid = true;
}

int Graph::vertexCountSSSR(int idx)
{
    if (!_sssr_valid)
        _calculateSSSR();

    return _v_sssr_count[idx];
}

int Graph::vertexSmallestRingSize(int idx)
{
    if (!_sssr_valid)
        _calculateSSSR();

    return _v_smallest_ring_size[idx];
}

int Graph::edgeSmallestRingSize(int idx)
{
    if (!_sssr_valid)
        _calculateSSSR();

    return _e_smallest_ring_size[idx];
}

void Graph::_calculateSSSRInit()
{
    _v_smallest_ring_size.clear_resize(vertexEnd());
    _e_smallest_ring_size.clear_resize(edgeEnd());
    _v_sssr_count.clear_resize(vertexEnd());

    _v_smallest_ring_size.fffill();
    _e_smallest_ring_size.fffill();
    _v_sssr_count.zerofill();

    if (_sssr_pool == 0)
        _sssr_pool = new Pool<List<int>::Elem>();

    _sssr_vertices.clear();
    _sssr_edges.clear();
}

void Graph::_calculateSSSRByCycleBasis(CycleBasis& basis)
{
    _calculateSSSRInit();

    for (int i = 0; i < basis.getCyclesCount(); i++)
    {
        const Array<int>& cycle = basis.getCycle(i);

        List<int>& vertices = _sssr_vertices.push(*_sssr_pool);
        List<int>& edges = _sssr_edges.push(*_sssr_pool);

        _calculateSSSRAddEdgesAndVertices(cycle, edges, vertices);

        for (int j = vertices.begin(); j != vertices.end(); j = vertices.next(j))
        {
            int idx = vertices[j];

            if (_v_smallest_ring_size[idx] == -1 || _v_smallest_ring_size[idx] > cycle.size())
                _v_smallest_ring_size[idx] = cycle.size();

            _v_sssr_count[idx]++;
        }

        for (int j = edges.begin(); j != edges.end(); j = edges.next(j))
        {
            int idx = edges[j];

            if (_e_smallest_ring_size[idx] == -1 || _e_smallest_ring_size[idx] > cycle.size())
                _e_smallest_ring_size[idx] = cycle.size();
        }
    }

    for (int i = 0; i < _v_smallest_ring_size.size(); i++)
        if (_v_smallest_ring_size[i] == -1)
            _v_smallest_ring_size[i] = 0;

    for (int i = 0; i < _e_smallest_ring_size.size(); i++)
        if (_e_smallest_ring_size[i] == -1)
            _e_smallest_ring_size[i] = 0;

    _sssr_valid = true;
}

void Graph::_calculateSSSR()
{
    // Note: function was split into smaller functions to reduce stack usage
    QS_DEF(CycleBasis, basis);
    basis.create(*this);
    _calculateSSSRByCycleBasis(basis);
}

void Graph::_calculateComponents()
{
    GraphDecomposer decomposer(*this);
    int i;

    decomposer.decompose();

    _component_numbers.clear_resize(vertexEnd());

    for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i))
        _component_numbers[i] = decomposer.getComponent(i);

    _components_count = decomposer.getComponentsCount();

    _component_vcount.clear_resize(_components_count);
    _component_ecount.clear_resize(_components_count);

    for (i = 0; i < _components_count; i++)
    {
        _component_vcount[i] = decomposer.getComponentVerticesCount(i);
        _component_ecount[i] = decomposer.getComponentEdgesCount(i);
    }

    _components_valid = true;
}

int Graph::vertexComponent(int v_idx)
{
    if (!_components_valid)
        _calculateComponents();

    return _component_numbers[v_idx];
}

int Graph::countComponents()
{
    if (!_components_valid)
        _calculateComponents();

    return _components_count;
}

int Graph::countComponentEdges(int comp_idx)
{
    if (!_components_valid)
        _calculateComponents();

    return _component_ecount[comp_idx];
}

int Graph::countComponentVertices(int comp_idx)
{
    if (!_components_valid)
        _calculateComponents();

    return _component_vcount[comp_idx];
}

const Array<int>& Graph::getDecomposition()
{
    if (!_components_valid)
        _calculateComponents();

    return _component_numbers;
}

List<int>& Graph::sssrEdges(int idx)
{
    if (!_sssr_valid)
        _calculateSSSR();
    return _sssr_edges[idx];
}

List<int>& Graph::sssrVertices(int idx)
{
    if (!_sssr_valid)
        _calculateSSSR();
    return _sssr_vertices[idx];
}

int Graph::sssrCount()
{
    if (!_sssr_valid)
        _calculateSSSR();
    return _sssr_vertices.size();
}

void Graph::_cloneGraph_KeepIndices(const Graph& other)
{
    if (vertexCount() > 0 || edgeCount() > 0)
        throw Error("can not _clone_KeepIndices into a non-empty graph");

    int i, j, i_prev;
    int max_vertex_idx = -1;
    int max_edge_idx = -1;

    for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i))
        if (max_vertex_idx < i)
            max_vertex_idx = i;

    for (i = other.edgeBegin(); i != other.edgeEnd(); i = other.edgeNext(i))
        if (max_edge_idx < i)
            max_edge_idx = i;

    for (i = 0; i <= max_vertex_idx; i++)
        if (addVertex() != i)
            throw Error("_clone_KeepIndices: unexpected vertex index");

    i_prev = -1;

    for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i))
    {
        for (j = i_prev + 1; j < i; j++)
            removeVertex(j);
        i_prev = i;
    }

    if (vertexCount() != other.vertexCount())
        throw Error("_clone_KeepIndices: internal");

    for (i = 0; i <= max_edge_idx; i++)
        if (_edges.add() != i)
            throw Error("_clone_KeepIndices: unexpected edge index");

    i_prev = -1;

    for (i = other.edgeBegin(); i != other.edgeEnd(); i = other.edgeNext(i))
    {
        for (j = i_prev + 1; j < i; j++)
            _edges.remove(j);

        _edges[i].beg = other._edges[i].beg;
        _edges[i].end = other._edges[i].end;

        Vertex& vbeg = _vertices->at(_edges[i].beg);
        Vertex& vend = _vertices->at(_edges[i].end);

        int ve1_idx = vbeg.neighbors_list.add();
        int ve2_idx = vend.neighbors_list.add();

        VertexEdge& ve1 = vbeg.neighbors_list[ve1_idx];
        VertexEdge& ve2 = vend.neighbors_list[ve2_idx];

        ve1.v = _edges[i].end;
        ve2.v = _edges[i].beg;
        ve1.e = i;
        ve2.e = i;

        i_prev = i;
    }

    if (edgeCount() != other.edgeCount())
        throw Error("_clone_KeepIndices: internal");

    _topology_valid = false;
    _sssr_valid = false;
    _components_valid = false;
}

void Graph::_calculateSSSRAddEdgesAndVertices(const Array<int>& cycle, List<int>& edges, List<int>& vertices)
{
    int prev_beg = -1;
    int prev_end = -1;

    for (int j = 0; j < cycle.size(); j++)
    {
        const Edge& edge = getEdge(cycle[j]);

        edges.add(cycle[j]);

        if (j != cycle.size() - 1)
        {
            if (edge.beg != prev_beg && edge.beg != prev_end)
                vertices.add(edge.beg);
            if (edge.end != prev_beg && edge.end != prev_end)
                vertices.add(edge.end);
        }
        prev_beg = edge.beg;
        prev_end = edge.end;
    }
}
