/* -------------------------------------- */
                 /* Lowest level - Checksumming Algorithms */
                 /* -------------------------------------- */

/* These are only used by second-level HashSet makers internally. */

/** Abstract base for Algorithms */
class Transformer {
public:
	Transformer();
	virtual Transformer() = 0;
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashBase* getHash() = 0;
};

// Implementation of concrete algorithms
// -------------------------------------

/** Perform MD4 Checksumming */
class MD4Transformer : public Transformer {
public:
	MD4Transformer();
	virtual ~MD4Transformer();
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashBase* getHash();
};

/** Perform MD5 Checksumming */
class MD5Transformer : public Transformer {
public:
	MD5Transformer();
	virtual ~MD5Transformer();
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashBase* getHash();
};

/** Perform SHA-1 Checksumming */
class SHA1Transformer : public Transformer {
public:
	SHA1Transformer();
	virtual ~SHA1Transformer();
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashBase* getHash();
};

             /* -------------------------------------------- */
             /* Second level - HashSets used in P2P Networks */
             /* -------------------------------------------- */

/**
 * Abstract base for HashSet maker 
 */
class HashSetMaker : public Transformer {
public:
	HashSetMaker();
	virtual ~HashSetMaker() = 0;
	virtual void sumUp(const char *data, uint32_t length) = 0;
	virtual HashSetBase* getHashSet() = 0;
	virtual HashBase* getHash() = 0;
};

// Implementation of Concrete HashSet Makers
// -----------------------------------------

/**
 * Generates ED2KHashSet. Uses MD4Hasher internally. 
 */
class ED2KHashMaker : public HashSetMaker {
public:
	typedef HashSet<ED2KHash, MD4Hash, 9728000> ED2KHashSet;

	ED2KHashMaker();
	virtual ~ED2KHashMaker();
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashSetBase* getHashSet() {
		ED2KHashSet *hs = new ED2KHashSet();
		for (
			Iter i = m_partHashes.begin(); 
			i != m_partHashes.end(); ++i
		) {
			hs->addPartHash(*i);
		}
		hs->setFileHash(m_fileHash);
		return hs;
	}
	virtual HashBase* getHash();
private:
	std::vector< Hash<MD4HAsh> > m_partHashes;
	typedef std::vector< Hash<MD4Hash> >::iterator Iter;
	Hash<ED2KHash> m_fileHash;
};

/**
 * Generates FTHashSet (also known as UUHash, used in FastTrack network)
 * Uses MD5Hasher internally, but hashes 300k blocks with skipping. 
 */
class FTHashMaker : public HashSetMaker {
public:
	FTHashMaker();
	virtual ~FTHashMaker();
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashSetBase* getHashSet() {
		HashSet<UUHash> *hs = new HashSet<UUHash>;
		hs->setFileHash(m_fileHash);
		return hs;
	}
	virtual HashBase* getHash() {
		HashBase *h = new Hash<UUHash>(m_fileHash);
		return h;
	}
private:
	Hash<UUHash> m_fileHash;
};

/**
 * Generates BTHashSet, with varying chunksize. Uses SHA1Hasher internally. 
 */
class BTHashMaker : public HashSetMaker {
public:
	BTHashMaker(uint32_t chunkSize);
	virtual ~BTHashMaker();
	virtual void sumUp(const char *data, uint32_t length);
	virtual HashSetBase* getHashSet() {
		HashSet<SHA1Hash, SHA1Hash, m_chunkSize> *hs;
		hs = new HashSet<SHA1Hash, SHA1Hash, m_chunkSize>;
		hs->setFileHash(m_fileHash);
		for (
			Iter i = m_partHashes.begin(); 
			i != m_partHashes.end(); ++i
		) {
			hs->addPartHash(*i);
		}
		return hs;
	}
	virtual HashBase* getHash() {
		HashBase *h = new Hash<SHA1Hash>(m_fileHash);
	}
private:
	std::vector< Hash<SHA1Hash> > m_partHashes;
	typedef std::vector< Hash<SHA1Hash> >::iterator Iter;
	Hash<SHA1Hash> m_fileHash;
	const uint32_t m_chunkSize;
};

/**
 * Abstract Factory - derive from this to create concrete factories for
 * transformers.
 */
class TransformerFactory {
public:
	virtual Transformer* create() = 0;
protected:
	TransformerFactory();
	virtual ~TransformerFactory() = 0;
};

/**
 * Creates specific transformers. This class is a Singleton Factory which
 * spits out specific transformers (of type ObjectType) from create()
 * function. The factory itself should generally be initialized as static
 * variable somewhere. Upon construction, the factory registers itself
 * with hasher, which will in turn use it during checksumming.
 */
template<class ObjectType>
class ConcreteTransformerFactory : public TransformerFactory {
public:
	virtual Transformer* create() {
		return new ObjectType();
	}
private:
	static ConcreteTransformerFactory<ObjectType> m_factory;
	ConcreteTransformerFactory() {
		Hasher::addFactory(this);
	}
	~ConcreteTransformerFactory() {}                         //!< Forbidden
	ConcreteTransformerFactory(ConcreteTransformerFactory&); //!< Forbidden
	//! Forbidden
	ConcreteTranformerFactory& operator=(const ConcreteTransformerFactory&);
};

/**
 * A small modification to the above factory - this one also allows passing
 * one argument (of type ObjectParameter) to the constructor of ObjectType.
 */
template<class ObjectType, class ObjectParameter, ObjectParameter value>
class ConcreteTransformerFactoryArg : public TransformerFactory {
public:
	virtual Transformer* create() {
		return new ObjectType(value);
	}
};

// This initializes a concrete factory which spits out ed2k transformers. The transformer self-registers itself
// with hasher and is instantly ready for usage.
ConcreteTransformerFactory<ED2kTransfomer> ConcreteTransformerFactory<ED2kTransfomer>::m_factory;
// Same as above - for ft transformers
ConcreteTransformerFactory<FTTransformer> ConcreteTransformerFactory<FTTransformer>::m_factory;
// Somewhat more complex - this one initializes bt transformer, passing integer value of 1mb to bttransformer
// constructor
ConcreteTransformerFactory<BTTransformer, int, 1024*1024*1024> ConcreteTransformerFactory<BTTransformer, int, 1024*1024*1024>::m_factory;
                
                /* ---------------------------- */
                /* Third Level - User Interface */
                /* ---------------------------- */

/**
 * Interaction with users, taking in work requests and sending back
 * completed work. This is the only interface users need to see and
 * communicate with.
 */

//! Event notifications passed to HWClient
enum HWEvent {
	HW_COMPLETED = 1,   //!< Posted when work has been completed.
	HW_FAILED           //!< Posted when work fails for some reason.
};

class HashWork;

//! Event handler for hash work
typedef CBFunctor2<boost::shared_ptr<HashWork>, HWEvent> HWClient;

/**
 * Describes a work to be carried out by Hasher.  Either entire file can be 
 * hashed,  or only partial. If doing partial hash, only a specific hash is
 * generated,  otherwise  all  hashes are generated.  Note that the data is 
 * passed to ALL  MetaData  extractors  in either case.  After  creating an 
 * object of this class, register an event handler also to be notified when
 * the job has been completed.
 */
class HashWork : public EventSink<
	boost::shared_ptr<HashWork>, HWClient, HWEvent
> {
public:
	HashWork(const std::string &filename);
	HashWork(
		const std::string &filename, uint64_t begin, uint64_t end,
		const HashType &type
	);
	~HashWork();
private:
	const std::string m_filename;
	const HashType m_type;
	const uint64_t m_begin;
	const uint64_t m_end;
};

/* Singleton */
class Hasher {
public:
	/**
	 * Post new work to be done. The object will emit an event when the 
	 * work is completed, which the user should handle. The work object is 
	 * a shared pointer and will auto-destruct once its no longer needed.
	 *
	 * @param work     Work to be done.
	 */
	static void postWork(boost::shared_ptr<HashWork> work);
	
	/**
	 * Add a new TransformerFactory which will be used to create checksums
	 * with.
	 */
	static void addTransformer(TransformerFactory *factory);
private:
	static std::queue< boost::shared_ptr<HashWork> > s_workQueue;
	static boost::thread::mutex s_workMutex;
	static boost::thread::condition s_workNotify;
	
	static std::vector<TransformerFactory*> s_transformers;
};