contract GoldTokenStorage is ResolverClient, DigixConstants {

  struct FeeConfiguration {
    uint256 base;
    uint256 rate;
  }

  struct GlobalConfig {
    bytes32 current_version;
    bool no_demurrage_fee;
    bool no_transfer_fee;
    uint256 minimum_transfer_amount;
    Fees fees;
  }

  struct Fees {
    FeeConfiguration demurrage;
    FeeConfiguration recast;
    FeeConfiguration transfer;
  }

  struct Collectors {
    address demurrage;
    address recast;
    address transfer;
  }

  struct UserConfig {
    bool no_demurrage_fee;
    bool no_transfer_fee;
    bool no_recast_fee;
  }

  struct UserData {
    uint256 last_payment_date;
    uint256 raw_balance;
    mapping (address => uint256) spender_allowances;
  }

  struct User {
    UserConfig config;
    UserData data;
  }

  struct System {
    Collectors collectors;
    GlobalConfig config;
    uint256 total_supply;
    uint256 effective_total_supply;
    mapping (address => User) users;
  }

  System system;

  function GoldTokenStorage(address _resolver) public
  {
    require(init(CONTRACT_STORAGE_GOLD_TOKEN, _resolver));

    address _demurrage_collector;
    address _transfer_collector;
    address _recast_collector;

    assembly {
      _demurrage_collector := create(0,0,0)
      _transfer_collector := create(0,0,0)
      _recast_collector := create(0,0,0)
    }
    system.collectors.demurrage = _demurrage_collector;
    system.collectors.recast = _recast_collector;
    system.collectors.transfer = _transfer_collector;
    system.config.fees.demurrage.base = 10000000;
    system.config.fees.demurrage.rate = 165;
    system.config.fees.recast.base = 100000000000;
    system.config.fees.recast.rate = 1000000000;
    system.config.fees.transfer.base = 10000;
    system.config.fees.transfer.rate = 13;
    system.config.minimum_transfer_amount = 1000000;
    system.config.no_demurrage_fee = false;
    system.config.no_transfer_fee = false;
    system.config.current_version = "1.0.0";
    system.total_supply = 0;
    system.effective_total_supply = 0;
  }


 /////////////////////////////////////////////////////
///    functions to read global configs           ///
/////////////////////////////////////////////////////

  /// @notice read the total number of tokens
  function read_total_supply()
           constant
           public
           returns (uint256 _total_supply)
  {
    _total_supply = system.total_supply;
  }

  /// @notice read the effective total, which is the number of nanograms of gold that is backing the Gold tokens
  function read_effective_total_supply()
           constant
           public
           returns (uint256 _effective_total_supply)
  {
    _effective_total_supply = system.effective_total_supply;
  }

  /// @notice read both the total_supply and effective_total_supply
  function read_supply()
           constant
           public
           returns (uint256 _total_supply, uint256 _effective_total_supply)
  {
    _total_supply = read_total_supply();
    _effective_total_supply = read_effective_total_supply();
  }

  /// @notice read general global configs: no_demurrage_fee, no_transfer_fee, minimum_transfer_amount, current_version
  function read_general_config()
           constant
           public
           returns (bytes32 _current_version, bool _no_demurrage_fee, bool _no_transfer_fee, uint256 _minimum_transfer_amount)
  {
    _current_version = system.config.current_version;
    _no_demurrage_fee = system.config.no_demurrage_fee;
    _no_transfer_fee = system.config.no_transfer_fee;
    _minimum_transfer_amount = system.config.minimum_transfer_amount;
  }

  function read_collectors_addresses()
           constant
           public
           returns (address[3] _collectors)
  {
    /* _collectors = new address[](3); */
    _collectors[0] = system.collectors.demurrage;
    _collectors[1] = system.collectors.recast;
    _collectors[2] = system.collectors.transfer;
  }


 ///////////////////////////////////////////////
///    functions to read users' configs     ///
///////////////////////////////////////////////

  /// @notice read all details of a user
  function read_user(address _account)
           public
           constant
           returns (bool _exists,
                    uint256 _raw_balance,
                    uint256 _payment_date,
                    bool _no_demurrage_fee,
                    bool _no_recast_fee,
                    bool _no_transfer_fee)
  {
    _exists = (system.users[_account].data.last_payment_date > 0);
    _raw_balance = system.users[_account].data.raw_balance;
    _payment_date = system.users[_account].data.last_payment_date;
    (_no_demurrage_fee, _no_transfer_fee, _no_recast_fee) = read_user_fees_configs(_account);
  }

  function read_user_fees_configs(address _account)
           public
           constant
           returns (bool _no_demurrage_fee,
                    bool _no_transfer_fee,
                    bool _no_recast_fee)
  {
    _no_demurrage_fee = system.users[_account].config.no_demurrage_fee;
    _no_transfer_fee = system.users[_account].config.no_transfer_fee;
    _no_recast_fee = system.users[_account].config.no_recast_fee;
  }

  /// @notice read a user's spender allowance
  function read_account_spender_allowance(address _account,
                                          address _spender)
           public
           constant
           returns (uint256 _spender_allowance)
  {
    _spender_allowance = system.users[_account].data.spender_allowances[_spender];
  }


 //////////////////////////////////////////////////
///    Update functions regarding users        ///
//////////////////////////////////////////////////

  /// @notice called by TokenApprovalController to update an account's spender allowance of a _spender
  function update_account_spender_allowance(address _account,
                                            address _spender,
                                            uint256 _new_allowance)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_APPROVAL)
           public
           returns (bool _success)
  {
    system.users[_account].data.spender_allowances[_spender] = _new_allowance;
    _success = true;
  }

  /// @notice update the _user balance and the global total supply after minting some tokens
  function update_balances_after_mint(address _user, uint256 _user_new_balance, uint256 _new_total_supply, uint256 _new_effective_total_supply)
           if_sender_is(CONTRACT_CONTROLLER_ASSETS)
           public
           returns (bool _success)
  {
    system.users[_user].data.raw_balance = _user_new_balance;
    system.total_supply = _new_total_supply;
    system.effective_total_supply = _new_effective_total_supply;
    _success = true;
  }

  /// @notice update a user's fees configs
  function update_user_fees_configs(address _user, bool _no_demurrage_fee, bool _no_transfer_fee, bool _no_recast_fee)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_CONFIG)
           public
           returns (bool _success)
  {
    system.users[_user].config.no_demurrage_fee = _no_demurrage_fee;
    system.users[_user].config.no_transfer_fee = _no_transfer_fee;
    system.users[_user].config.no_recast_fee = _no_recast_fee;
    _success = true;
  }


 ///////////////////////////////////////////////////////////
///    Update functions to change global configs        ///
///////////////////////////////////////////////////////////

  /// @notice called by AssetsController to update the effective supply (when an item failed audit or is replaced)
  function update_effective_supply(uint256 _effective_total_supply)
           if_sender_is(CONTRACT_CONTROLLER_ASSETS)
           public
           returns (bool _success)
  {
    system.effective_total_supply = _effective_total_supply;
    _success = true;
  }

  /// @notice update configs for recast fees
  function update_config_recast(uint256 _base, uint256 _rate)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_CONFIG)
           public
           returns (bool _success)
  {
    system.config.fees.recast.base = _base;
    system.config.fees.recast.rate = _rate;
    _success = true;
  }

  /// @notice update configs for demurrage fees
  function update_config_demurrage(uint256 _base, uint256 _rate, bool _no_demurrage_fee)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_CONFIG)
           public
           returns (bool _success)
  {
    system.config.fees.demurrage.base = _base;
    system.config.fees.demurrage.rate = _rate;
    system.config.no_demurrage_fee = _no_demurrage_fee;
    _success = true;
  }

  /// @notice update configs for transfer fees
  function update_config_transfer(uint256 _base, uint256 _rate, bool _no_transfer_fee, uint256 _minimum_transfer_amount)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_CONFIG)
           public
           returns (bool _success)
  {
    system.config.fees.transfer.base = _base;
    system.config.fees.transfer.rate = _rate;
    system.config.no_transfer_fee = _no_transfer_fee;
    system.config.minimum_transfer_amount = _minimum_transfer_amount;
    _success = true;
  }


 /////////////////////////////////////////////
///    Demurrage-related functions        ///
/////////////////////////////////////////////

  /// @notice called by TokenDemurrageService to get the global demurrage configs
  function read_demurrage_config()
           constant
           public
           returns (uint256 _collector_balance,
                    uint256 _base,
                    uint256 _rate,
                    address _collector)
  {
    _collector_balance = system.users[system.collectors.demurrage].data.raw_balance;
    bool _global_demurrage_disabled = system.config.no_demurrage_fee;
    _collector = system.collectors.demurrage;

    if (_global_demurrage_disabled) {
      _base = 0;
      _rate = 0;
    } else {
      _base = system.config.fees.demurrage.base;
      _rate = system.config.fees.demurrage.rate;
    }
  }

  function read_demurrage_config_underlying()
           public
           constant
           returns (uint256 _base,
                    uint256 _rate,
                    address _collector,
                    bool _no_demurrage_fee)
  {
    _base = system.config.fees.demurrage.base;
    _rate = system.config.fees.demurrage.rate;
    _collector = system.collectors.demurrage;
    _no_demurrage_fee = system.config.no_demurrage_fee;
  }

  /// @notice called by TokenDemurrageService to get the user's information needed to deduct his demurrage fees
  function read_user_for_demurrage(address _account)
           public
           constant
           returns (uint256 _raw_balance, uint256 _payment_date, bool _no_demurrage_fee)
  {
    _raw_balance = system.users[_account].data.raw_balance;
    _payment_date = system.users[_account].data.last_payment_date;
    _no_demurrage_fee = system.users[_account].config.no_demurrage_fee || system.config.no_demurrage_fee;
  }

  /// @notice called by TokenDemurrageService to deduct demurrage fees from a user's balance
  function update_user_for_demurrage(address _user, uint256 _user_new_balance, uint256 _user_new_payment_date, uint256 _collector_new_balance)
           if_sender_is(CONTRACT_SERVICE_TOKEN_DEMURRAGE)
           public
           returns (bool _success)
  {
    system.users[system.collectors.demurrage].data.raw_balance = _collector_new_balance;
    system.users[_user].data.raw_balance = _user_new_balance;
    system.users[_user].data.last_payment_date = _user_new_payment_date;
    _success = true;
  }


 ////////////////////////////////////////
///    Recast-related functions      ///
////////////////////////////////////////

  /// @notice called by AssetsController to read global info to recast an asset item
  function read_recast_config()
           constant
           public
           returns (uint256 _base,
                    uint256 _rate,
                    uint256 _total_supply,
                    uint256 _effective_total_supply,
                    address _collector,
                    uint256 _collector_balance)
  {
    _base = system.config.fees.recast.base;
    _rate = system.config.fees.recast.rate;
    _total_supply = system.total_supply;
    _effective_total_supply = system.effective_total_supply;
    _collector = system.collectors.recast;
    _collector_balance = system.users[system.collectors.recast].data.raw_balance;
  }

  /// @notice called by AssetsController to read a user's configs for recasting assets
  function read_user_for_recast(address _account)
           public
           constant
           returns (uint256 _raw_balance, bool _no_recast_fee)
  {
    _raw_balance = system.users[_account].data.raw_balance;
    _no_recast_fee = system.users[_account].config.no_recast_fee;
  }

  /// @notice called by AssetsController to recast an asset item
  function update_balances_after_recast(address _recaster,
                               uint256 _recaster_new_balance,
                               uint256 _recast_fee_collector_new_balance,
                               uint256 _new_total_supply,
                               uint256 _new_effective_total_supply)
           if_sender_is(CONTRACT_CONTROLLER_ASSETS_RECAST)
           public
           returns (bool _success)
  {
    system.users[_recaster].data.raw_balance = _recaster_new_balance;
    system.users[system.collectors.recast].data.raw_balance = _recast_fee_collector_new_balance;
    system.total_supply = _new_total_supply;
    system.effective_total_supply = _new_effective_total_supply;
    _success = true;
  }


 ////////////////////////////////////////
///    Transfer-related functions    ///
////////////////////////////////////////

  /// @notice called by TokenTransferController to read global configs for transfering tokens
  function read_transfer_config()
           public
           constant
           returns (uint256 _collector_balance,
                    uint256 _base,
                    uint256 _rate,
                    address _collector,
                    bool _no_transfer_fee,
                    uint256 _minimum_transfer_amount)
  {
    _collector_balance = system.users[system.collectors.transfer].data.raw_balance;
    _base = system.config.fees.transfer.base;
    _rate = system.config.fees.transfer.rate;
    _collector = system.collectors.transfer;
    _no_transfer_fee = system.config.no_transfer_fee;
    _minimum_transfer_amount = system.config.minimum_transfer_amount;
  }

  /// @notice called by TokenTransferController to read a user's configs for tranfering tokens
  function read_user_for_transfer(address _account)
           public
           constant
           returns (uint256 _raw_balance, bool _no_transfer_fee)
  {
    _raw_balance = system.users[_account].data.raw_balance;
    _no_transfer_fee = system.users[_account].config.no_transfer_fee;
  }

  /// @notice called by TokenTransferController to update user balances after transfering
  function update_transfer_balance(address _sender, uint256 _sender_new_balance, address _recipient,
                                   uint256 _recipient_new_balance, uint256 _transfer_fee_collector_new_balance)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_TRANSFER)
           public
           returns (bool _success)
  {
    system.users[_sender].data.raw_balance = _sender_new_balance;
    system.users[_recipient].data.raw_balance = _recipient_new_balance;
    system.users[system.collectors.transfer].data.raw_balance = _transfer_fee_collector_new_balance;
    _success = true;
  }

  /// @notice called by TokenTransferController to update balances after transfering from
  function update_transfer_from_balance(address _sender, uint256 _sender_new_balance, address _recipient,
                                        uint256 _recipient_new_balance, uint256 _transfer_fee_collector_new_balance,
                                        address _spender, uint256 _spender_new_allowance)
           if_sender_is(CONTRACT_CONTROLLER_TOKEN_TRANSFER)
           public
           returns (bool _success)
  {
    system.users[_sender].data.raw_balance = _sender_new_balance;
    system.users[_recipient].data.raw_balance = _recipient_new_balance;
    system.users[system.collectors.transfer].data.raw_balance = _transfer_fee_collector_new_balance;
    system.users[_sender].data.spender_allowances[_spender] = _spender_new_allowance;
    _success = true;
  }

  ////////////////////////////////////////
 ///    transfer fees to distributors  ///
 ////////////////////////////////////////
  function internal_move_balance(address _from, address _to)
           internal
           returns (uint256 _fees)
  {
    _fees = system.users[_from].data.raw_balance;
    system.users[_to].data.raw_balance += _fees;
    system.users[_from].data.raw_balance = 0;
  }

  function move_fees_to_distributors(address _demurrage_fees_distributor, address _recast_fees_distributor, address _transfer_fees_distributor)
          if_sender_is(CONTRACT_CONTROLLER_TOKEN_CONFIG)
          public
          returns (bool _success, uint256[3] _fees_array)
  {
    /* _fees_array = new uint256[](3); */
    _fees_array[0] = internal_move_balance(system.collectors.demurrage, _demurrage_fees_distributor);
    _fees_array[1] = internal_move_balance(system.collectors.recast, _recast_fees_distributor);
    _fees_array[2] = internal_move_balance(system.collectors.transfer, _transfer_fees_distributor);
    _success = true;
  }

}
function read_user_for_recast(address _account)
         public
         constant
         returns (uint256 _raw_balance, bool _no_recast_fee)
{
  _raw_balance = system.users[_account].data.raw_balance;
  _no_recast_fee = system.users[_account].config.no_recast_fee;
}

/// @notice called by AssetsController to read a user's configs for recasting assets