contract MarketplaceStorage is ResolverClient, DigixConstants {

  struct Config {
    uint256 global_daily_dgx_ng_limit;
    uint256 minimum_purchase_dgx_ng;
    uint256 maximum_block_drift;
    address payment_collector;
    uint256 max_dgx_available_daily;
    uint256 price_floor_wei_per_dgx_mg;
  }

  struct Purchase {
    address recipient; // the recipient is not necessarily the user who purchased
    uint256 timestamp;
    uint256 amount;
    uint256 price;
  }

  struct User {
    uint256 overwrite_daily_dgx_ng_limit;
    Purchase[] purchases;
  }

  struct Marketplace {
    Config config;
    Purchase[] purchases;
    mapping (address => User) users;
    mapping (address => bool) approved_signers;
    mapping (uint => mapping (uint => bool)) used_nonces;
  }

  Marketplace marketplace;

  function MarketplaceStorage(address _resolver) public
  {
    require(init(CONTRACT_STORAGE_MARKETPLACE, _resolver));
    marketplace.config.global_daily_dgx_ng_limit = 102000000000;
    marketplace.config.minimum_purchase_dgx_ng = 10000000;
  }

  function token_contract()
           internal
           constant
           returns (ERCTwenty _contract)
  {
    _contract = ERCTwenty(get_contract(CONTRACT_INTERACTIVE_TOKEN));
  }

  function read_total_number_of_purchases()
           public
           constant
           returns (uint256 _total_number_of_purchases)
  {
    _total_number_of_purchases = marketplace.purchases.length;
  }

  function read_total_number_of_user_purchases(address _user)
           public
           constant
           returns (uint256 _total_number_of_user_purchases)
  {
    _total_number_of_user_purchases = marketplace.users[_user].purchases.length;
  }

  function read_purchase_at_index(uint256 _index)
           public
           constant
           returns (address _recipient, uint256 _timestamp, uint256 _amount, uint256 _price)
  {
    Purchase memory _purchase = marketplace.purchases[_index];
    _recipient = _purchase.recipient;
    _timestamp = _purchase.timestamp;
    _amount = _purchase.amount;
    _price = _purchase.price;
  }

  function read_user_purchase_at_index(address _user, uint256 _index)
           public
           constant
           returns (address _recipient, uint256 _timestamp, uint256 _amount, uint256 _price)
  {
    Purchase memory _purchase = marketplace.users[_user].purchases[_index];
    _recipient = _purchase.recipient;
    _timestamp = _purchase.timestamp;
    _amount = _purchase.amount;
    _price = _purchase.price;
  }

  function create_user(address _user, uint256 _overwrite_daily_dgx_ng_limit)
           if_sender_is(CONTRACT_CONTROLLER_IDENTITY)
           public
           returns (bool _success)
  {
    marketplace.users[_user].overwrite_daily_dgx_ng_limit = _overwrite_daily_dgx_ng_limit;
    _success = true;
  }

  function update_user_purchase(address _recipient_user, address _buyer, uint256 _purchase_amount, uint256 _purchase_price, uint256 _block_number, uint256 _nonce)
           if_sender_is(CONTRACT_CONTROLLER_MARKETPLACE)
           public
           returns (bool _success)
  {
    marketplace.used_nonces[_block_number][_nonce] = true;
    Purchase memory _purchase;
    _purchase.recipient = _recipient_user;
    _purchase.timestamp = now;
    _purchase.amount = _purchase_amount;
    _purchase.price = _purchase_price;
    marketplace.users[_buyer].purchases.push(_purchase);
    marketplace.purchases.push(_purchase);
    require(token_contract().transfer(_recipient_user, _purchase_amount) == true);
    _success = true;
  }

  function update_config(uint256 _global_daily_dgx_ng_limit, uint256 _minimum_purchase_dgx_ng, uint256 _maximum_block_drift, address _payment_collector)
           if_sender_is(CONTRACT_CONTROLLER_MARKETPLACE_ADMIN)
           public
           returns (bool _success)
  {
    marketplace.config.global_daily_dgx_ng_limit = _global_daily_dgx_ng_limit;
    marketplace.config.minimum_purchase_dgx_ng = _minimum_purchase_dgx_ng;
    marketplace.config.payment_collector = _payment_collector;
    marketplace.config.maximum_block_drift = _maximum_block_drift;
    _success = true;
  }

  function move_inventory(address _destination, uint256 _amount)
           if_sender_is(CONTRACT_CONTROLLER_MARKETPLACE_ADMIN)
           public
           returns (bool _success)
  {
    require(token_contract().transfer(_destination, _amount));
    _success = true;
  }

  function update_max_dgx_available_daily(uint256 _max_dgx_available_daily)
           if_sender_is(CONTRACT_CONTROLLER_MARKETPLACE_ADMIN)
           public
           returns (bool _success)
  {
    marketplace.config.max_dgx_available_daily = _max_dgx_available_daily;
    _success = true;
  }

  function read_max_dgx_available_daily()
           public
           constant
           returns (uint256 _max_dgx_available_daily)
  {
    _max_dgx_available_daily = marketplace.config.max_dgx_available_daily;
  }

  function update_price_floor(uint256 _price_floor_wei_per_dgx_mg)
           if_sender_is(CONTRACT_CONTROLLER_MARKETPLACE_ADMIN)
           public
           returns (bool _success)
  {
    marketplace.config.price_floor_wei_per_dgx_mg = _price_floor_wei_per_dgx_mg;
    _success = true;
  }

  function read_price_floor()
           public
           constant
           returns (uint256 _price_floor_wei_per_dgx_mg)
  {
    _price_floor_wei_per_dgx_mg = marketplace.config.price_floor_wei_per_dgx_mg;
  }

  function update_signer_approval(address _signer, bool _approve)
           if_sender_is(CONTRACT_CONTROLLER_MARKETPLACE_ADMIN)
           public
           returns (bool _success)
  {
    marketplace.approved_signers[_signer] = _approve;
    _success = true;
  }

  function read_config()
           public
           constant
           returns (uint256 _global_daily_dgx_ng_limit, uint256 _minimum_purchase_dgx_ng, uint256 _maximum_block_drift, address _payment_collector)
  {
    _global_daily_dgx_ng_limit = marketplace.config.global_daily_dgx_ng_limit;
    _minimum_purchase_dgx_ng = marketplace.config.minimum_purchase_dgx_ng;
    _maximum_block_drift = marketplace.config.maximum_block_drift;
    _payment_collector = marketplace.config.payment_collector;
  }

  function read_is_approved_signer(address _signer)
           public
           constant
           returns (bool _yes)
  {
    _yes = marketplace.approved_signers[_signer];
  }

  function read_total_purchased_today(address _user)
           public
           constant
           returns (uint256 _total_purchased_today)
  {
    _total_purchased_today = 0;
    uint256 _i = marketplace.users[_user].purchases.length;
    while((_i >= 1) && (scope_timestamp_is_from_today(marketplace.users[_user].purchases[_i - 1].timestamp) == true)) {
      _total_purchased_today += marketplace.users[_user].purchases[_i - 1].amount;
      _i--;
    }
  }

  function read_total_global_purchased_today()
           public
           constant
           returns (uint256 _total_global_purchased_today)
  {
    _total_global_purchased_today = 0;
    uint256 _i = marketplace.purchases.length;
    while((_i >= 1) && (scope_timestamp_is_from_today(marketplace.purchases[_i - 1].timestamp) == true)) {
      _total_global_purchased_today += marketplace.purchases[_i - 1].amount;
      _i--;
    }
  }

  function read_dgx_inventory_balance_ng()
           public
           constant
           returns (uint256 _balance)
  {
    _balance = token_contract().balanceOf(address(this));
  }

  function scope_timestamp_is_from_today(uint256 _timestamp)
           public
           constant
           returns (bool _yes)
  {
    _yes = (_timestamp >= (now - 1 days));
  }

  function read_for_purchase(address _user, uint256 _block_number, uint256 _nonce, address _signer)
           public
           constant
           returns (uint256 _daily_dgx_limit, uint256 _total_purchased_today,
                    bool _used_nonce, bool _approved_signer)
  {
    _daily_dgx_limit = read_user_daily_limit(_user);
    _used_nonce = marketplace.used_nonces[_block_number][_nonce];
    _approved_signer = marketplace.approved_signers[_signer];
    _total_purchased_today = read_total_purchased_today(_user);
  }

  function read_user(address _user)
           public
           constant
           returns (uint256 _daily_dgx_limit, uint256 _total_purchased_today)
  {
    _daily_dgx_limit = read_user_daily_limit(_user);
    _total_purchased_today = read_total_purchased_today(_user);
  }

  /// user daily limit = max(user's overwrite_daily_dgx_ng_limit, global_daily_dgx_ng_limit)
  function read_user_daily_limit(address _user)
           public
           constant
           returns (uint256 _daily_dgx_limit)
  {
    _daily_dgx_limit = marketplace.users[_user].overwrite_daily_dgx_ng_limit;
    if (_daily_dgx_limit < marketplace.config.global_daily_dgx_ng_limit) {
      _daily_dgx_limit = marketplace.config.global_daily_dgx_ng_limit;
    }
  }

  function read_user_purchases_count(address _user)
           public
           constant
           returns (uint256 _count)
  {
    _count = marketplace.users[_user].purchases.length;
  }

  function read_user_purchase_details(address _user, uint256 _index)
           public
           constant
           returns (address _recipient, uint256 _timestamp, uint256 _amount, uint256 _price)
  {
    _recipient = marketplace.users[_user].purchases[_index].recipient;
    _timestamp = marketplace.users[_user].purchases[_index].timestamp;
    _amount = marketplace.users[_user].purchases[_index].amount;
    _price = marketplace.users[_user].purchases[_index].price;
  }
}
// Contracts that need to be deployed for this contract to work:
//    DoublyLinkedList, CoreStorage, MathUtils, TokenTransferController, TokenInfoController,
//    TokenDemurrageService, TokenApprovalController, Token, ContractResolver
function read_user_daily_limit(address _user)
         public
         constant
         returns (uint256 _daily_dgx_limit)
{
  _daily_dgx_limit = marketplace.users[_user].overwrite_daily_dgx_ng_limit;
  if (_daily_dgx_limit < marketplace.config.global_daily_dgx_ng_limit) {
    _daily_dgx_limit = marketplace.config.global_daily_dgx_ng_limit;
  }
}

/// user daily limit = max(user's overwrite_daily_dgx_ng_limit, global_daily_dgx_ng_limit)