#ifndef __GRAPH_HPP__
#define __GRAPH_HPP__
#include "types.hpp"

inline void split(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters = " ") {
	// Skip delimiters at beginning.
	std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	std::string::size_type pos = str.find_first_of(delimiters, lastPos);
	while (std::string::npos != pos || std::string::npos != lastPos) {
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

// Canonical graph used for canonical check.
// A pattern (DFSCode) is converted to a canonical graph
// to perform a canonical check (minimal DFSCode)
class CGraph : public std::vector<Vertex> {
private:
	unsigned edge_size_;
public:
	typedef std::vector<Vertex>::iterator vertex_iterator;
	std::map<int,int> global_local_id_map;
	int max_local_vid;
	bool has_ext_neighbor;
	CGraph() : edge_size_(0), directed(false) {}
	CGraph(bool _directed) { directed = _directed; }
	bool directed;
	unsigned edge_size() const { return edge_size_; }
	unsigned vertex_size() const { return (unsigned)size(); } // wrapper
	void buildEdge() {
		char buf[512];
		std::map <std::string, unsigned> tmp;
		unsigned id = 0;
		for(VeridT from = 0; from < (VeridT)size(); ++from) {
			for(Vertex::edge_iterator it = (*this)[from].edge.begin();
					it != (*this)[from].edge.end(); ++it) {
				if(directed || from <= it->to)
					std::sprintf(buf, "%d %d %d", from, it->to, it->elabel);
				else
					std::sprintf(buf, "%d %d %d", it->to, from, it->elabel);
				// Assign unique id's for the edges.
				if(tmp.find(buf) == tmp.end()) {
					it->id = id;
					tmp[buf] = id;
					++id;
				} else {
					it->id = tmp[buf];
				}
			}
		}
		edge_size_ = id;
	}
/*
	std::istream & read_txt(std::istream &is) {
		char line[1024];
		std::vector<std::string> result;
		clear();
		while(true) {
			unsigned pos = is.tellg();
			if(!is.getline(line, 1024)) { break; }
			result.clear();
			split(line, result);
			if(result.empty()) {
				// do nothing
			} else if(result[0] == "t") {
				if(!empty()) {   // use as delimiter
					is.seekg(pos, std::ios_base::beg);
					break;
				} else {
					// y = atoi (result[3].c_str());
				}
			} else if(result[0] == "v" && result.size() >= 3) {
				unsigned id    = atoi(result[1].c_str());
				this->resize(id + 1);
				(*this)[id].label = atoi(result[2].c_str());
				(*this)[id].global_vid = id;
			} else if(result[0] == "e" && result.size() >= 4) {
				VeridT from   = atoi(result[1].c_str());
				VeridT to     = atoi(result[2].c_str());
				LabelT elabel = atoi(result[3].c_str());
				if((VeridT)size() <= from || (VeridT)size() <= to) {
					std::cerr << "Format Error: define vertex lists before edges, from: " 
						<< from << "; to: " << to << "; vertex count: " << size() << std::endl;
					exit(1);
				}
				(*this)[from].push(from, to, elabel);
				if(directed == false)
					(*this)[to].push(to, from, elabel);
			}
		}
		this->max_local_vid = this->size() - 1;
		buildEdge();
		return is;
	}
	std::istream &read_adj(std::istream &is) {
		char line[1024];
		std::vector<std::string> result;
		clear();
		//int num_vertices, num_edges;
		//unsigned pos = is.tellg();
		is.getline(line, 1024);
		result.clear();
		split(line, result);
		if(result.empty()) {
			std::cerr << "Empty first line" << std::endl;
		} else {
			//num_vertices = atoi(result[0].c_str());
			//num_edges = atoi(result[1].c_str());
		}
		VeridT vertex_id = 0;
		while(true) {
			//pos = is.tellg();
			if(!is.getline(line, 1024)) break;
			result.clear();
			split(line, result);
			if(result.empty()) {
			} else {
				this->resize(vertex_id + 1);
				(*this)[vertex_id].label = atoi(result[0].c_str()) - 1;
				(*this)[vertex_id].global_vid = vertex_id;
				for(size_t i = 1; i < result.size(); i++) {
					VeridT to = atoi(result[i++].c_str()) - 1;
					LabelT elabel = atoi(result[i].c_str()) - 1;
					(*this)[vertex_id].push(vertex_id, to, elabel);
				}
			}
			vertex_id++;
		}
		this->max_local_vid = this->size() - 1;
		buildEdge();
		return is;
	}
	std::istream &read_partition_info(std::istream &, int part_id);  // read
	std::istream &read_partition_info_non_locals(std::istream &);  // read
	std::istream &read_adj_par(std::istream &);  // read
	std::istream &read_local_adjp(std::istream &is);
	std::ofstream &write_local_adjp(std::ofstream &os); // write
	void check(void);
	int get_vertex_label(int vid) const { return at(vid).label; }
	int get_local_vid(int global_vid){
		if (global_local_id_map.count(global_vid) > 0)
			return global_local_id_map[global_vid];
		else return -1;
	}
	bool is_pseudo_local(int local_id){
		return (local_id > max_local_vid && (*this)[local_id].vertex_part_id != (*this)[local_id].orig_part_id);
	}
	bool is_external(int local_id){
		return (local_id > max_local_vid && (*this)[local_id].vertex_part_id == (*this)[local_id].orig_part_id);
	}
	void delete_edge(int from, int to);
	void delete_vertices(std::vector<int> local_ids);
	std::string to_string() const;
	static size_t get_serialized_size(const CGraph &grph);
	static size_t get_serialized_size(char *buffer, size_t buffer_size);
	static size_t serialize(const CGraph &grph, char *buffer, size_t buffer_size);
	static size_t deserialize(CGraph &grph, char *buffer, size_t buffer_size);
	static size_t get_serialized_size_db(char *buffer, size_t buffer_size);
	size_t get_serialized_size_for_partition(int vid);
	size_t serialize_neighbors_for_partition(int global_vid, int *buffer, int buffer_size);
	size_t get_serialized_size_for_partition(int vid, int requester_partition_id);
	size_t serialize_neighbors_for_partition(int requester_partition_id, int global_vid, int* &buffer, int buffer_size);
	size_t serialize_neighbors_for_partition(int requester_partition_id, int global_vid, int* &buffer, int buffer_size, std::set<int> &exclusions);
*/
};

#endif
