/*
	This file is part of cpp-ethereum.

	cpp-ethereum 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 3 of the License, or
	(at your option) any later version.

	cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 * Ethereum client.
 */
#if ETH_ETHASHCL
#define __CL_ENABLE_EXCEPTIONS
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#include <libethash-cl/cl.hpp>
#pragma clang diagnostic pop
#else
#include <libethash-cl/cl.hpp>
#endif
#endif
#include <functional>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <libdevcore/TrieDB.h>
#include <libdevcore/TrieHash.h>
#include <libdevcore/RangeMask.h>
#include <libdevcore/Log.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TransientDirectory.h>
#include <libdevcore/CommonIO.h>
#include <libdevcrypto/SecretStore.h>
#include <libp2p/All.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/Farm.h>
#include <libdevcore/FileSystem.h>
#include <libethereum/All.h>
#include <libethcore/KeyManager.h>

#include <libethereum/AccountDiff.h>
#include <libethereum/DownloadMan.h>
#include <libethereum/Client.h>
#include <liblll/All.h>
#include <libwhisper/WhisperPeer.h>
#include <libwhisper/WhisperHost.h>
#include <test/JsonSpiritHeaders.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::p2p;
using namespace dev::shh;
namespace js = json_spirit;
namespace fs = boost::filesystem;

#if 1

int main()
{
	cdebug << pbkdf2("password", asBytes("salt"), 1, 32);
	cdebug << pbkdf2("password", asBytes("salt"), 1, 16);
	cdebug << pbkdf2("password", asBytes("salt"), 2, 16);
	cdebug << pbkdf2("testpassword", fromHex("de5742f1f1045c402296422cee5a8a9ecf0ac5bf594deca1170d22aef33a79cf"), 262144, 16);
	return 0;
}


#elif 0

int main()
{
	cdebug << "EXP";
	vector<bytes> data;
	for (unsigned i = 0; i < 10000; ++i)
		data.push_back(rlp(i));

	h256 ret;
	DEV_TIMED("triedb")
	{
		MemoryDB mdb;
		GenericTrieDB<MemoryDB> t(&mdb);
		t.init();
		unsigned i = 0;
		for (auto const& d: data)
			t.insert(rlp(i++), d);
		ret = t.root();
	}
	cdebug << ret;
	DEV_TIMED("hash256")
		ret = orderedTrieRoot(data);
	cdebug << ret;
}

#elif 0

int main()
{
	KeyManager keyman;
	if (keyman.exists())
		keyman.load("foo");
	else
		keyman.create("foo");

	Address a("9cab1cc4e8fe528267c6c3af664a1adbce810b5f");

//	keyman.importExisting(fromUUID("441193ae-a767-f1c3-48ba-dd6610db5ed0"), "{\"name\":\"Gavin Wood - Main identity\"}", "bar", "{\"hint\":\"Not foo.\"}");
//	Address a2 = keyman.address(keyman.import(Secret::random(), "Key with no additional security."));
//	cdebug << toString(a2);
	Address a2("19c486071651b2650449ba3c6a807f316a73e8fe");

	cdebug << keyman.accountDetails();

	cdebug << "Secret key for " << a << "is" << keyman.secret(a, [](){ return "bar"; });
	cdebug << "Secret key for " << a2 << "is" << keyman.secret(a2);

}

#elif 0
int main()
{
	DownloadMan man;
	DownloadSub s0(man);
	DownloadSub s1(man);
	DownloadSub s2(man);
	man.resetToChain(h256s({u256(0), u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8)}));
	assert((s0.nextFetch(2) == h256Set{(u256)7, (u256)8}));
	assert((s1.nextFetch(2) == h256Set{(u256)5, (u256)6}));
	assert((s2.nextFetch(2) == h256Set{(u256)3, (u256)4}));
	s0.noteBlock(u256(8));
	s0.doneFetch();
	assert((s0.nextFetch(2) == h256Set{(u256)2, (u256)7}));
	s1.noteBlock(u256(6));
	s1.noteBlock(u256(5));
	s1.doneFetch();
	assert((s1.nextFetch(2) == h256Set{(u256)0, (u256)1}));
	s0.doneFetch();				// TODO: check exact semantics of doneFetch & nextFetch. Not sure if they're right -> doneFetch calls resetFetch which kills all the info of past fetches.
	cdebug << s0.nextFetch(2);
	assert((s0.nextFetch(2) == h256Set{(u256)3, (u256)4}));

/*	RangeMask<unsigned> m(0, 100);
	cnote << m;
	m += UnsignedRange(3, 10);
	cnote << m;
	m += UnsignedRange(11, 16);
	cnote << m;
	m += UnsignedRange(10, 11);
	cnote << m;
	cnote << ~m;
	cnote << (~m).lowest(10);
	for (auto i: (~m).lowest(10))
		cnote << i;*/
	return 0;
}
#elif 0
int main()
{
	KeyPair u = KeyPair::create();
	KeyPair cb = KeyPair::create();
	OverlayDB db;
	State s(cb.address(), db, BaseState::Empty);
	cnote << s.rootHash();
	s.addBalance(u.address(), 1 * ether);
	Address c = s.newContract(1000 * ether, compileLLL("(suicide (caller))"));
	s.commit();
	State before = s;
	cnote << "State before transaction: " << before;
	Transaction t(0, 10000, 10000, c, bytes(), 0, u.secret());
	cnote << "Transaction: " << t;
	cnote << s.balance(c);
	s.execute(LastHashes(), t.rlp());
	cnote << "State after transaction: " << s;
	cnote << before.diff(s);
}
#elif 0
int main()
{
	GenericFarm<Ethash> f;
	BlockInfo genesis = CanonBlockChain::genesis();
	genesis.difficulty = 1 << 18;
	cdebug << genesis.boundary();

	auto mine = [](GenericFarm<Ethash>& f, BlockInfo const& g, unsigned timeout) {
		BlockInfo bi = g;
		bool completed = false;
		f.onSolutionFound([&](ProofOfWork::Solution sol)
		{
			ProofOfWork::assignResult(sol, bi);
			return completed = true;
		});
		f.setWork(bi);
		for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush)
			this_thread::sleep_for(chrono::milliseconds(100));
		cout << endl << flush;
		cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad");
	};

	Ethash::prep(genesis);

	genesis.difficulty = u256(1) << 40;
	genesis.noteDirty();
	f.startCPU();
	mine(f, genesis, 10);

	f.startGPU();

	cdebug << "Good:";
	genesis.difficulty = 1 << 18;
	genesis.noteDirty();
	mine(f, genesis, 30);

	cdebug << "Bad:";
	genesis.difficulty = (u256(1) << 40);
	genesis.noteDirty();
	mine(f, genesis, 30);

	f.stop();

	return 0;
}
#elif 0

void mine(State& s, BlockChain const& _bc)
{
	s.commitToMine(_bc);
	GenericFarm<ProofOfWork> f;
	bool completed = false;
	f.onSolutionFound([&](ProofOfWork::Solution sol)
	{
		return completed = s.completeMine<ProofOfWork>(sol);
	});
	f.setWork(s.info());
	f.startCPU();
	while (!completed)
		this_thread::sleep_for(chrono::milliseconds(20));
}
#elif 0
int main()
{
	cnote << "Testing State...";

	KeyPair me = sha3("Gav Wood");
	KeyPair myMiner = sha3("Gav's Miner");
//	KeyPair you = sha3("123");

	Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()));

	OverlayDB stateDB = State::openDB();
	CanonBlockChain bc;
	cout << bc;

	State s(stateDB, BaseState::CanonGenesis, myMiner.address());
	cout << s;

	// Sync up - this won't do much until we use the last state.
	s.sync(bc);

	cout << s;

	// Mine to get some ether!
	mine(s, bc);

	bc.attemptImport(s.blockData(), stateDB);

	cout << bc;

	s.sync(bc);

	cout << s;

	// Inject a transaction to transfer funds from miner to me.
	Transaction t(1000, 10000, 30000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret());
	assert(t.sender() == myMiner.address());
	s.execute(bc.lastHashes(), t);

	cout << s;

	// Mine to get some ether and set in stone.
	s.commitToMine(bc);
	s.commitToMine(bc);
	mine(s, bc);
	bc.attemptImport(s.blockData(), stateDB);

	cout << bc;

	s.sync(bc);

	cout << s;

	return 0;
}
#else
int main()
{
	string tempDir = boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count());

	KeyPair myMiner = sha3("Gav's Miner");

	p2p::Host net("Test");
	cdebug << "Path:" << tempDir;
	Client c(&net, tempDir);

	c.setAddress(myMiner.address());

	this_thread::sleep_for(chrono::milliseconds(1000));

	c.startMining();

	this_thread::sleep_for(chrono::milliseconds(6000));

	c.stopMining();

	return 0;
}
#endif