contract HorseyToken is EthorseHelpers,Pausable {
    using SafeMath for uint256;

    /// @dev called when someone claims a token
    event Claimed(address raceAddress, address eth_address, uint256 tokenId);
    
    /// @dev called when someone starts a feeding process
    event Feeding(uint256 tokenId);

    /// @dev called when someone ends a feeding process
    event ReceivedCarrot(uint256 tokenId, bytes32 newDna);

    /// @dev called when someone fails to end a feeding on the 255 blocks timer
    event FeedingFailed(uint256 tokenId);

    /// @dev called when a horsey is renamed
    event HorseyRenamed(uint256 tokenId, string newName);

    /// @dev called when a horsey is freed for carrots
    event HorseyFreed(uint256 tokenId);

    /// @dev address of the RoyalStables
    RoyalStablesInterface public stables;

    ///@dev multiplier applied to carrots received from burning a horsey
    uint8 public carrotsMultiplier = 1;

    ///@dev multiplier applied to rarity bounds when feeding horsey
    uint8 public rarityMultiplier = 1;

    ///@dev fee to pay when claiming a token
    uint256 public claimingFee = 0.000 ether;

    /**
        @dev Holds the necessary data to feed a horsey
            The user has to create begin feeding and wait for the block
            with the feeding transaction to be hashed
            Only then he can stop the feeding
    */
    struct FeedingData {
        uint256 blockNumber;    ///@dev Holds the block number where the feeding began
        uint256 horsey;         ///@dev Holds the horsey id
    }

    /// @dev Maps a user to his pending feeding
    mapping(address => FeedingData) public pendingFeedings;

    /// @dev Stores the renaming fees per character a user has to pay upon renaming a horsey
    uint256 public renamingCostsPerChar = 0.001 ether;

    /**
        @dev Contracts constructor
            Initializes token data
            is pausable,ownable
        @param stablesAddress Address of the official RoyalStables contract
    */
    constructor(address stablesAddress) 
    EthorseHelpers() 
    Pausable() public {
        stables = RoyalStablesInterface(stablesAddress);
    }

    /**
        @dev Changes multiplier for rarity on feed
        @param newRarityMultiplier The cost to charge in wei for each character of the name
    */
    function setRarityMultiplier(uint8 newRarityMultiplier) external 
    onlyOwner()  {
        rarityMultiplier = newRarityMultiplier;
    }

    /**
        @dev Sets a new muliplier for freeing a horse
        @param newCarrotsMultiplier the new multiplier for feeding
    */
    function setCarrotsMultiplier(uint8 newCarrotsMultiplier) external 
    onlyOwner()  {
        carrotsMultiplier = newCarrotsMultiplier;
    }

    /**
        @dev Sets a new renaming per character cost in wei
            Any CLevel can call this function
        @param newRenamingCost The cost to charge in wei for each character of the name
    */
    function setRenamingCosts(uint256 newRenamingCost) external 
    onlyOwner()  {
        renamingCostsPerChar = newRenamingCost;
    }

    /**
        @dev Sets a new claiming fee in wei
            Any CLevel can call this function
        @param newClaimingFee The cost to charge in wei for each claimed HRSY
    */
    function setClaimingCosts(uint256 newClaimingFee) external
    onlyOwner()  {
        claimingFee = newClaimingFee;
    }

    /**
        @dev Allows to add a race address for races validation
        @param newAddress the race address
    */
    function addLegitRaceAddress(address newAddress) external
    onlyOwner() {
        _addLegitRace(newAddress);
    }

    /**
        @dev Owner can withdraw the current balance
    */
    function withdraw() external 
    onlyOwner()  {
        owner.transfer(address(this).balance); //throws on fail
    }

    //allows owner to add a horse name to the possible horses list (BTC,ETH,LTC,...)
    /**
        @dev Adds a new horse index to the possible horses list (BTC,ETH,LTC,...)
            This is in case ethorse adds a new coin
            Any CLevel can call this function
        @param newHorse Index of the horse to add (same data type as the original ethorse erc20 contract code)
    */
    function addHorseIndex(bytes32 newHorse) external
    onlyOwner() {
        _addHorse(newHorse);
    }

    /**
        @dev Gets the complete list of token ids which belongs to an address
        @param eth_address The address you want to lookup owned tokens from
        @return List of all owned by eth_address tokenIds
    */
    function getOwnedTokens(address eth_address) public view returns (uint256[]) {
        return stables.getOwnedTokens(eth_address);
    }
    
    /**
        @dev Allows to check if an eth_address can claim a horsey from this contract
            should we also check if already claimed here?
        @param raceAddress The ethorse race you want to claim from
        @param eth_address The users address you want to claim the token for
        @return True only if eth_address is a winner of the race contract at raceAddress
    */
    function can_claim(address raceAddress, address eth_address) public view returns (bool) {
        bool res;
        (res,) = _isWinnerOf(raceAddress, eth_address);
        return res;
    }

    /**
        @dev Allows a user to claim a special horsey with the same dna as the race one
            Cant be used on paused
            The sender has to be a winner of the race and must never have claimed a special horsey from this race
        @param raceAddress The race's address
    */
    function claim(address raceAddress) external payable
    costs(claimingFee)
    whenNotPaused()
    {
        //call _isWinnerOf with a 0 address to simply get the winner horse
        bytes32 winner;
        bool res;
        (res,winner) = _isWinnerOf(raceAddress, address(0));
        require(winner != bytes32(0),"Winner is zero");
        require(res,"can_claim return false");
        //require(!exists(id)); should already be checked by mining function
        uint256 id = _generate_special_horsey(raceAddress, msg.sender, winner);
        emit Claimed(raceAddress, msg.sender, id);
    }

    /**
        @dev Allows a user to give a horsey a name or rename it
            This function is payable and its cost is renamingCostsPerChar * length(newname)
            Cant be called while paused
            If called with too low balance, the modifier will throw
            If called with too much balance, we try to return the remaining funds back
            Upon completion we update all ceos balances, maybe not very efficient?
        @param tokenId ID of the horsey to rename
        @param newName The name to give to the horsey
    */
    function renameHorsey(uint256 tokenId, string newName) external 
    whenNotPaused()
    onlyOwnerOf(tokenId) 
    costs(renamingCostsPerChar * bytes(newName).length)
    payable {
        uint256 renamingFee = renamingCostsPerChar * bytes(newName).length;
        //Return over paid amount to sender if necessary
        if(msg.value > renamingFee) //overpaid
        {
            msg.sender.transfer(msg.value.sub(renamingFee));
        }
        //store the new name
        stables.storeName(tokenId,newName);
        emit HorseyRenamed(tokenId,newName);
    }

    /**
        @dev Allows a user to burn a token he owns to get carrots
            The mount of carrots given is equal to the horsey's feedingCounter upon burning
            Cant be called on a horsey with a pending feeding
            Cant be called while paused
        @param tokenId ID of the token to burn
    */
    function freeForCarrots(uint256 tokenId) external 
    whenNotPaused()
    onlyOwnerOf(tokenId) {
        require(pendingFeedings[msg.sender].horsey != tokenId,"");
        //credit carrots
        uint8 feedingCounter;
        (,,feedingCounter,) = stables.horseys(tokenId);
        stables.storeCarrotsCredit(msg.sender,stables.carrot_credits(msg.sender) + uint32(feedingCounter * carrotsMultiplier));
        stables.unstoreHorsey(tokenId);
        emit HorseyFreed(tokenId);
    }

    /**
        @dev Returns the amount of carrots the user owns
            We have a getter to hide the carrots amount from public view
        @return The current amount of carrot credits the sender owns 
    */
    function getCarrotCredits() external view returns (uint32) {
        return stables.carrot_credits(msg.sender);
    }

    /**
        @dev Returns horsey data of a given token
        @param tokenId ID of the horsey to fetch
        @return (race address, dna, feedingCounter, name)
    */
    function getHorsey(uint256 tokenId) public view returns (address, bytes32, uint8, string) {
        RoyalStablesInterface.Horsey memory temp;
        (temp.race,temp.dna,temp.feedingCounter,temp.tier) = stables.horseys(tokenId);
        return (temp.race,temp.dna,temp.feedingCounter,stables.names(tokenId));
    }

    /**
        @dev Allows to feed a horsey to increase its feedingCounter value
            Gives a chance to get a rare trait
            The amount of carrots required is the value of current feedingCounter
            The carrots the user owns will be reduced accordingly upon success
            Cant be called while paused
        @param tokenId ID of the horsey to feed
    */
    function feed(uint256 tokenId) external 
    whenNotPaused()
    onlyOwnerOf(tokenId) 
    carrotsMeetLevel(tokenId)
    noFeedingInProgress()
    {
        pendingFeedings[msg.sender] = FeedingData(block.number,tokenId);
        uint8 feedingCounter;
        (,,feedingCounter,) = stables.horseys(tokenId);
        stables.storeCarrotsCredit(msg.sender,stables.carrot_credits(msg.sender) - uint32(feedingCounter));
        emit Feeding(tokenId);
    }

    /**
        @dev Allows user to stop feeding a horsey
            This will trigger a random rarity chance
    */
    function stopFeeding() external
    feedingInProgress() returns (bool) {
        uint256 blockNumber = pendingFeedings[msg.sender].blockNumber;
        uint256 tokenId = pendingFeedings[msg.sender].horsey;
        //you cant feed and stop feeding from the same block!
        require(block.number - blockNumber >= 1,"feeding and stop feeding are in same block");

        delete pendingFeedings[msg.sender];

        //solidity only gives you access to the previous 256 blocks
        //deny and remove this obsolete feeding if we cant fetch its blocks hash
        if(block.number - blockNumber > 255) {
            //the feeding is outdated = failed
            //the user can feed again but he lost his carrots
            emit FeedingFailed(tokenId);
            return false; 
        }

        //token could have been transfered in the meantime to someone else
        if(stables.ownerOf(tokenId) != msg.sender) {
            //the feeding is failed because the token no longer belongs to this user = failed
            //the user has lost his carrots
            emit FeedingFailed(tokenId);
            return false; 
        }
        
        //call horsey generation with the claim block hash
        _feed(tokenId, blockhash(blockNumber));
        bytes32 dna;
        (,dna,,) = stables.horseys(tokenId);
        emit ReceivedCarrot(tokenId, dna);
        return true;
    }

    /// @dev Only ether sent explicitly through the donation() function is accepted
    function() external payable {
        revert("Not accepting donations");
    }

    /**
        @dev Internal function to increase a horsey's rarity
            Uses a random value to assess if the feeding process increases rarity
            The chances of having a rarity increase are based on the current feedingCounter
        @param tokenId ID of the token to "feed"
        @param blockHash Hash of the block where the feeding began
    */
    function _feed(uint256 tokenId, bytes32 blockHash) internal {
        //Grab the upperbound for probability 100,100
        uint8 tier;
        uint8 feedingCounter;
        (,,feedingCounter,tier) = stables.horseys(tokenId);
        uint256 probabilityByRarity = 10 ** (uint256(tier).add(1));
        uint256 randNum = uint256(keccak256(abi.encodePacked(tokenId, blockHash))) % probabilityByRarity;

        //Scale probability based on horsey's level
        if(randNum <= (feedingCounter * rarityMultiplier)){
            _increaseRarity(tokenId, blockHash);
        }

        //Increment feedingCounter
        //Maximum allowed is 255, which requires 32385 carrots, so we should never reach that
        if(feedingCounter < 255) {
            stables.modifyHorseyFeedingCounter(tokenId,feedingCounter+1);
        }
    }

    /// @dev creates a special token id based on the race and the coin index
    function _makeSpecialId(address race, address sender, bytes32 coinIndex) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(race, sender, coinIndex)));
    }

    /**
        @dev Internal function to generate a SPECIAL horsey token
            we then use the ERC721 inherited minting process
            the dna is a bytes32 target for a keccak256. Not using blockhash
            finaly, a bitmask zeros the first 2 bytes for rarity traits
        @param race Address of the associated race
        @param eth_address Address of the user to receive the token
        @param coinIndex The index of the winning coin
        @return ID of the token
    */
    function _generate_special_horsey(address race, address eth_address, bytes32 coinIndex) internal returns (uint256) {
        uint256 id = _makeSpecialId(race, eth_address, coinIndex);
        //generate dna
        bytes32 dna = _shiftRight(keccak256(abi.encodePacked(race, coinIndex)),16);
         //storeHorsey checks if the token exists before minting already, so we dont have to here
        stables.storeHorsey(eth_address,id,race,dna,1,0);
        return id;
    }
    
    /**
        @dev Internal function called to increase a horsey rarity
            We generate a random zeros mask with a single 1 in the leading 16 bits
        @param tokenId Id of the token to increase rarity of
        @param blockHash hash of the block where the feeding began
    */
    function _increaseRarity(uint256 tokenId, bytes32 blockHash) private {
        uint8 tier;
        bytes32 dna;
        (,dna,,tier) = stables.horseys(tokenId);
        if(tier < 255)
            stables.modifyHorseyTier(tokenId,tier+1);
        uint256 random = uint256(keccak256(abi.encodePacked(tokenId, blockHash)));
        //this creates a mask of 256 bits such as one of the first 16 bits will be 1
        bytes32 rarityMask = _shiftLeft(bytes32(1), (random % 16 + 240));
        bytes32 newdna = dna | rarityMask; //apply mask to add the random flag
        stables.modifyHorseyDna(tokenId,newdna);
    }

    /// @dev shifts a bytes32 left by n positions
    function _shiftLeft(bytes32 data, uint n) internal pure returns (bytes32) {
        return bytes32(uint256(data)*(2 ** n));
    }

    /// @dev shifts a bytes32 right by n positions
    function _shiftRight(bytes32 data, uint n) internal pure returns (bytes32) {
        return bytes32(uint256(data)/(2 ** n));
    }

    /// @dev Modifier to ensure user can afford a rehorse
    modifier carrotsMeetLevel(uint256 tokenId){
        uint256 feedingCounter;
        (,,feedingCounter,) = stables.horseys(tokenId);
        require(feedingCounter <= stables.carrot_credits(msg.sender),"Not enough carrots");
        _;
    }

    /// @dev insures the caller payed the required amount
    modifier costs(uint256 amount) {
        require(msg.value >= amount,"Not enough funds");
        _;
    }

    /// @dev requires the address to be non null
    modifier validAddress(address addr) {
        require(addr != address(0),"Address is zero");
        _;
    }

    /// @dev requires that the user isnt feeding a horsey already
    modifier noFeedingInProgress() {
        //if the key does not exit, then the default struct data is used where blockNumber is 0
        require(pendingFeedings[msg.sender].blockNumber == 0,"Already feeding");
        _;
    }

    /// @dev requires that the user isnt feeding a horsey already
    modifier feedingInProgress() {
        //if the key does not exit, then the default struct data is used where blockNumber is 0
        require(pendingFeedings[msg.sender].blockNumber != 0,"No pending feeding");
        _;
    }

    /// @dev requires that the user isnt feeding a horsey already
    modifier onlyOwnerOf(uint256 tokenId) {
        require(stables.ownerOf(tokenId) == msg.sender, "Caller is not owner of this token");
        _;
    }
}
/**
 @title HorseyToken ERC721 Token
 @dev Horse contract - horse derives fro AccessManager built on top of ERC721 token and uses 
 @dev EthorseHelpers and AccessManager
 */
function withdraw() external 
onlyOwner()  {
    owner.transfer(address(this).balance); //throws on fail
}

/**
 @dev Owner can withdraw the current balance
 */