/**
 * @file contig_graph_path.h
 * @brief ContigGraph Class.
 * @author Yu Peng (ypeng@cs.hku.hk)
 * @version 1.0.0
 * @date 2011-08-16
 */

#ifndef __GRAPH_CONTIG_GRAPH_PATH_H_

#define __GRAPH_CONTIG_GRAPH_PATH_H_

#include <stdint.h>

#include <deque>

#include "graph/contig_builder.h"
#include "graph/contig_graph_vertex.h"
#include "graph/contig_info.h"


/**
 * @brief It is a path of contigs in ContigGraph.
 */
class ContigGraphPath
{
public:
    ContigGraphPath() {}
    ContigGraphPath(const ContigGraphPath &path)
        : vertices_(path.vertices_), distances_(path.distances_) {}

    const ContigGraphPath &operator =(const ContigGraphPath &path)
    { vertices_ = path.vertices_; distances_ = path.distances_; return *this; }

    bool operator <(const ContigGraphPath &path) const
    {
		for (unsigned i = 0; i < num_nodes() && i < path.num_nodes(); ++i)
		{
			if ((*this)[i] != path[i])
				return (*this)[i] < path[i];
		}

		return num_nodes() < path.num_nodes();
    }

    ContigGraphVertexAdaptor &operator [](uint32_t index) 
    { return vertices_[index]; }
    const ContigGraphVertexAdaptor &operator [](uint32_t index) const 
    { return vertices_[index]; }

    void Append(const ContigGraphVertexAdaptor &vertex, int d)
    {
        vertices_.push_back(vertex);
        if (vertices_.size() > 1)
            distances_.push_back(d);
    }

    void Append(const ContigGraphPath &path, int d)
    {
        for (unsigned i = 0; i < path.num_nodes(); ++i)
        {
            if (i == 0)
                Append(path[i], d);
            else
                Append(path[i], path.distances()[i-1]);
        }
    }

    void Pop()
    {
        vertices_.pop_back();
        if (!distances_.empty())
            distances_.pop_back();
    }

    const ContigGraphPath &ReverseComplement()
    {
        std::reverse(vertices_.begin(), vertices_.end());
        for (unsigned i = 0; i < vertices_.size(); ++i)
            vertices_[i].ReverseComplement();
        std::reverse(distances_.begin(), distances_.end());
        return *this;
    }

    void Assemble(Sequence &contig, ContigInfo &contig_info)
    {
        ContigBuilder contig_builder;

        if (vertices_.size() > 0)
        {
            contig_builder.Append(vertices_[0], 0);
            for (unsigned i = 1; i < vertices_.size(); ++i)
                contig_builder.Append(vertices_[i], distances_[i-1]);
        }

        contig = contig_builder.contig();
        contig_info = contig_builder.contig_info();
    }

    void swap(ContigGraphPath &path) 
    {
        if (this != &path)
        {
            vertices_.swap(path.vertices_); 
            distances_.swap(path.distances_); 
        }
    }

    ContigGraphVertexAdaptor &front() { return vertices_.front(); }
    const ContigGraphVertexAdaptor &front() const { return vertices_.front(); }

    ContigGraphVertexAdaptor &back() { return vertices_.back(); }
    const ContigGraphVertexAdaptor &back() const { return vertices_.back(); }

    uint64_t kmer_count() const
    {
        uint64_t sum = 0;
        for (unsigned i = 0; i < vertices_.size(); ++i)
            sum += vertices_[i].kmer_count();
        return sum;
    }

    uint32_t size() const
    {
        uint32_t size = 0;
        for (unsigned i = 0; i < vertices_.size(); ++i)
            size += vertices_[i].contig_size();
        for (unsigned i = 0; i < distances_.size(); ++i)
            size += distances_[i];
        return size;
    }

    uint32_t internal_size(int kmer_size) const
    {
        if (vertices_.size() <= 1)
            return vertices_.size();

        uint32_t size = kmer_size + 1;
        for (unsigned i = 1; i+1 < vertices_.size(); ++i)
            size += vertices_[i].contig_size();
        for (unsigned i = 0; i < distances_.size(); ++i)
            size += distances_[i];
        return size;
    }

    uint32_t num_nodes() const { return vertices_.size(); }
    void clear() { vertices_.clear(); distances_.clear(); }

    std::deque<int> &distances() { return distances_; }
    const std::deque<int> &distances() const { return distances_; }

private:
    std::deque<ContigGraphVertexAdaptor> vertices_;
    std::deque<int> distances_; 
};

namespace std
{
template <> inline void swap(ContigGraphPath &x, ContigGraphPath &y) { x.swap(y); }
}

#endif

