#ifndef _SIAAPI_H
#define _SIAAPI_H

#include <siacommon.h>
#include <siacurl.h>
#include <autothread.h>

NS_BEGIN(Sia)
NS_BEGIN(Api)

class CSiaDriveConfig;
class SIADRIVE_EXPORTABLE CSiaBase
{
public:
	explicit CSiaBase(CSiaDriveConfig* siaDriveConfig) :
		_siaDriveConfig(siaDriveConfig)
	{
	}

public:
	virtual ~CSiaBase() = 0
	{
	}

private:
	CSiaDriveConfig* _siaDriveConfig;

public:
	inline CSiaDriveConfig& GetSiaDriveConfig() const
	{
		return *_siaDriveConfig;
	}
};


class SIADRIVE_EXPORTABLE CSiaApi
{
public:
	enum class _SiaApiErrorCode
	{
		Success,
		NotImplemented,
    NotConnected,
		RequestError,
		WalletExists,
		WalletLocked,
		WalletUnlocked,
		WalletNotCreated
	};

	enum class _SiaSeedLanguage
	{
		English,
		German,
		Japanese
	};

	class _CSiaFileTree;
	class SIADRIVE_EXPORTABLE _CSiaFile :
		public virtual CSiaBase
	{
		friend CSiaApi;
		friend _CSiaFileTree;

	public:
		explicit _CSiaFile(CSiaDriveConfig* siaDriveConfig, const json& fileJson);

	public:
		virtual ~_CSiaFile();

	// Properties
		Property(SString, SiaPath, public, private)
		Property(std::uint64_t, FileSize, public, private)
		Property(bool, Available, public, private)
		Property(bool, Renewing, public, private)
		Property(std::uint32_t, Redundancy, public, private)
		Property(std::uint32_t, UploadProgress, public, private)
		Property(std::uint32_t, Expiration, public, private)
	};

	class SIADRIVE_EXPORTABLE _CSiaFileTree :
		public virtual CSiaBase
	{
		friend CSiaApi;
	public:
		explicit _CSiaFileTree(CSiaDriveConfig* siaDriveConfig);

	public:
		virtual ~_CSiaFileTree();

	private:
		std::shared_ptr<std::vector<std::shared_ptr<_CSiaFile>>> _fileList;
    std::mutex _updateMutex;

	public:
		void BuildTree(const json& result);
		std::shared_ptr<std::vector<std::shared_ptr<_CSiaFile>>> GetFileList();
		std::vector<std::shared_ptr<_CSiaFile>> Query(SString query);
		std::shared_ptr<_CSiaFile> GetFile(const SString& siaPath);
		std::vector<SString> QueryDirectories(SString query);
		bool FileExists(const SString& siaPath);
	};

	class SIADRIVE_EXPORTABLE _CSiaWallet :
		public virtual CSiaBase
	{
		friend CSiaApi;
	private:
		explicit _CSiaWallet(CSiaDriveConfig* siaDriveConfig);

	public:
		virtual ~_CSiaWallet();

	// Properties
		Property(bool, Created, public, private)
		Property(bool, Locked, public, private)
    Property(bool, Connected, public, private)
    Property(SiaCurrency, ConfirmedBalance, public, private)
    Property(SiaCurrency, UnconfirmedBalance, public, private)
    Property(SString, ReceiveAddress, public, private)

	private:
    virtual void Refresh();

	public:
		CSiaError<_SiaApiErrorCode> Create(const SString& password, const _SiaSeedLanguage& seedLanguage, SString& seed);
		CSiaError<_SiaApiErrorCode> Restore(const SString& seed);
		CSiaError<_SiaApiErrorCode> Lock();
    CSiaError<_SiaApiErrorCode> Unlock(const SString& password);
    CSiaError<_SiaApiErrorCode> Send(const SString& address, const SiaCurrency& amount);
	};

	class SIADRIVE_EXPORTABLE _CSiaRenter :
		public virtual CSiaBase
	{
		friend CSiaApi;

	public:
    typedef struct
    {
      SiaCurrency Funds;
      std::uint64_t Hosts;
      std::uint64_t Period;
      std::uint64_t RenewWindowInBlocks;
    } _SiaRenterAllowance;

	private:
		explicit _CSiaRenter(CSiaDriveConfig* siaDriveConfig);

	public:
		virtual ~_CSiaRenter();
		
		Property(SiaCurrency, Funds, public, private)
		Property(std::uint64_t, Hosts, public, private)
		Property(SiaCurrency, Unspent, public, private)
		Property(std::uint64_t, TotalUsedBytes, public, private)
		Property(std::uint32_t, TotalUploadProgress, public, private)
    Property(std::uint64_t, Period, public, private)
    Property(std::uint64_t, RenewWindow, public, private)

	private:
    _SiaRenterAllowance _currentAllowance;
    std::shared_ptr<_CSiaFileTree> _fileTree;
    SiaCurrency _storageterabytemonth;
    SiaCurrency _downloadterabyte;
    SiaCurrency _uploadterabyte;

	private:
		void Refresh();

	public:
    CSiaError<_SiaApiErrorCode> CalculateEstimatedStorage(const SiaCurrency& funds, SiaCurrency& resultInBytes) const;
    CSiaError<_SiaApiErrorCode> CalculateEstimatedStorageCost(SiaCurrency& resultInBytes) const;
    CSiaError<_SiaApiErrorCode> CalculateEstimatedDownloadCost(SiaCurrency& result) const;
    CSiaError<_SiaApiErrorCode> CalculateEstimatedUploadCost(SiaCurrency& result) const;
    CSiaError<_SiaApiErrorCode> DownloadFile(const SString& siaPath, const SString& location) const;
    CSiaError<_SiaApiErrorCode> FileExists(const SString& siaPath, bool& exists) const;
    _SiaRenterAllowance GetAllowance() const;
    CSiaError<_SiaApiErrorCode> GetFileTree(std::shared_ptr<_CSiaFileTree>& siaFileTree) const;
    CSiaError<_SiaApiErrorCode> RefreshFileTree( );
    CSiaError<_SiaApiErrorCode> RenameFile(const SString& siaPath, const SString& newSiaPath);
    CSiaError<_SiaApiErrorCode> RenameFolder(const SString& siaPath, const SString& newSiaPath, std::unordered_map<SString, SString>* renamedMap = nullptr);
    CSiaError<_SiaApiErrorCode> SetAllowance(const _SiaRenterAllowance& renterAllowance);
	};

	class SIADRIVE_EXPORTABLE _CSiaConsensus : 
		public virtual CSiaBase
	{
		friend CSiaApi;

	private:
		explicit _CSiaConsensus(CSiaDriveConfig* siaDriveConfig);

	public:
		virtual ~_CSiaConsensus();

	// Properties
		Property(std::uint64_t, Height, public, private)
		Property(bool, Synced, public, private)
		Property(SString, CurrentBlock, public, private)

	private:
		void Refresh();
	};

  class SIADRIVE_EXPORTABLE _CSiaHost :
    public virtual CSiaBase
  {
    friend CSiaApi;

  private:
    explicit _CSiaHost(CSiaDriveConfig* siaDriveConfig, const json& hostJson);

  public:
    virtual ~_CSiaHost();

  //Properties
    Property(bool, AcceptingContracts, public, private)
    Property(std::uint64_t, MaxDownloadBatchSize, public, private)
    Property(std::uint64_t, MaxDuration, public, private)
    Property(std::uint64_t, MaxReviseBatchSize, public, private)
    Property(SString, NetAddress, public, private)
    Property(std::uint64_t, RemainingStorage, public, private)
    Property(std::uint64_t, SectorSize, public, private)
    Property(std::uint64_t, TotalStorage, public, private)
    Property(SString, UnlockHash, public, private)
    Property(std::uint64_t, WindowSize, public, private)
    Property(std::pair<SString COMMA SString>, PublicKey, public, private)
  };

	class SIADRIVE_EXPORTABLE _CSiaHostDb :
		public virtual CSiaBase
	{
		friend CSiaApi;

	private:
		explicit _CSiaHostDb(CSiaDriveConfig* siaDriveConfig);

	public:
		virtual ~_CSiaHostDb();

	private:
    std::shared_ptr<std::vector<std::shared_ptr<_CSiaHost>>> _activeHosts;

	private:
		void Refresh();

	public:
    std::shared_ptr<std::vector<std::shared_ptr<_CSiaHost>>> GetActiveHosts() const;
	};

public:
	explicit CSiaApi(CSiaDriveConfig* siaDriveConfig);

public:
	~CSiaApi();

private:
	CSiaDriveConfig* _siaDriveConfig;
	std::shared_ptr<_CSiaWallet> _wallet;
	std::shared_ptr<_CSiaRenter> _renter;
  std::shared_ptr<_CSiaConsensus> _consensus;
  std::shared_ptr<_CSiaHostDb> _hostDb;
  std::unique_ptr<CAutoThread> _refreshThread;
  
private:
  void Refresh();

public:
	static SString FormatToSiaPath(SString path);

public:
  void StartBackgroundRefresh();
  void StopBackgroundRefresh();
	std::shared_ptr<_CSiaWallet> GetWallet() const;
	std::shared_ptr<_CSiaRenter> GetRenter() const;
  std::shared_ptr<_CSiaConsensus> GetConsensus() const;
  std::shared_ptr<_CSiaHostDb> GetHostDb() const;
	SString GetServerVersion() const;
};

typedef CSiaApi::_SiaApiErrorCode SiaApiErrorCode;
typedef CSiaError<SiaApiErrorCode> SiaApiError;
typedef CSiaApi::_SiaSeedLanguage SiaSeedLanguage;
typedef CSiaApi::_CSiaWallet CSiaWallet;
typedef CSiaApi::_CSiaRenter CSiaRenter;
typedef CSiaRenter::_SiaRenterAllowance SiaRenterAllowance;
typedef CSiaApi::_CSiaConsensus CSiaConsensus;
typedef CSiaApi::_CSiaHostDb CSiaHostDb;
typedef CSiaApi::_CSiaHost CSiaHost;
typedef std::shared_ptr<CSiaHostDb> CSiaHostDbPtr;
typedef std::shared_ptr<CSiaWallet> CSiaWalletPtr;
typedef std::shared_ptr<CSiaRenter> CSiaRenterPtr;
typedef std::shared_ptr<CSiaConsensus> CSiaConsensusPtr;
typedef std::shared_ptr<CSiaHost> CSiaHostPtr;
typedef std::vector<CSiaHostPtr> CSiaHostCollection;
typedef std::shared_ptr<CSiaHostCollection> CSiaHostCollectionPtr;
typedef CSiaApi::_CSiaFile CSiaFile;
typedef std::shared_ptr<CSiaFile> CSiaFilePtr;
typedef std::vector<CSiaFilePtr> CSiaFileCollection;
typedef std::shared_ptr<CSiaFileCollection> CSiaFileCollectionPtr;
typedef CSiaApi::_CSiaFileTree CSiaFileTree;
typedef std::shared_ptr<CSiaFileTree> CSiaFileTreePtr;
NS_END(2)
#endif //_SIAAPI_H