/**
 * File:   aux.cpp
 * Author: Alexander Ksenofontov <aksenofo@yahoo.ru>
 *
 * Created on August 15, 2016, 14:13 PM
 */

#include <sstream>
#include <algorithm>
#include <boost/uuid/sha1.hpp>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/uuid/sha1.hpp>
#include <chrono>

#include "tohaux.h"

int create_directory_recursively(const std::string& path) {
    return create_directory_recursively(boost::filesystem::path (path));
}

int create_directory_recursively(const boost::filesystem::path& p) {
    try {
		if (!boost::filesystem::create_directories(p) &&
			!boost::filesystem::exists(p)) {
    			return -1;
		}
		return 0;
    }
    catch (...) {
		return -1;
    }
}

    // Convert hex string to integer
int hexstr2int(const char* str, int len) {
    int ret = 0;
    for (auto i(0); i < len; ++i) {
	auto c = str[i];
	switch (c) {
	case 'A': case 'a': c = 0xa; break;
	case 'B': case 'b': c = 0xb; break;
	case 'C': case 'c': c = 0xc; break;
	case 'D': case 'd': c = 0xd; break;
	case 'E': case 'e': c = 0xe; break;
	case 'F': case 'f': c = 0xf; break;
	default:
	    if (c < '0' || c > '9')
		return -1;
	    c = c - '0';
	}
	ret = ret * 16 + c;
    }
    return ret;
}

    // calculate hash value
void calc_file_hash(const std::string& file_name, boost::uuids::detail::sha1::digest_type hash) {
    boost::uuids::detail::sha1 sha1;
    const size_t max_buffer_len(0xfffff);

    std::ifstream is;
    is.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    is.open(file_name.c_str(), std::ifstream::binary);

    std::unique_ptr<char[]> file_buffer(new char[max_buffer_len]);
    while (!is.eof()) {
	try {
	    is.read(file_buffer.get(), max_buffer_len);
	}
	catch (std::ios_base::failure&) {
	    if (!is.eof())
		throw;
	}
	std::streamsize len = is.gcount();
	sha1.process_bytes(file_buffer.get(), len);
    }
    is.close();
    sha1.get_digest(hash);
}

// Convert buffer path to url path
std::string url_path_encode(const std::string &s) {
    static const char lookup[] = "0123456789abcdef";
    std::stringstream e;
    for (int i = 0, ix = s.length(); i < ix; i++) {
	const char& c = s[i];
	if ((47 == c) || //  "/"
	    (48 <= c && c <= 57)  ||//0-9
	    (65 <= c && c <= 90)  ||//abc...xyz
	    (97 <= c && c <= 122) || //ABC...XYZ
	    (c == '-' || c == '_' || c == '.' || c == '~')) {
	    e << c;
	}
	else {
	    e << '%';
	    e << lookup[(c & 0xF0) >> 4];
	    e << lookup[(c & 0x0F)];
	}
    }
    return e.str();
}

void url_parser::parse(const std::string& url) {
    boost::regex expression(
	//       proto                 host         port
	"^(\?:([^:/\?#]+)://)\?(\\w+[^/\?#:]*)(\?::(\\d+))\?"
	//       path                  file       parameters
	"(/\?(\?:[^\?#/]*/)*)\?([^\?#]*)\?(\\\?(.*))\?"
    );
    std::vector<std::string> values;

    std::string src(url);

    if (boost::regex_split(std::back_inserter(values), src, expression)) {
	protocol_ = values[0];
	host_ = values[1];
	std::size_t found;
	found = host_.find("@");
	if (found != std::string::npos) {
	    user_ = host_.substr(0, found);
	    host_ = host_.substr(found + 1);
	}
	port_ = values[2];
	path_ = values[3];
	file_ = values[4];
	parameter_ = values[5];
    }
}

// Parce json file and convert it to a list
std::shared_ptr<std::vector<std::tuple<std::string, uint64_t, std::string>>>
    convert(std::istream& is, int64_t& total) {

    std::shared_ptr<std::vector<std::tuple<std::string, uint64_t, std::string>>>
	rc(new std::vector<std::tuple<std::string, uint64_t, std::string>>);
    boost::property_tree::ptree pt;
    boost::property_tree::read_json(is, pt);
    BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt.get_child("files")) {
		std::string path(v.second.get_child("path").data());
		int64_t size(v.second.get_child("size").get_value<int64_t>());
		std::string  checksum(v.second.get_child("checksum").data());
		rc->push_back(std::make_tuple(path, size, checksum));
		total += size;
    }
    return rc;
}

bool cmp_file_with_description(const std::string& root, const std::tuple<std::string, uint64_t, std::string>& desr) {

    // 1. check if file exist.
    struct _stat64 buffer;
    auto full_path(merge_path(root, std::get<0>(desr)));
    if (_stat64(full_path.c_str(), &buffer))
		return false;

    // 2. check a total sise
    if (buffer.st_size != (__int64)std::get<1>(desr))
		return false;

    // 3. check hash sum
    unsigned int hash[5];
    calc_file_hash(full_path.c_str(), hash);
    for (int i(0); i < 5; i++) {
	if (hash[i] != (unsigned int)hexstr2int(std::get<2>(desr).c_str() + (8 * i), 8))
    	return false;
    }
    return true;
}

std::string get_full_path_name(const std::string& path) {
	return merge_path(boost::filesystem::current_path().string(), path);
}
