contract BLBlocks is BLBalances {

    event CreatedBlock(
        uint x,
        uint y,
        uint price,
        address indexed owner,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL);

    event SetBlockForSale(
        uint x,
        uint y,
        uint price,
        address indexed owner);

    event UnsetBlockForSale(
        uint x,
        uint y,
        address indexed owner);

    event BoughtBlock(
        uint x,
        uint y,
        uint price,
        address indexed owner,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL);

    event SoldBlock(
        uint x,
        uint y,
        uint oldPrice,
        uint newPrice,
        uint feePercentage,
        address indexed owner);

    event UpdatedBlock(uint x,
        uint y,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL,
        address indexed owner);

    // Create a block if it doesn't exist
    function createBlock(
        uint x,
        uint y,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL
    ) public payable {
        bytes32 key = getKey(x, y);
        uint initialPrice = s.getUInt("initialPrice");
        address owner = s.getAdd(keccak256(key, "owner"));
        uint allowance = s.getUInt(keccak256(msg.sender, "promoAllowance"));
        require(msg.value >= initialPrice || allowance > 0);
        require(owner == address(0));
        uint feePercentage = s.getUInt("buyOutFeePercentage");
        if (msg.value >= initialPrice) {
            rewardParties(x, y, feePercentage);
            s.setUInt(keccak256(key, "price"), msg.value);
        } else {
            allowance--;
            s.setUInt(keccak256(msg.sender, "promoAllowance"), allowance);
            s.setUInt(keccak256(key, "price"), initialPrice);
        }
        s.setBytes32(keccak256(key, "name"), name);
        s.setBytes32(keccak256(key, "description"), description);
        s.setBytes32(keccak256(key, "url"), url);
        s.setBytes32(keccak256(key, "imageURL"), imageURL);
        s.setAdd(keccak256(key, "owner"), msg.sender);
        uint blockCount = s.getUInt("blockCount");
        giveBonusIfExists(x, y);
        blockCount++;
        s.setUInt("blockCount", blockCount);
        storageAddress.transfer(msg.value);
        CreatedBlock(x,
            y,
            msg.value,
            msg.sender,
            name,
            description,
            url,
            imageURL);
    }

    // Get details for a block
    function getBlock (uint x, uint y) public view returns (
        uint price,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL,
        uint forSale,
        uint pricePerDay,
        address owner
    ) {
        bytes32 key = getKey(x, y);
        price = s.getUInt(keccak256(key, "price"));
        name = s.getBytes32(keccak256(key, "name"));
        description = s.getBytes32(keccak256(key, "description"));
        url = s.getBytes32(keccak256(key, "url"));
        imageURL = s.getBytes32(keccak256(key, "imageURL"));
        forSale = s.getUInt(keccak256(key, "forSale"));
        pricePerDay = s.getUInt(keccak256(key, "pricePerDay"));
        owner = s.getAdd(keccak256(key, "owner"));
    }

    // Sets a block up for sale
    function sellBlock(uint x, uint y, uint price) public {
        bytes32 key = getKey(x, y);
        uint basePrice = s.getUInt(keccak256(key, "price"));
        require(s.getAdd(keccak256(key, "owner")) == msg.sender);
        require(price < basePrice * 2);
        s.setUInt(keccak256(key, "forSale"), price);
        SetBlockForSale(x, y, price, msg.sender);
    }

    // Sets a block not for sale
    function cancelSellBlock(uint x, uint y) public {
        bytes32 key = getKey(x, y);
        require(s.getAdd(keccak256(key, "owner")) == msg.sender);
        s.setUInt(keccak256(key, "forSale"), 0);
        UnsetBlockForSale(x, y, msg.sender);
    }

    // transfers ownership of an existing block
    function buyBlock(
        uint x,
        uint y,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL
    ) public payable {
        bytes32 key = getKey(x, y);
        uint price = s.getUInt(keccak256(key, "price"));
        uint forSale = s.getUInt(keccak256(key, "forSale"));
        address owner = s.getAdd(keccak256(key, "owner"));
        require(owner != address(0));
        require((forSale > 0 && msg.value >= forSale) || msg.value >= price * 2);
        uint feePercentage = s.getUInt("buyOutFeePercentage");
        rewardParties(x, y, feePercentage);
        s.setUInt(keccak256(key, "price"), msg.value);
        s.setBytes32(keccak256(key, "name"), name);
        s.setBytes32(keccak256(key, "description"), description);
        s.setBytes32(keccak256(key, "url"), url);
        s.setBytes32(keccak256(key, "imageURL"), imageURL);
        s.setAdd(keccak256(key, "owner"), msg.sender);
        s.setUInt(keccak256(key, "forSale"), 0);
        s.setUInt(keccak256(key, "pricePerDay"), 0);
        giveBonusIfExists(x, y);
        storageAddress.transfer(msg.value);
        BoughtBlock(x, y, msg.value, msg.sender,
            name, description, url, imageURL);
        SoldBlock(x, y, price, msg.value, feePercentage, owner);
    }

    // update details for an existing block
    function updateBlock(
        uint x,
        uint y,
        bytes32 name,
        bytes32 description,
        bytes32 url,
        bytes32 imageURL
    )  public {
        bytes32 key = getKey(x, y);
        address owner = s.getAdd(keccak256(key, "owner"));
        require(msg.sender == owner);
        s.setBytes32(keccak256(key, "name"), name);
        s.setBytes32(keccak256(key, "description"), description);
        s.setBytes32(keccak256(key, "url"), url);
        s.setBytes32(keccak256(key, "imageURL"), imageURL);
        UpdatedBlock(x, y, name, description, url, imageURL, msg.sender);
    }
    
    // Add a bonus to a block. That bonus will be awarded to the next buyer.
    // Note, we are not emitting an event to avoid cheating.
    function addBonusToBlock(
        uint x,
        uint y,
        uint bonus
    ) public onlyPrimary {
        bytes32 key = getKey(x, y);
        uint bonusBalance = s.getUInt(keccak256(bonusAddress, "balance"));
        require(bonusBalance >= bonus);
        s.setUInt(keccak256(key, "bonus"), bonus);
    }

}
function sellBlock(uint x, uint y, uint price) public {
    bytes32 key = getKey(x, y);
    uint basePrice = s.getUInt(keccak256(key, "price"));
    require(s.getAdd(keccak256(key, "owner")) == msg.sender);
    require(price < basePrice * 2);
    s.setUInt(keccak256(key, "forSale"), price);
    SetBlockForSale(x, y, price, msg.sender);
}

// Sets a block up for sale