#pragma once
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>

#define  IN
#define OUT
#define INOUT


class CivDbConn;

/**
 *@brief  边表结点 
 *@param weight 权重类型
 */
template <class EdgeType>
struct Edge
{
    int nDestVertex; // 该边所指向的顶点位置

    EdgeType edgeObj;  // 边的属性值对象

    Edge<EdgeType>* pNextEdge; // 连接下一条弧的指针

    Edge()
    { }

    Edge(int d, EdgeType c, Edge<EdgeType>* p = NULL)
        :nDestVertex(d), edgeObj(c), pNextEdge(p)
    {}
};

/**
 *@brief  邻接表中表的顶点
 *@param VertexType 节点信息
 *@param EdgeType 弧信息
 */
template <class VertexType, class EdgeType>
struct Vertex
{
    VertexType mVertex; // 顶点信息
    
    Edge<EdgeType>* pAdjEdges; // 指向第一条依附该顶点的弧

    Vertex(VertexType x, Edge<EdgeType>* p = NULL)
        :mVertex(x), pAdjEdges(p)
    {}
};

/**
   基于邻接表的图
*/
template <class  VertexType, class EdgeType>
class ALGraph
{
public:
    explicit ALGraph();
    ~ALGraph();

    void getVisitedResult(IN const VertexType& vetexType,OUT std::vector<VertexType>& vertex,OUT std::vector<EdgeType>& edges);

    void BFS(IN VertexType vetexType, OUT std::vector<int>& visited);

    /**
    *@brief 构建逆邻接表
    */
    ALGraph<VertexType, EdgeType>* reverseGraph();

    /**
    *@brief 往图中插入一个节点
    *@param vertexName 节点值
    */
    bool insertAVertex(IN const VertexType& vertexName);

    /**
    *@brief 两个节点之间插入一条边
    *@param vertexName1 节点对象1
    *@param vertexName2 节点对象2
    *@param edge 边
    */
    bool insertAEdge(IN const VertexType& vertexName1, IN const VertexType& vertexName2, IN const EdgeType& edge);


    /**
    *@brief 添加头节点
    *@param index 顶点索引
    *@param arcNode 头节点
    */
    bool addHeadEdage(IN const int index, IN  Edge<EdgeType>* arcNode);


    bool insertBeforeEdage(IN const int index, IN  Edge<EdgeType>* arcNode);

    /**
    *@brief 移除两个节点的边
    *@param vertexName1 节点对象1
    *@param vertexName2 节点对象2
    *@param edge 边
    */
    bool removeAEdge(IN const VertexType& vertexName1, IN const VertexType& vertexName2, IN const EdgeType& edgeWeight);

    /**
     *@brief 获取两个节点之间连接的最小权重值
     *@param vertexName1  节点1
     *@param vertexName2  节点2
     */
    EdgeType getMinWeight(IN const VertexType& vertexName1, IN const VertexType& vertexName2);

    /**
     *@brief 获取顶点索引
     *@param vertexName  顶点名，唯一标识
     */
    int getVertexIndex(IN const VertexType& vertexName);

    /**
     *@brief 获取顶点数据
     */
    int getVertexNumber();

    /**
    *@brief 根据索引获取顶点数据
    *@param index 索引值
    */
    VertexType getData(IN int index);

private:
    EdgeType getEdgeWeight(IN const Edge<EdgeType>* pEdge);

    void getVertexEdgeWeight(IN const int v1, OUT std::vector<EdgeType>& DistanceArray);

    std::vector<Vertex<VertexType, EdgeType>> mVertexArray; // 节点数组
};


template <class VertexType, class EdgeType>
ALGraph<VertexType, EdgeType>::ALGraph()
{

}

template <class VertexType, class EdgeType>
void ALGraph<VertexType, EdgeType>::getVisitedResult(
    IN const VertexType& vetexType,
    OUT std::vector<VertexType>& vertex,
    OUT std::vector<EdgeType>& edges)
{
    std::vector<int> visited;
    BFS(vetexType, visited);

    size_t total = visited.size();
    for (int i = 0; i < total; i++)
    {
        int index = visited[i];
        Vertex<VertexType, EdgeType> vertexTemp = mVertexArray.at(index);
        VertexType vertexType = vertexTemp.mVertex;
        Edge<EdgeType>* pArcNext = vertexTemp.pAdjEdges;

        // 顶点数据
        vertex.push_back(vertexType);

        // 边数据，注意去重
        while (pArcNext != nullptr)
        {
            EdgeType edgeType = pArcNext->edgeObj;
            pArcNext = pArcNext->pNextEdge;

            edges.push_back(edgeType);
          
        }
    }
}

template <class VertexType, class EdgeType>
void ALGraph<VertexType, EdgeType>::BFS(IN VertexType vetexType, OUT std::vector<int>& visited)
{
    // 获取输入顶点索引
    int vi = getVertexIndex(vetexType);
    if (-1 == vi)
    {
        return;
    }

    Edge<EdgeType>* edageNext;

    // 初始化队列
    std::queue<int> Q;
    Q.push(vi);

    visited.push_back(vi);
    while (!Q.empty()) {

        int node = Q.front();
        Q.pop();

        edageNext = mVertexArray.at(node).pAdjEdges;
        while (edageNext)
        {
            // 未访问过的放入队列
            int pVal = edageNext->nDestVertex;

            auto iter = std::find(visited.begin(), visited.end(), pVal);
            if (pVal >-1 && iter == visited.end())
            {
                visited.push_back(pVal);
                Q.push(pVal);
            }

            // 后移
            edageNext = edageNext->pNextEdge;
        }
    }
}


template <class VertexType, class EdgeType>
ALGraph<VertexType, EdgeType>* ALGraph<VertexType, EdgeType>::reverseGraph()
{
    // 构造一个你临界表图对象
    ALGraph<VertexType, EdgeType>* graph = new   ALGraph<VertexType, EdgeType>();

    size_t vertexSize = mVertexArray.size();
    //初始化表头顶点表
    for (int i = 0; i < vertexSize; i++)
    {
        Vertex<VertexType, EdgeType> vetex = mVertexArray.at(i);
        graph->insertAVertex(vetex.mVertex);

        // 定义头节点
        Edge<EdgeType>* arcNode = new Edge<EdgeType>(-1, EdgeType());
        int index = graph->getVertexIndex(vetex.mVertex);

        // 添加头节点
        graph->addHeadEdage(index, arcNode);
    }

    //遍历所有弧，构建逆邻接表
    for (int i = 0; i < vertexSize; i++)
    {
        Vertex<VertexType, EdgeType> vetex = mVertexArray.at(i);
        Edge<EdgeType>* edge = vetex.pAdjEdges;  // 顶点i的第一个出度

        while (edge != nullptr)
        {
            int k = edge->nDestVertex;// 记录节点索引位置

            // 构造
            Edge<EdgeType>* arcNode = new Edge<EdgeType>(i, edge->edgeObj);
            graph->insertBeforeEdage(k, arcNode);

            edge = edge->pNextEdge;
        }
    }

    return graph;
}

template <class VertexType, class EdgeType>
ALGraph<VertexType, EdgeType>::~ALGraph()
{
    for (auto iter = mVertexArray.begin(); iter != mVertexArray.end(); iter++)
    {
        Edge<EdgeType>* p = iter->pAdjEdges;
        while (NULL != p)
        {
            iter->pAdjEdges = p->pNextEdge;
            delete p;
            p = iter->pAdjEdges;
        }
    }
    if (!mVertexArray.empty())
    {
        mVertexArray.clear();
    }
}

template <class VertexType, class EdgeType>
bool ALGraph<VertexType, EdgeType>::insertAVertex(IN const VertexType& vertexName)
{
    Vertex<VertexType, EdgeType> VertexInstance(vertexName, NULL);
    mVertexArray.push_back(VertexInstance);

    return true;
}

template <class VertexType, class EdgeType>
bool ALGraph<VertexType, EdgeType>::addHeadEdage(IN const int index, IN  Edge<EdgeType>* arcNode)
{
    if (index < 0 || index >= mVertexArray.size())
    {
        return false;
    }
    mVertexArray.at(index).pAdjEdges = arcNode;

    return true;
}

template <class VertexType, class EdgeType>
bool ALGraph<VertexType, EdgeType>::insertBeforeEdage(IN const int index, IN Edge<EdgeType>* arcNode)
{
    if (index < 0 && index >= mVertexArray.size())
    {
        return false;
    }
    arcNode->pNextEdge = mVertexArray.at(index).pAdjEdges->pNextEdge;
    mVertexArray.at(index).pAdjEdges->pNextEdge = arcNode;

    return true;
}


template <class VertexType, class EdgeType>
bool ALGraph<VertexType, EdgeType>::insertAEdge(IN const VertexType& vertexName1,
    IN const VertexType& vertexName2, IN const EdgeType& edgeWeight)
{
    int v1 = getVertexIndex(vertexName1);
    if (-1 == v1)
    {

        return false;
    }

    int v2 = getVertexIndex(vertexName2);
    if (-1 == v2)
    {

        return false;
    }

    Edge<EdgeType>* p = mVertexArray.at(v1).pAdjEdges;
    while (p != NULL && p->nDestVertex != v2)
    {
        p = p->pNextEdge;
    }

    if (NULL == p)
    {
        p = new Edge<EdgeType>(v2, edgeWeight, mVertexArray.at(v1).pAdjEdges);
        mVertexArray.at(v1).pAdjEdges = p;
        return true;
    }

    if (v2 == p->nDestVertex)
    {
        Edge<EdgeType>* q = p;
        p = new Edge<EdgeType>(v2, edgeWeight, q->pNextEdge);
        q->pNextEdge = p;
        return true;
    }

    return false;
}

template <class VertexType, class EdgeType>
bool ALGraph<VertexType, EdgeType>::removeAEdge(IN const VertexType& vertexName1,
    IN const VertexType& vertexName2, IN const EdgeType& edgeWeight)
{
    int v1 = getVertexIndex(vertexName1);
    if (-1 == v1)
    {

        return false;
    }

    int v2 = getVertexIndex(vertexName2);
    if (-1 == v2)
    {

        return false;
    }

    Edge<EdgeType>* p = mVertexArray.at(v1).pAdjEdges;
    Edge<EdgeType>* q = NULL;
    while (p != NULL && p->nDestVertex != v2)
    {
        q = p;
        p = p->pNextEdge;
    }

    if (NULL == p)
    {
        return false;
    }

    while (edgeWeight != p->edgeObj && p->nDestVertex == v2)
    {
        q = p;
        p = p->pNextEdge;
    }

    if (v2 != p->nDestVertex)
    {
        return false;
    }

    q->pNextEdge = p->pNextEdge;
    delete p;

    return true;
}

template <class VertexType, class EdgeType>
EdgeType ALGraph<VertexType, EdgeType>::getEdgeWeight(IN const Edge<EdgeType>* pEdge)
{
    return pEdge->edgeObj;
}

template <class VertexType, class EdgeType>
void ALGraph<VertexType, EdgeType>::getVertexEdgeWeight(IN const int v1, OUT  std::vector<EdgeType>& DistanceArray)
{
    Vertex<VertexType, EdgeType> tex = mVertexArray.at(v1);
    Edge<EdgeType>* p = tex.pAdjEdges;

    int prevIndex = -1;
    EdgeType tmp;

    while (NULL != p)
    {
        // 存在相同的边
        if (prevIndex == p->nDestVertex)
        {
            if (tmp > p->edgeWeight)
            {
                DistanceArray[prevIndex] = p->edgeWeight;
            }
        }
        else
        {
            DistanceArray[p->nDestVertex] = p->edgeWeight;
            prevIndex = p->nDestVertex;
            tmp = p->edgeWeight;
        }

        p = p->pNextEdge;
    }
}

template <class VertexType, class EdgeType>
EdgeType ALGraph<VertexType, EdgeType>::getMinWeight(IN const VertexType& vertexName1,
    IN const VertexType& vertexName2)
{
    Edge<EdgeType>* pEdge = NULL;
    int v1 = getVertexIndex(vertexName1);
    if (-1 == v1)
    {
        return false;
    }

    int v2 = getVertexIndex(vertexName2);
    if (-1 == v2)
    {
        return false;
    }

    Edge<EdgeType>* p = mVertexArray.at(v1).pAdjEdges;
    while (p != NULL && p->nDestVertex != v2)
    {
        p = p->pNextEdge;
    }
    if (NULL == p)
    {
        pEdge = NULL;
        return EdgeType(0);
    }
    EdgeType tmp = getEdgeWeight(p);
    pEdge = p;
    while (NULL != p && v2 == p->nDestVertex)
    {
        if (tmp > getEdgeWeight(p))
        {
            tmp = getEdgeWeight(p);
            pEdge = p;
        }
        p = p->pNextEdge;
    }
    return tmp;
}

template <class  VertexType, class EdgeType>
int ALGraph<VertexType, EdgeType>::getVertexIndex(IN const VertexType& vertexName)
{
    for (int i = 0; i < mVertexArray.size(); i++)
    {
        if (vertexName == getData(i))
        {
            return i;
        }
    }
    return -1;
}

template <class   VertexType, class EdgeType>
int ALGraph<VertexType, EdgeType>::getVertexNumber()
{
    return mVertexArray.size();
}

template <class  VertexType, class EdgeType>
VertexType ALGraph<VertexType, EdgeType>::getData(IN int index)
{
    if (index < 0)
        return VertexType();

    Vertex<VertexType, EdgeType> tex = mVertexArray.at(index);
    VertexType vertex = tex.mVertex;
    return vertex;
}



