contract BLBalances is BLStorage {

    event WithdrawBalance(address indexed owner, uint amount);
    event AllowanceGranted(address indexed owner, uint _amount);
    event SentFeeToPlatform(uint amount);
    event SentAmountToOwner(uint amount, address indexed owner);
    event BonusGranted(address _beneficiary, uint _amount);
    event SentAmountToNeighbours(uint reward, address indexed owner);

    // get the balance for a given account
    function getBalance() public view returns (uint) {
        return s.getUInt(keccak256(msg.sender, "balance"));
    }

    // get the balance for a given account
    function getAccountBalance(address _account) public view onlyPrimary returns (uint) {
        return s.getUInt(keccak256(_account, "balance"));
    }

    function getAccountAllowance(address _account) public view onlyPrimary returns (uint) {
        return s.getUInt(keccak256(_account, "promoAllowance"));
    }

    function getMyAllowance() public view returns (uint) {
        return s.getUInt(keccak256(msg.sender, "promoAllowance"));
    }

    // IF a block has been assigned a bonus, provude the bonus to the next buyer.
    function giveBonusIfExists(uint x, uint y) internal {
      bytes32 key = getKey(x, y);
      uint bonus = s.getUInt(keccak256(key, "bonus"));
      uint balance = s.getUInt(keccak256(msg.sender, "balance"));
      uint total = balance + bonus;
      s.setUInt(keccak256(msg.sender, "balance"), total);
      s.setUInt(keccak256(key, "bonus"), 0);
      if (bonus > 0) {
        BonusGranted(msg.sender, bonus);
      }
    }

    // allow a block allowance for promo and early beta users
    function grantAllowance(address beneficiary, uint allowance) public onlyPrimary {
        uint existingAllowance = s.getUInt(keccak256(beneficiary, "promoAllowance"));
        existingAllowance += allowance;
        s.setUInt(keccak256(beneficiary, "promoAllowance"), existingAllowance);
        AllowanceGranted(beneficiary, allowance);
    }

    // withdraw the current balance
    function withdraw() public {
        uint balance = s.getUInt(keccak256(msg.sender, "balance"));
        s.withdraw(msg.sender);
        WithdrawBalance(msg.sender, balance);
    }

    // Trading and buying balances flow
    function rewardParties (uint x, uint y, uint feePercentage) internal {
        uint fee = msg.value * feePercentage / 100;
        uint remainder = msg.value - fee;
        uint rewardPct = s.getUInt("neighbourRewardPercentage");
        uint toOwner = remainder - (remainder * rewardPct * 8 / 100);
        rewardContract(fee);
        rewardPreviousOwner(x, y, toOwner);
        rewardNeighbours(x, y, remainder, rewardPct);
    }

    function rewardNeighbours (uint x, uint y, uint remainder, uint rewardPct) internal {
        uint rewardAmount = remainder * rewardPct / 100;
      address nw = s.getAdd(keccak256(keccak256(x-1, ":", y-1), "owner"));
      address n = s.getAdd(keccak256(keccak256(x-1, ":", y), "owner"));
      address ne = s.getAdd(keccak256(keccak256(x-1, ":", y+1), "owner"));
      address w = s.getAdd(keccak256(keccak256(x, ":", y-1), "owner"));
      address e = s.getAdd(keccak256(keccak256(x, ":", y+1), "owner"));
      address sw = s.getAdd(keccak256(keccak256(x+1, ":", y-1), "owner"));
      address south = s.getAdd(keccak256(keccak256(x+1, ":", y), "owner"));
      address se = s.getAdd(keccak256(keccak256(x+1, ":", y+1), "owner"));
      nw != address(0) ? rewardBlock(nw, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      n != address(0) ? rewardBlock(n, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      ne != address(0) ? rewardBlock(ne, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      w != address(0) ? rewardBlock(w, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      e != address(0) ? rewardBlock(e, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      sw != address(0) ? rewardBlock(sw, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      south != address(0) ? rewardBlock(south, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
      se != address(0) ? rewardBlock(se, rewardAmount) : rewardBlock(bonusAddress, rewardAmount);
    }

    function rewardBlock(address account, uint reward) internal {
      uint balance = s.getUInt(keccak256(account, "balance"));
      balance += reward;
      s.setUInt(keccak256(account, "balance"), balance);
      SentAmountToNeighbours(reward,account);
    }

    // contract commissions
    function rewardContract (uint fee) internal {
        uint mainBalance = s.getUInt(keccak256(mainAddress, "balance"));
        mainBalance += fee;
        s.setUInt(keccak256(mainAddress, "balance"), mainBalance);
        SentFeeToPlatform(fee);
    }

    // reward the previous owner of the block or the contract if the block is bought for the first time
    function rewardPreviousOwner (uint x, uint y, uint amount) internal {
        uint rewardBalance;
        bytes32 key = getKey(x, y);
        address owner = s.getAdd(keccak256(key, "owner"));
        if (owner == address(0)) {
            rewardBalance = s.getUInt(keccak256(mainAddress, "balance"));
            rewardBalance += amount;
            s.setUInt(keccak256(mainAddress, "balance"), rewardBalance);
            SentAmountToOwner(amount, mainAddress);
        } else {
            rewardBalance = s.getUInt(keccak256(owner, "balance"));
            rewardBalance += amount;
            s.setUInt(keccak256(owner, "balance"), rewardBalance);
            SentAmountToOwner(amount, owner);
        }
    }
}
function grantAllowance(address beneficiary, uint allowance) public onlyPrimary {
    uint existingAllowance = s.getUInt(keccak256(beneficiary, "promoAllowance"));
    existingAllowance += allowance;
    s.setUInt(keccak256(beneficiary, "promoAllowance"), existingAllowance);
    AllowanceGranted(beneficiary, allowance);
}

// allow a block allowance for promo and early beta users