/*
 *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdint.h>
#include <string>
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <map>
boost::lambda::placeholder1_type __1;

/**
 * Bencoder encodes input data, using `bencoding' system, as defined by
 * BitTorrent protocol specification found at http://wiki.theory.org/BitTorrentSpecification
 *
 * Summary:
 * \code
 * Integers:      i[value]e
 * Strings:       [length]:[value]
 * Lists:         l[value]:[value]:[value]e
 * Dictionaries:  d[key]:[value]:[key]:[value]e
 * \endcode
 * \note Lists and Maps may contain other lists and maps as well.
 *
 * \example
 * \code
 * std::map<std::string, BenCoder> map;
 * map["hello"] = 10;
 * map["world"] = "doh";
 * std::cerr << map << std::endl; // outputs "d5:helloi10e5:world3:dohe"
 * \endcode
 */
class BenCoder {
public:
	BenCoder();
	BenCoder(const int32_t &data);
	BenCoder(const std::string &data);
	BenCoder(const char *data);
	BenCoder(const std::vector<BenCoder> &data);
	BenCoder(const std::map<std::string, BenCoder> &data);
	friend std::ostream& operator<<(std::ostream &o, const BenCoder &b);
private:
	boost::shared_ptr<std::ostringstream> m_data;
};

typedef std::map<std::string, BenCoder> BDict;
typedef std::vector<BenCoder> BList;

// Bencoder implmenetation
// -----------------------
BenCoder::BenCoder() : m_data(new std::ostringstream) {}

BenCoder::BenCoder(const int32_t &data) : m_data(new std::ostringstream) {
	*m_data << boost::format("i%de") % data;
}

BenCoder::BenCoder(const std::string &data) : m_data(new std::ostringstream) {
	*m_data << boost::format("%d:%s") % data.size() % data;
}

BenCoder::BenCoder(const char *data) : m_data(new std::ostringstream) {
	*m_data << boost::format("%d:%s") % std::string(data).size() % data;
}

BenCoder::BenCoder(const std::vector<BenCoder> &data)
: m_data(new std::ostringstream) {
	*m_data << 'l';
	for_each(data.begin(), data.end(), *m_data << __1);
	*m_data << 'e';
}

BenCoder::BenCoder(const std::map<std::string, BenCoder> &data)
: m_data(new std::ostringstream) {
	typedef std::map<std::string, BenCoder>::const_iterator Iter;
	*m_data << 'd';
	for (Iter it = data.begin(); it != data.end(); ++it) {
		*m_data << BenCoder((*it).first) << (*it).second;
	}
	*m_data << 'e';
}

std::ostream& operator<<(std::ostream &o, const BenCoder &b) {
	return o << b.m_data->str();
}

// Sample usage - creates a standard .torrent file with example content
int main() {
	BDict torrent;
	torrent["announce"] = "http://tracker.prq.to/announce";
	torrent["creation date"] = 1115355915;

	BDict t_info; // "info" dictionary
	t_info["name"] = "My released torrent"; // top-most filename
	t_info["piece length"] = 250000;
	t_info["pieces"] = "asdfasdfagasdfasdfasdfasdfasdfasdfasdfasdfasdf"; // hashes

	BList files;

	BDict file1;
	file1["length"] = 123123;
	file1["path"] = "file1.avi";
	files.push_back(file1);

	BDict file2;
	file2["length"] = 123123;
	file2["name"] = "file2.avi";
	files.push_back(file2);

	t_info["files"] = files;
	torrent["info"] = t_info;

	std::cerr << torrent << std::endl;
}