contract SuperOneSpots is User {

    event Withdrawal(address wallet, uint256 weiAmount);
    event CommissionSent(address indexed from, address to, uint256 amount);

    // coordinates to Spot index first is level(x) then mapping of y to owner address
    mapping(uint256 => mapping(uint256 => address)) public coordinates;

    // mapping to return what to add according to x index
    mapping(uint256 => uint256) public x_addition;

    // mapping to return what to add according to y index
    mapping(uint256 => uint256) public y_addition;

    // Constructor to bootstartp the contract
    constructor() public {
        // initial coordinates belongs to owner
        coordinates[0][0] = msg.sender;
        uint256 x_factor = 1;
        uint256 y_factor;
        //pre fill x_addition for level calculation in the tree
        for (uint256 i; i < 128; i++) {
            // for x_addition
            if ((i + 2) >= (2 * (2 ** x_factor))) {
                x_factor += 1;
                y_factor = 1;
            } else {
                y_factor += 1;
            }
            x_addition[i] = x_factor;
            y_addition[i] = y_factor - 1;
        }

    }

    /**
     * @dev method to assign spots, can be called by admin only
     * @param coord_x coordinate x of the tree to start
     * @param coord_y coordinate y of the tree to start
     * @param _count count of spots to be assigned
     */
    function assignSpotsByAdmin(uint256 coord_x, uint256 coord_y, uint _count, address external_wallet_address) external onlyOwner() {
        require(_count < 128);
        coordinates[coord_x][coord_y] = external_wallet_address;
        for (uint256 i; i < _count - 1; i++) {
            coordinates[coord_x + x_addition[i]][coord_y * (2 * (2 ** (x_addition[i] - 1))) + y_addition[i]] = external_wallet_address;
        }
    }

    /**
     * @dev method to assign spots, can be called by internal wallets only
     * @param coord_x coordinate x of the tree to start
     * @param coord_y coordinate y of the tree to start
     * @param _count count of spots to be assigned
     */
    function assignSpots(uint256 coord_x, uint256 coord_y, uint _count) external onlyInternalWallets() {
        require(_count < 128);
        address wallet = internal_to_external[msg.sender];
        coordinates[coord_x][coord_y] = wallet;
        for (uint256 i; i < _count - 1; i++) {
            coordinates[coord_x + x_addition[i]][coord_y * (2 * (2 ** (x_addition[i] - 1))) + y_addition[i]] = wallet;
        }
    }

    /**
     * @dev method to distribute Comission, can be called by internal wallets only
     * @param beneficiaries address to which funds will be transferred
     * @param amounts the amount of index wise benificiaries
     */
    function distributeCommission(address payable[] calldata beneficiaries, uint256[] calldata amounts) external payable onlyInternalWallets() {
        require(beneficiaries.length == amounts.length);
        for (uint256 i; i < beneficiaries.length; i++) {
            beneficiaries[i].transfer(amounts[i]);
            emit CommissionSent(internal_to_external[msg.sender], beneficiaries[i], amounts[i]);
        }
    }

    /**
     * @dev method to withdraw funds only by owner
     * @param _wallet address to which funds will be transferred
     */
    function withdraw(address payable _wallet) onlyOwner() public {
        uint256 weiAmount = address(this).balance;
        require(weiAmount > 0);
        _wallet.transfer(weiAmount);
        emit Withdrawal(_wallet, weiAmount);
    }

    function checkSpots(uint256[] calldata x, uint256[] calldata y) external view returns (address[] memory){
        address[] memory addresses;
        for (uint256 i; i < x.length; i++) {
            addresses[i] = coordinates[x[i]][y[i]];
        }
        return addresses;
    }

}
function assignSpots(uint256 coord_x, uint256 coord_y, uint _count) external onlyInternalWallets() {
    require(_count < 128);
    address wallet = internal_to_external[msg.sender];
    coordinates[coord_x][coord_y] = wallet;
    for (uint256 i; i < _count - 1; i++) {
        coordinates[coord_x + x_addition[i]][coord_y * (2 * (2 ** (x_addition[i] - 1))) + y_addition[i]] = wallet;
    }
}

/**
 * @dev method to assign spots, can be called by internal wallets only
 * @param coord_x coordinate x of the tree to start
 * @param coord_y coordinate y of the tree to start
 * @param _count count of spots to be assigned
 */