#include <siaapi.h>
#include <siadriveconfig.h>

using namespace Sia::Api;

static SString SeedLangToString(const SiaSeedLanguage& lang)
{
	switch (lang)
	{
	case SiaSeedLanguage::English:
		return L"english";

	case SiaSeedLanguage::German:
		return L"german";

	case SiaSeedLanguage::Japanese:
		return L"japanese";

	default:
		throw std::exception("Seed language not implemented");
	}
}

CSiaApi::_CSiaWallet::_CSiaWallet(CSiaDriveConfig* siaDriveConfig) :
  CSiaBase(siaDriveConfig),
	_Created(false),
	_Locked(false),
  _Connected(false),
  _ConfirmedBalance(0),
  _UnconfirmedBalance(0)
{
}

CSiaApi::_CSiaWallet::~_CSiaWallet()
{
}

/*{
"encrypted": true,
"unlocked":  true,

"confirmedsiacoinbalance":     "123456", // hastings, big int
"unconfirmedoutgoingsiacoins": "0",      // hastings, big int
"unconfirmedincomingsiacoins": "789",    // hastings, big int

"siafundbalance":      "1",    // siafunds, big int
"siacoinclaimbalance": "9001", // hastings, big int
}*/
void CSiaApi::_CSiaWallet::Refresh( )
{
  bool connected = false;
  {
    bool locked = false;
    bool created = false;
    SiaCurrency unconfirmed = 0;
    SiaCurrency confirmed = 0;
    SString address;

    json result;
    SiaCurlError cerror = CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Get(L"/wallet", result);
    if (ApiSuccess(cerror))
    {
      created = result["encrypted"].get<bool>();
      locked = !result["unlocked"].get<bool>();
      confirmed = HastingsStringToSiaCurrency(result["confirmedsiacoinbalance"].get<std::string>());
      SiaCurrency sc1 = HastingsStringToSiaCurrency(result["unconfirmedincomingsiacoins"].get<std::string>());
      SiaCurrency sc2 = HastingsStringToSiaCurrency(result["unconfirmedoutgoingsiacoins"].get<std::string>());
      unconfirmed = sc1 - sc2;

      cerror = CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Get(L"/wallet/address", result);
      if (ApiSuccess(cerror))
      {
        address = result["address"].get<std::string>();
      }
      connected = true;
    }

    SetCreated(created);
    SetLocked(locked);
    SetConfirmedBalance(confirmed);
    SetUnconfirmedBalance(unconfirmed);
    SetReceiveAddress(address);
  }

  {
    if (connected)
    {
      json result;
      SiaCurlError cerror = CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Get(L"/wallet/transactions", { { "startheight", "1" },{ "endheight", "99999999" } }, result);
      if (ApiSuccess(cerror))
      {
        if (result.find("unconfirmedtransactions") != result.end())
        {
          for (auto tx : result["unconfirmedtransactions"])
          {
          }
        }

        if (result.find("confirmedtransactions") != result.end())
        {
          for (auto tx : result["confirmedtransactions"])
          {
            json j(tx);
          }
        }
      }
    }
  }

  // Update connected status last so all properties are current
  SetConnected(connected);
}

SiaApiError CSiaApi::_CSiaWallet::Create(const SString& password, const SiaSeedLanguage& seedLanguage, SString& seed)
{
  SiaApiError error = SiaApiErrorCode::NotConnected;
	if (GetConnected())
	{
		if (GetCreated())
		{
		  error = SiaApiErrorCode::WalletExists;
		}
    else
		{
			json result;
			SiaCurlError cerror = password.IsNullOrEmpty() ? 
        CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Post(L"/wallet/init", { {L"dictionary", SeedLangToString(seedLanguage)} }, result) :
        CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Post(L"/wallet/init", { { L"dictionary", SeedLangToString(seedLanguage) }, {L"encryptionpassword", password } }, result);
			if (ApiSuccess(cerror))
			{
				error = SiaApiErrorCode::Success;
				seed = result["primaryseed"].get<std::string>();
			}
      else
      {
        error = { SiaApiErrorCode::RequestError, cerror.GetReason() };
      }
		}
	}

	return error;
}

SiaApiError CSiaApi::_CSiaWallet::Restore(const SString& seed)
{
	SiaApiError error = SiaApiErrorCode::NotImplemented;
	// TODO Future enhancement
	return error;
}

SiaApiError CSiaApi::_CSiaWallet::Lock()
{
	SiaApiError error = GetCreated() ? (GetLocked() ? SiaApiErrorCode::WalletLocked : SiaApiErrorCode::Success) : SiaApiErrorCode::WalletNotCreated;
	if (ApiSuccess(error))
	{
		json result;
		SiaCurlError cerror = CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Post(L"/wallet/lock", {}, result);
		if (ApiSuccess(cerror))
		{
			error = SiaApiErrorCode::Success;
		}
    else
    {
      error = { SiaApiErrorCode::RequestError, cerror.GetReason() };
    }
	}

	return error;
}

SiaApiError CSiaApi::_CSiaWallet::Unlock(const SString& password)
{
	SiaApiError error = GetCreated() ? (GetLocked() ? SiaApiErrorCode::Success : SiaApiErrorCode::WalletUnlocked) : SiaApiErrorCode::WalletNotCreated;
	if (ApiSuccess(error))
	{
		json result;
		SiaCurlError cerror = CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Post(L"/wallet/unlock", { {L"encryptionpassword", password} }, result);
		if (ApiSuccess(cerror))
		{
			error = SiaApiErrorCode::Success;
		}
    else
    {
      error = { SiaApiErrorCode::RequestError, cerror.GetReason() };
    }
	}

	return error;
}

SiaApiError CSiaApi::_CSiaWallet::Send(const SString& address, const SiaCurrency& amount)
{
  SiaApiError error = SiaApiErrorCode::Success;
  const SString hast = SiaCurrencyToHastingsString(amount);
	json result;
	SiaCurlError cerror = CSiaCurl(GetSiaDriveConfig().GetHostConfig()).Post(L"/wallet/siacoins", { {L"destination", address}, { L"amount", hast} }, result);
	if (ApiSuccess(cerror))
	{
		error = SiaApiErrorCode::Success;
	}
  else
  {
    error = { SiaApiErrorCode::RequestError, cerror.GetReason() };
  }

	return error;
}