contract CryptoAdrian is AccessControl, DetailedERC721 {
    using SafeMath for uint256;

    /// @dev The TokenCreated event is fired whenever a new token is created.
    event TokenCreated(uint256 tokenId, string name, uint32 buffness, uint256 price, address owner);

    /// @dev The TokenSold event is fired whenever a token is sold.
    event TokenSold(uint256 indexed tokenId, string name, uint32 buffness, uint256 sellingPrice,
    uint256 newPrice, address indexed oldOwner, address indexed newOwner);

    /// @dev A mapping from tokenIds to the address that owns them. All tokens have
    ///  some valid owner address.
    mapping (uint256 => address) private tokenIdToOwner;

    /// @dev A mapping from TokenIds to the price of the token.
    mapping (uint256 => uint256) private tokenIdToPrice;

    /// @dev A mapping from owner address to count of tokens that address owns.
    ///  Used internally inside balanceOf() to resolve ownership count.
    mapping (address => uint256) private ownershipTokenCount;

    /// @dev A mapping from TokenIds to an address that has been approved to call
    ///  transferFrom(). Each Token can only have one approved address for transfer
    ///  at any time. A zero value means no approval is outstanding
    mapping (uint256 => address) public tokenIdToApproved;

    struct Adrian {
        string name;
        uint32 buffness;
    }

    Adrian[] private adrians;

    uint256 private startingPrice = 0.001 ether;
    bool private erc721Enabled = false;

    modifier onlyERC721() {
        require(erc721Enabled);
        _;
    }

    /// @dev Creates a new token with the given name and _price and assignes it to an _owner.
    function createToken(string _name, uint32 _buffness, address _owner, uint256 _price) public onlyCLevel {
        require(_owner != address(0));
        require(_price >= startingPrice);

        _createToken(_name, _buffness, _owner, _price);
    }

    /// @dev Creates a new token with the given name.
    function createToken(string _name, uint32 _buffness) public onlyCLevel {
        _createToken(_name, _buffness, address(this), startingPrice);
    }

    function _createToken(string _name, uint32 _buffness, address _owner, uint256 _price) private {
        Adrian memory _adrian = Adrian({
            name: _name,
            buffness: _buffness
        });
        uint256 newTokenId = adrians.push(_adrian) - 1;
        tokenIdToPrice[newTokenId] = _price;

        TokenCreated(newTokenId, _name, _buffness, _price, _owner);

        // This will assign ownership, and also emit the Transfer event as per ERC721 draft
        _transfer(address(0), _owner, newTokenId);
    }

    function getToken(uint256 _tokenId) public view returns (
        string _tokenName,
        uint32 _buffness,
        uint256 _price,
        uint256 _nextPrice,
        address _owner
    ) {
        _tokenName = adrians[_tokenId].name;
        _buffness = adrians[_tokenId].buffness;
        _price = tokenIdToPrice[_tokenId];
        _nextPrice = nextPriceOf(_tokenId);
        _owner = tokenIdToOwner[_tokenId];
    }

    function getAllTokens() public view returns (
        uint256[],
        uint256[],
        address[]
    ) {
        uint256 total = totalSupply();
        uint256[] memory prices = new uint256[](total);
        uint256[] memory nextPrices = new uint256[](total);
        address[] memory owners = new address[](total);

        for (uint256 i = 0; i < total; i++) {
            prices[i] = tokenIdToPrice[i];
            nextPrices[i] = nextPriceOf(i);
            owners[i] = tokenIdToOwner[i];
        }

        return (prices, nextPrices, owners);
    }

    function tokensOf(address _owner) public view returns(uint256[]) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 total = totalSupply();
            uint256 resultIndex = 0;

            for (uint256 i = 0; i < total; i++) {
                if (tokenIdToOwner[i] == _owner) {
                    result[resultIndex] = i;
                    resultIndex++;
                }
            }
            return result;
        }
    }

    /// @dev This function withdraws the contract owner's cut.
    /// Any amount may be withdrawn as there is no user funds.
    /// User funds are immediately sent to the old owner in `purchase`
    function withdrawBalance(address _to, uint256 _amount) public onlyCEO {
        require(_amount <= this.balance);

        if (_amount == 0) {
            _amount = this.balance;
        }

        if (_to == address(0)) {
            ceoAddress.transfer(_amount);
        } else {
            _to.transfer(_amount);
        }
    }

    // Send ether and obtain the token
    function purchase(uint256 _tokenId) public payable whenNotPaused {
        address oldOwner = ownerOf(_tokenId);
        address newOwner = msg.sender;
        uint256 sellingPrice = priceOf(_tokenId);

        // active tokens
        require(oldOwner != address(0));
        // maybe one day newOwner's logic allows this to happen
        require(newOwner != address(0));
        // don't buy from yourself
        require(oldOwner != newOwner);
        // don't sell to contracts
        // but even this doesn't prevent bad contracts to become an owner of a token
        require(!_isContract(newOwner));
        // another check to be sure that token is active
        require(sellingPrice > 0);
        // min required amount check
        require(msg.value >= sellingPrice);

        // transfer to the new owner
        _transfer(oldOwner, newOwner, _tokenId);
        // update fields before emitting an event
        tokenIdToPrice[_tokenId] = nextPriceOf(_tokenId);
        // emit event
        TokenSold(_tokenId, adrians[_tokenId].name, adrians[_tokenId].buffness, sellingPrice, priceOf(_tokenId), oldOwner, newOwner);

        // extra ether which should be returned back to buyer
        uint256 excess = msg.value.sub(sellingPrice);
        // contract owner's cut which is left in contract and accesed by withdrawBalance
        uint256 contractCut = sellingPrice.mul(6).div(100); // 6%

        // no need to transfer if it's initial sell
        if (oldOwner != address(this)) {
            // transfer payment to seller minus the contract's cut
            oldOwner.transfer(sellingPrice.sub(contractCut));
        }

        // return extra ether
        if (excess > 0) {
            newOwner.transfer(excess);
        }
    }

    function priceOf(uint256 _tokenId) public view returns (uint256 _price) {
        return tokenIdToPrice[_tokenId];
    }

    uint256 private increaseLimit1 = 0.001 ether;
    uint256 private increaseLimit2 = 0.05 ether;
    uint256 private increaseLimit3 = 0.1 ether;
    uint256 private increaseLimit4 = 1.0 ether;

    function nextPriceOf(uint256 _tokenId) public view returns (uint256 _nextPrice) {
        uint256 _price = priceOf(_tokenId);
        if (_price < increaseLimit1) {
            return _price.mul(200).div(95);
        } else if (_price < increaseLimit2) {
            return _price.mul(135).div(96);
        } else if (_price < increaseLimit3) {
            return _price.mul(125).div(97);
        } else if (_price < increaseLimit4) {
            return _price.mul(117).div(97);
        } else {
            return _price.mul(115).div(98);
        }
    }


    /*** ERC-721 ***/
    // Unlocks ERC721 behaviour, allowing for trading on third party platforms.
    function enableERC721() onlyCEO public {
        erc721Enabled = true;
    }

    function totalSupply() public view returns (uint256 _totalSupply) {
        _totalSupply = adrians.length;
    }

    function balanceOf(address _owner) public view returns (uint256 _balance) {
        _balance = ownershipTokenCount[_owner];
    }

    function ownerOf(uint256 _tokenId) public view returns (address _owner) {
        _owner = tokenIdToOwner[_tokenId];
        // require(_owner != address(0));
    }

    function approve(address _to, uint256 _tokenId) public whenNotPaused onlyERC721 {
        require(_owns(msg.sender, _tokenId));

        tokenIdToApproved[_tokenId] = _to;

        Approval(msg.sender, _to, _tokenId);
    }

    function transferFrom(address _from, address _to, uint256 _tokenId) public whenNotPaused onlyERC721 {
        require(_to != address(0));
        require(_owns(_from, _tokenId));
        require(_approved(msg.sender, _tokenId));

        _transfer(_from, _to, _tokenId);
    }

    function transfer(address _to, uint256 _tokenId) public whenNotPaused onlyERC721 {
        require(_to != address(0));
        require(_owns(msg.sender, _tokenId));

        // Reassign ownership, clear pending approvals, emit Transfer event.
        _transfer(msg.sender, _to, _tokenId);
    }

    function implementsERC721() public view whenNotPaused returns (bool) {
        return erc721Enabled;
    }

    function takeOwnership(uint256 _tokenId) public whenNotPaused onlyERC721 {
        require(_approved(msg.sender, _tokenId));

        _transfer(tokenIdToOwner[_tokenId], msg.sender, _tokenId);
    }

    function name() public view returns (string _name) {
        _name = "CryptoAdrian";
    }

    function symbol() public view returns (string _symbol) {
        _symbol = "CRPA";
    }

    /*** PRIVATES ***/
    /// @dev Check for token ownership.
    function _owns(address _claimant, uint256 _tokenId) private view returns (bool) {
        return tokenIdToOwner[_tokenId] == _claimant;
    }

    /// @dev For checking approval of transfer for address _to.
    function _approved(address _to, uint256 _tokenId) private view returns (bool) {
        return tokenIdToApproved[_tokenId] == _to;
    }

    /// @dev Assigns ownership of a specific token to an address.
    function _transfer(address _from, address _to, uint256 _tokenId) private {
        // Since the number of tokens is capped to 2^32 we can't overflow this
        ownershipTokenCount[_to]++;
        // Transfer ownership
        tokenIdToOwner[_tokenId] = _to;

        // When creating new token _from is 0x0, but we can't account that address.
        if (_from != address(0)) {
            ownershipTokenCount[_from]--;
            // clear any previously approved ownership exchange
            delete tokenIdToApproved[_tokenId];
        }

        // Emit the transfer event.
        Transfer(_from, _to, _tokenId);
    }

    /// @dev Checks if the address ia a contract or not
    function _isContract(address addr) private view returns (bool) {
        uint256 size;
        assembly { size := extcodesize(addr) }
        return size > 0;
    }
}
function _owns(address _claimant, uint256 _tokenId) private view returns (bool) {
    return tokenIdToOwner[_tokenId] == _claimant;
}

/// @dev Check for token ownership.