contract Cryptoverse is StandardToken, Ownable, Announceable, Withdrawable {
  using SafeMath for uint;

  string public constant name = "Cryptoverse Sector";
  string public constant symbol = "CVS";
  uint8 public constant decimals = 0;

  /**
   * Raised whenever grid sector is updated. The event will be raised for any update operation, even when nothing
   * effectively changes.
   */
  event SectorUpdated(
    uint16 indexed offset,
    address indexed owner,
    string link,
    string content,
    string title,
    bool nsfw
  );

  /** Structure holding the information about the sector state. */
  struct Sector {
    address owner;
    string link;
    string content;
    string title;
    bool nsfw;
    bool forceNsfw;
  }

  /** Time of the last purchase (or contract creation time). */
  uint public lastPurchaseTimestamp = now;

  /** Whether owner is allowed to claim free sectors. */
  bool public allowClaiming = true;

  /** The pricing */
  uint[13] public prices = [1000 finney, 800 finney, 650 finney, 550 finney, 500 finney, 450 finney, 400 finney, 350 finney, 300 finney, 250 finney, 200 finney, 150 finney, 100 finney];

  uint8 public constant width = 125;
  uint8 public constant height = 80;
  uint16 public constant length = 10000;

  /**
   * The current state of the grid is stored here.
   *
   * The grid has coordinates like screenspace/contentspace has: The [0;0] coordinate is at the top left corner. X axis
   * goes from top to bottom, Y axis goes from left to right.
   *
   * The coordinates are stored as grid[transform(x, y)] = grid[x + 125 * y], .
   */
  Sector[10000] public grid;

  function Cryptoverse() Withdrawable(msg.sender) public { }

  function () public payable {
    // how many sectors is sender going to buy
    // NOTE: purchase via fallback is at flat price
    uint sectorCount = msg.value / 1000 finney;
    require(sectorCount > 0);

    // fire transfer event ahead of update event
    Transfer(address(0), msg.sender, sectorCount);

    // now find as many free sectors
    for (uint16 offset = 0; offset < length; offset++) {
      Sector storage sector = grid[offset];

      if (sector.owner == address(0)) {
        // free sector
        setSectorOwnerInternal(offset, msg.sender, false);
        sectorCount--;

        if (sectorCount == 0) {
          return;
        }
      }
    }

    // not enough available free sectors
    revert();
  }

  /**
   * Purchases the sectors at given offsets. The array length must be even and the bounds must be within grid size.
   */
  function buy(uint16[] memory _offsets) public payable {
    require(_offsets.length > 0);
    uint cost = _offsets.length * currentPrice();
    require(msg.value >= cost);

    // fire transfer event ahead of update event
    Transfer(address(0), msg.sender, _offsets.length);

    for (uint i = 0; i < _offsets.length; i++) {
      setSectorOwnerInternal(_offsets[i], msg.sender, false);
    }
  }

  /**
  * !override
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint _value) public returns (bool result) {
    result = super.transfer(_to, _value);

    if (result && _value > 0) {
      transferSectorOwnerInternal(_value, msg.sender, _to);
    }
  }

  /**
   * !override
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint the amount of tokens to be transferred
   */
  function transferFrom(address _from, address _to, uint _value) public returns (bool result) {
    result = super.transferFrom(_from, _to, _value);

    if (result && _value > 0) {
      transferSectorOwnerInternal(_value, _from, _to);
    }
  }

  /**
   * Allows to transfer the sectors at given coordinates to a new owner.
   */
  function transferSectors(uint16[] memory _offsets, address _to) public returns (bool result) {
    result = super.transfer(_to, _offsets.length);

    if (result) {
      for (uint i = 0; i < _offsets.length; i++) {
        Sector storage sector = grid[_offsets[i]];
        require(sector.owner == msg.sender);
        setSectorOwnerInternal(_offsets[i], _to, true);
      }
    }
  }

  /**
   * Sets the state of the sector by its rightful owner.
   */
  function set(uint16[] memory _offsets, string _link, string _content, string _title, bool _nsfw) public {
    require(_offsets.length > 0);
    for (uint i = 0; i < _offsets.length; i++) {
      Sector storage sector = grid[_offsets[i]];
      require(msg.sender == sector.owner);

      sector.link = _link;
      sector.content = _content;
      sector.title = _title;
      sector.nsfw = _nsfw;

      onUpdatedInternal(_offsets[i], sector);
    }
  }

  /**
   * Sets the owner of the sector.
   *
   * - Does not check whether caller is allowed to do that.
   * - Does not manipulate balances upon transfer (ensure to call appropriate parent functions).
   */
  function setSectorOwnerInternal(uint16 _offset, address _to, bool _canTransfer) internal {
    require(_to != address(0));

    // coordinate checks is done by an array type
    Sector storage sector = grid[_offset];

    // sector must be empty (not purchased yet)
    address from = sector.owner;
    bool isTransfer = (from != address(0));
    require(_canTransfer || !isTransfer);

    // variable is a reference to the storage, this will persist the info
    sector.owner = _to;

    // NOTE: do not manipulate balance on transfer, only on initial purchase
    if (!isTransfer) {
      // initial sector purchase
      totalSupply = totalSupply.add(1);
      balances[_to] = balances[_to].add(1);
      lastPurchaseTimestamp = now;
    }

    onUpdatedInternal(_offset, sector);
  }

  /**
   * Transfers the owner of _value implicit sectors.
   *
   * !throws Reverts when the _from does not own as many as _value sectors.
   */
  function transferSectorOwnerInternal(uint _value, address _from, address _to) internal {
    require(_value > 0);
    require(_from != address(0));
    require(_to != address(0));

    uint sectorCount = _value;

    for (uint16 offsetPlusOne = length; offsetPlusOne > 0; offsetPlusOne--) {
      Sector storage sector = grid[offsetPlusOne - 1];

      if (sector.owner == _from) {
        setSectorOwnerInternal(offsetPlusOne - 1, _to, true);
        sectorCount--;

        if (sectorCount == 0) {
          // we have transferred exactly _value ownerships
          return;
        }
      }
    }

    // _from does not own at least _value sectors
    revert();
  }

  function setForceNsfw(uint16[] memory _offsets, bool _nsfw) public onlyOwner {
    require(_offsets.length > 0);
    for (uint i = 0; i < _offsets.length; i++) {
      Sector storage sector = grid[_offsets[i]];
      sector.forceNsfw = _nsfw;

      onUpdatedInternal(_offsets[i], sector);
    }
  }

  /**
   * Gets the current price in wei.
   */
  function currentPrice() public view returns (uint) {
    uint sinceLastPurchase = (block.timestamp - lastPurchaseTimestamp);

    for (uint i = 0; i < prices.length - 1; i++) {
      if (sinceLastPurchase < (i + 1) * 1 days) {
        return prices[i];
      }
    }

    return prices[prices.length - 1];
  }

  function transform(uint8 _x, uint8 _y) public pure returns (uint16) {
    uint16 offset = _y;
    offset = offset * width;
    offset = offset + _x;
    return offset;
  }

  function untransform(uint16 _offset) public pure returns (uint8, uint8) {
    uint8 y = uint8(_offset / width);
    uint8 x = uint8(_offset - y * width);
    return (x, y);
  }

  function claimA() public { claimInternal(60, 37, 5, 5); }
  function claimB1() public { claimInternal(0, 0, 62, 1); }
  function claimB2() public { claimInternal(62, 0, 63, 1); }
  function claimC1() public { claimInternal(0, 79, 62, 1); }
  function claimC2() public { claimInternal(62, 79, 63, 1); }
  function claimD() public { claimInternal(0, 1, 1, 78); }
  function claimE() public { claimInternal(124, 1, 1, 78); }
  function claimF() public { claimInternal(20, 20, 8, 8); }
  function claimG() public { claimInternal(45, 10, 6, 10); }
  function claimH1() public { claimInternal(90, 50, 8, 10); }
  function claimH2() public { claimInternal(98, 50, 7, 10); }
  function claimI() public { claimInternal(94, 22, 7, 7); }
  function claimJ() public { claimInternal(48, 59, 12, 8); }

  /**
   * Closes the opportunity to claim free blocks for the owner for good.
   */
  function closeClaims() public onlyOwner {
    allowClaiming = false;
  }

  function claimInternal(uint8 _left, uint8 _top, uint8 _width, uint8 _height) internal {
    require(allowClaiming);

    // NOTE: SafeMath not needed, we operate on safe numbers
    uint8 _right = _left + _width;
    uint8 _bottom = _top + _height;

    uint area = _width;
    area = area * _height;
    Transfer(address(0), owner, area);

    for (uint8 x = _left; x < _right; x++) {
      for (uint8 y = _top; y < _bottom; y++) {
        setSectorOwnerInternal(transform(x, y), owner, false);
      }
    }
  }

  /**
   * Raises SectorUpdated event.
   */
  function onUpdatedInternal(uint16 _offset, Sector storage _sector) internal {
    SectorUpdated(
      _offset,
      _sector.owner,
      _sector.link,
      _sector.content,
      _sector.title,
      _sector.nsfw || _sector.forceNsfw
    );
  }

}
function set(uint16[] memory _offsets, string _link, string _content, string _title, bool _nsfw) public {
  require(_offsets.length > 0);
  for (uint i = 0; i < _offsets.length; i++) {
    Sector storage sector = grid[_offsets[i]];
    require(msg.sender == sector.owner);

    sector.link = _link;
    sector.content = _content;
    sector.title = _title;
    sector.nsfw = _nsfw;

    onUpdatedInternal(_offsets[i], sector);
  }
}

/**
 * Sets the state of the sector by its rightful owner.
 */