
#ifndef _FILEITEM_H
#define _FILEITEM_H

#include <string>

#include "config.h"
#include "lockable.h"
#include <header.h>

// Life line (process states) of a file description item
#define FIST_FRESH 		0
#define FIST_INITED		1
#define FIST_DLPENDING		4
#define FIST_DLGOTHEAD		8
#define FIST_DLRECEIVING	16
#define FIST_COMPLETE		32
// usually unreachable states
#define FIST_LOCALERROR 	999
#define FIST_ERRNOUSER		9999

//! Base class containing all required data and methods for communication with the download sources
class fileitem_base : public condition
{
	friend class fileitem;
public:
	virtual ~fileitem_base();
	
	// initialize file item, return the status
	virtual FiStatus Setup(bool bDynType);
	
	virtual int GetFileFd();
	uint64_t GetTransferCount();
	// send helper like wrapper for sendfile. Just declare virtual here to make it better customizable later.
	virtual ssize_t SendData(int confd, int filefd, off_t &nSendPos, size_t nMax2SendNow)=0;
	
	// downloader instruments
	typedef extended_bool<bool, false> SuccessWithTransErrorFlag;
	virtual SuccessWithTransErrorFlag DownloadStartedStoreHeader(const header & h, const char *pNextData,
			bool bForcedRestart) =0;
	void IncDlRefCount();
	void DecDlRefCount(const mstring & sReasonStatusLine);
	//virtual void SetFailureMode(const mstring & message, FiStatus fist=FIST_ERROR,
	//	bool bOnlyIfNoDlRunnuning=false);
	
	/*!
	 * \return true IFF ok and caller might continue. False -> caller should abort.
	 */
	virtual bool StoreFileData(const char *data, unsigned int size)=0;
	header const & GetHeaderUnlocked();
	inline header GetHeader() { setLockGuard; return m_head; }
	mstring GetHttpMsg();
	
	FiStatus GetStatus() { setLockGuard; return m_status; }
	FiStatus GetStatusUnlocked(off_t &nGoodDataSize) { nGoodDataSize = m_nSizeChecked; return m_status; }
	void ResetCacheState();

	//! returns true if complete or DL not started yet but partial file is present and contains requested range and file contents is static
	bool CheckUsableRange_unlocked(off_t nRangeLastByte);

	// returns when the state changes to complete or error
	FiStatus WaitForFinish(int *httpCode=NULL);

	bool SetupClean(bool bForce=false);
	
	/// mark the item as complete as-is, assuming that sizeseen is correct
	void SetupComplete();

	uint64_t m_nIncommingCount;
	off_t m_nSizeSeen;
	
	bool m_bCheckFreshness;

	// those is only good for very special purposes [tm]
	bool m_bHeadOnly;
	off_t m_nRangeLimit;

protected:

	bool m_bAllowStoreData;

	fileitem_base(mstring);
	fileitem_base();
	off_t m_nSizeChecked;
	header m_head;
	int m_filefd;
	int m_nDlRefsCount;
	FiStatus m_status;

	time_t m_nTimeExpireAt; //! when to consider this item obsolete
	static time_t m_nEarliestExpiration; //! when to consider a closer look for expired items

	mstring m_sPathAbs, m_sPathRel;

	time_t m_nTimeDlDone;

public:
	virtual void Unreg() {}; // to decrease dependent user count when used in global contents


private:
	void Init();
};

typedef SHARED_PTR<fileitem_base> tFileItemPtr;

#ifndef MINIBUILD

// dl item implementation with storage on disk
class fileitem_with_storage : public fileitem_base
{
public:
	inline fileitem_with_storage(cmstring &s) : fileitem_base(s) {};
	virtual ~fileitem_with_storage();
	// send helper like wrapper for sendfile. Just declare virtual here to make it better customizable later.
	virtual ssize_t SendData(int confd, int filefd, off_t &nSendPos, size_t nMax2SendNow);
	virtual SuccessWithTransErrorFlag DownloadStartedStoreHeader(const header & h, const char *pNextData,
			bool bForcedRestart);
	virtual bool StoreFileData(const char *data, unsigned int size);
};

typedef MYSTD::pair<int,tFileItemPtr> tFileRefEntry;
typedef MYSTD::multimap<mstring, tFileRefEntry> tFiGlobMap;

//! This is a fileitem class which is aware of other items and download sources,
//! includes global download state management
class fileitem : public fileitem_with_storage
{
public:
	virtual void Unreg(); // decrease dependent user count

	// related to GetRegisteredFileItem but used for registration of custom file item
	// implementations created elsewhere (which still need to obey regular work flow)
	static bool RegisterFileItem(SHARED_PTR<fileitem> spCustomFileItem);

	// public constructor wrapper, get a unique object from the map or a new one
	static tFileItemPtr GetRegisteredFileItem(cmstring &sPath, bool bConsiderAltStore);

	//! @return: true iff there is still something in the pool for later cleaning
	static time_t DoDelayedUnregAndCheck();

	static void dump_status();

protected:
	fileitem(cmstring &s);
	tFiGlobMap::iterator m_globRef;
};
#endif

#endif


