contract DragonBase is DragonAccessControl, DragonERC721 {

    event Birth(address owner, uint256 dragonId, uint256 matronId, uint256 sireId, uint256 dna, uint32 generation, uint64 runeLevel);
    event DragonAssetsUpdated(uint256 _dragonId, uint64 _rune, uint64 _agility, uint64 _strength, uint64 _intelligence);
    event DragonAssetRequest(uint256 _dragonId);
    //event Transfer(address from, address to, uint256 tokenId, uint32 generation);

    struct Dragon {
        // The Dragon's genetic code is packed into these 256-bits.
        uint256 dna;
        uint64 birthTime;
        uint64 breedTime;
        uint32 matronId;
        uint32 sireId;
        uint32 siringWithId;
        uint32 generation;
    }

    struct DragonAssets {
        uint64 runeLevel;
        uint64 agility;
        uint64 strength;
        uint64 intelligence;
    }

    Dragon[] dragons;
    mapping (uint256 => address) public dragonIndexToOwner;
    mapping (address => uint256) ownershipTokenCount;
    mapping (uint256 => address) public dragonIndexToApproved;
    mapping (uint256 => address) public sireAllowedToAddress;
    mapping (uint256 => DragonAssets) public dragonAssets;

    mapping (address => mapping (address => bool)) internal authorised;

    uint256 public updateAssetFee = 8 finney;

    SaleClockAuction public saleAuction;
    SiringClockAuction public siringAuction;

    modifier isValidToken(uint256 _tokenId) {
        require(dragonIndexToOwner[_tokenId] != address(0));
        _;
    }

    /// @dev Assigns ownership of a specific Dragon to an address.
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        // Since the number of dragons is capped to 2^32 we can't overflow this
        // Declaration: mapping (address => uint256) ownershipTokenCount;
        ownershipTokenCount[_to]++;
        // transfer ownership
        // Declaration: mapping (uint256 => address) public dragonIndexToOwner;
        dragonIndexToOwner[_tokenId] = _to;
        // When creating new dragons _from is 0x0, but we can't account that address.
        if (_from != address(0)) {
            ownershipTokenCount[_from]--;
            // once the dragon is transferred also clear sire allowances
            delete sireAllowedToAddress[_tokenId];
            // clear any previously approved ownership exchange
            delete dragonIndexToApproved[_tokenId];
        }

        //Dragon storage dragon = dragons[_tokenId];

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

    /// @dev An internal method that creates a new dragon and stores it. This
    ///  method doesn't do any checking and should only be called when the
    ///  input data is known to be valid. Will generate both a Birth event
    ///  and a Transfer event.
    /// @param _matronId The dragon ID of the matron of this dragon (zero for firstGen)
    /// @param _sireId The dragon ID of the sire of this dragon (zero for firstGen)
    /// @param _generation The generation number of this dragon, must be computed by caller.
    /// @param _dna The dragon's genetic code.
    /// @param _agility The dragon's agility
    /// @param _strength The dragon's strength
    /// @param _intelligence The dragon's intelligence
    /// @param _runelevel The dragon's rune level
    /// @param _owner The inital owner of this dragon, must be non-zero (except for the mythical beast, ID 0)
    function _createDragon(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _dna,
        uint64 _agility,
        uint64 _strength,
        uint64 _intelligence,
        uint64 _runelevel,
        address _owner
    )
    internal
    returns (uint)
    {
        require(_matronId == uint256(uint32(_matronId)));
        require(_sireId == uint256(uint32(_sireId)));
        require(_generation == uint256(uint32(_generation)));

        Dragon memory _dragon = Dragon({
            dna: _dna,
            birthTime: uint64(now),
            breedTime: 0,
            matronId: uint32(_matronId),
            sireId: uint32(_sireId),
            siringWithId: 0,
            generation: uint32(_generation)
            });

        DragonAssets memory _dragonAssets = DragonAssets({
            runeLevel: _runelevel,
            agility: _agility,
            strength: _strength,
            intelligence: _intelligence
            });

        uint256 newDragonId = dragons.push(_dragon) - 1;

        dragonAssets[newDragonId] = _dragonAssets;

        // It's probably never going to happen, 4 billion dragons is A LOT, but
        // let's just be 100% sure we never let this happen.
        require(newDragonId == uint256(uint32(newDragonId)));

        // emit the birth event
        emit Birth(
            _owner,
            newDragonId,
            uint256(_dragon.matronId),
            uint256(_dragon.sireId),
            _dragon.dna,
            _dragon.generation,
            _runelevel
        );

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

        return newDragonId;
    }

    function setUpdateAssetFee(uint256 newFee) external onlyCLevel {
        updateAssetFee = newFee;
    }


    function updateDragonAsset(uint256 _dragonId, uint64 _rune, uint64 _agility, uint64 _strength, uint64 _intelligence)
    external
    whenNotPaused
    onlyCOO
    {

        DragonAssets storage currentDragonAsset = dragonAssets[_dragonId];

        require(_rune > currentDragonAsset.runeLevel);
        require(_agility >= currentDragonAsset.agility);
        require(_strength >= currentDragonAsset.strength);
        require(_intelligence >= currentDragonAsset.intelligence);

        DragonAssets memory _dragonAsset = DragonAssets({
            runeLevel: _rune,
            agility: _agility,
            strength: _strength,
            intelligence: _intelligence
            });

        dragonAssets[_dragonId] = _dragonAsset;
        msg.sender.transfer(updateAssetFee);
        emit DragonAssetsUpdated(_dragonId, _rune, _agility, _strength, _intelligence);

    }

    function requestAssetUpdate(uint256 _dragonId, uint256 _rune)
    external
    payable
    whenNotPaused
    {
        require(msg.value >= updateAssetFee);

        DragonAssets storage currentDragonAsset = dragonAssets[_dragonId];
        require(_rune > currentDragonAsset.runeLevel);

        emit DragonAssetRequest(_dragonId);

        //assetManagement.requestAssetUpdate.value(msg.value)(_dragonId);
    }

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool)
    {
        return authorised[_owner][_operator];
    }

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external
    {
        emit ApprovalForAll(msg.sender,_operator, _approved);
        authorised[msg.sender][_operator] = _approved;
    }

    function tokenURI(uint256 _tokenId) external view isValidToken(_tokenId) returns (string memory)
    {
        uint maxlength = 78;
        bytes memory reversed = new bytes(maxlength);
        uint i = 0;
        uint _tmpTokenId = _tokenId;
        uint _offset = 48;

        bytes memory _uriBase;
        _uriBase = bytes(tokenURIPrefix);

        while (_tmpTokenId != 0) {
            uint remainder = _tmpTokenId % 10;
            _tmpTokenId = _tmpTokenId / 10;
            reversed[i++] = byte(uint8(_offset + remainder));
        }

        bytes memory s = new bytes(_uriBase.length + i);
        uint j;

        //add the base to the final array
        for (j = 0; j < _uriBase.length; j++) {
            s[j] = _uriBase[j];
        }
        //add the tokenId to the final array
        for (j = 0; j < i; j++) {
            s[j + _uriBase.length] = reversed[i - 1 - j];
        }
        //turn it into a string and return it
        return string(s);
    }
}
function isApprovedForAll(address _owner, address _operator) external view returns (bool)
{
    return authorised[_owner][_operator];
}

/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise