/***************************************************************************
        cfilehasher.h  -  Calculate the TTH root and leaves for a file
                             -------------------
    begin                : Fri May 16 2008
    copyright            : (C) 2008 by Edward Sheldrake
    email                : ejs1920@yahoo.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef CFILEHASHER_H
#define CFILEHASHER_H

/**
 * @author Edward Sheldrake
 *
 * It's a wrapper for the tiger tree hashing functions, which currently
 * come from the DC++ source.
 *
 * However it was designed to hash files, and was never used. Much more
 * useful is to hash the data as it is received to verify it, dclib doesn't
 * do that either. Only the ValidateHashLeaves() function is used for hash
 * database verification plus maybe HashSize() is used in valknut, since
 * the DC++ headers are not installed.
 */

#include <dclib/dcos.h>
#include <dclib/core/cthread.h>
#include <dclib/core/cstring.h>
#include <dclib/core/cfile.h>

#include <dclib/core/clist.h>

/* The DC++ sources use different types than dclib: int64_t vs ulonglong */
#include <stdint.h>

class CByteArray;

/** */
enum eFileHasherStatus {
	efhsNotStarted=0,
	efhsReady,
	efhsWorking,
	efhsFinished,
	efhsError
};

class CHashedSegment {

public:
	/** Constructor */
	CHashedSegment() {
		start = 0;
		size  = 0;
	};
	/** Destructor */
	~CHashedSegment() { };
	
	/** start position */
	ulonglong start;
	/** chunk size */
	ulonglong size;
	/** expected hash, base32 encoded */
	CString expected;
	/** actual hash, base32 encoded */
	CString actual;
};

class CTreeVerificationReport {

public:
	/** Constructor */
	CTreeVerificationReport() {
		filesize = 0;
		allgood  = false;
		segments = new CList<CHashedSegment>;
	};
	
	/** Copy constructor */
	CTreeVerificationReport( const CTreeVerificationReport & other );
	
	/** Destructor */
	~CTreeVerificationReport() {
		delete segments;
		segments = 0;
	};
	
	/** Returns a nice string of the data that you can print out. */
	CString ToString() const;
	
	/** full name of file */
	CString filename;
	/** actual TTH root */
	CString tthRoot;
	/** file size */
	ulonglong filesize;
	/** true if all hashes matched */
	bool allgood;
	/** the list of file segments and their hashes */
	CList<CHashedSegment> * segments;
};
class CFileHasher {

public:
	/** Constructor */
	CFileHasher( const CString filename, CByteArray * workmem = 0 );
	/** Destructor */
	virtual ~CFileHasher();
	
	/** 
	 * Calculate the hash.
	 * Sets status to the given value when finished.
	 */
	void ComputeHash( const eFileHasherStatus doneStatus = efhsFinished );
	/** Stop the hashing */
	void StopHashing();
	/** */
	bool IsStop() const { return m_bStop; };
	/** Get status */
	eFileHasherStatus GetStatus() const { return status; };
	/** Get progress as number of bytes hashed */
	ulonglong GetProgress() const { return m_nProgress; };
	/** Get the filesize */
	ulonglong GetFileSize() const { return filesize; };
	/** Get the TTH root as a base32 encoded cstring */
	CString GetHashRoot();
	/** Returns a newly allocated copy of the hash root data */
	CByteArray * GetHashRootRaw();
	/** Returns a newly allocated copy of the hash leaf data */
	CByteArray * GetLeafData();
	/** Returns a pointer to the hash root data, so be careful with it.*/
	CByteArray * HashRootDirect() { return m_pRootData; };
	/** Returns a pointer to the hash leaf data, so be careful with it.*/
	CByteArray * HashLeavesDirect() { return m_pLeafData; };
	
	/** Returns true if the base32 encoded TTH root matches the TTH leaf data */
	static bool ValidateHashLeaves( const CString tth, CByteArray * leaves, const ulonglong filesize );
	/** The same, but with the raw TTH root not the base32 encoded string */
	static bool ValidateHashLeaves( CByteArray * root, CByteArray * leaves, const ulonglong filesize );
	/** */
	static int64_t GetBlockSize( const unsigned long leavesSize, const int64_t filesize );
	/** Get the root TTH of a bytearray as raw */
	static CByteArray * HashByteArray( CByteArray * data, unsigned long size );
	/** Get the size in bytes of the unencoded TTH value */
	static unsigned long HashSize();

protected:
	/** The file to hash */
	CFile file;
	/** The size of the file */
	ulonglong filesize;	
	/** Number of bytes hashed so far */
	ulonglong m_nProgress;
	/** Memory area to read file data to */
	CByteArray * m_pWorkMem;
	/** True if we allocated our own */
	bool usingOwnMem;
	/** Store the final TTH root data */
	CByteArray * m_pRootData;
	/** Store the final leaf data */
	CByteArray * m_pLeafData;
	/** Status */
	eFileHasherStatus status;
	/** Set to true to stop the hashing */
	bool m_bStop;
};

class CFileHasherThread : public CFileHasher, public CThread {

public:
	/** Constructor */
	CFileHasherThread ( const CString filename, CByteArray * workmem = 0 ) : CFileHasher( filename, workmem ) { };
	/** Destructor */
	virtual ~CFileHasherThread() { };
	
	/** Thread callback function */
	virtual void Thread();
};

class CFileTreeVerifier: public CFileHasher, public CThread {

public:
	/** Constructor */
	CFileTreeVerifier( const CString filename, CByteArray * leaves, CByteArray * workmem = 0 );
	/** Destructor */
	virtual ~CFileTreeVerifier();
	
	/**
	 * Gets the results of the verification.
	 * If finished, Returns the pointer to our report and sets m_pReport=0
	 * so we won't delete it when we're deleted.
	 * Whoever gets it must delete it when done with it.
	 * Otherwise returns 0 (and returns 0 the second time if you call it twice).
	 */
	CTreeVerificationReport * GetReport();
	
	/** Thread callback function */
	virtual void Thread();
	/** */
	int GetPass() const { return m_nPass; };

private:
	/** The hash leaves we are going to compare the file with */
	CByteArray * m_pLeaves;
	/** The output of the verification */
	CTreeVerificationReport * m_pReport;
	/** Set to 1 when hashing file or 2 when checking segments */
	int m_nPass;
};

#endif // CFILEHASHER_H
