contract EtherAthlete is CryptoNFT {
    /// @notice Name and symbol of the non fungible token, as defined in ERC721.
    string public constant NAME = "EtherAthlete"; // solhint-disable-line
    string public constant SYMBOL = "EAT"; // solhint-disable-line

    uint256 private constant PROMO_CREATION_LIMIT = 5000;
    uint256 public promoCreatedCount;

    bool public allowPriceUpdate;

    /*** CONSTANTS ***/
    uint256 private firstStepLimit =  0.32 ether;
    uint256 private secondStepLimit = 2.8629151 ether;

    uint256 private defaultIncreasePercent = 200;
    uint256 private fsIncreasePercent = 155;
    uint256 private ssIncreasePercent = 130;

    uint256 private defaultPlayerPercent = 7500;
    uint256 private fsPlayerPercent = 8400;
    uint256 private ssPlayerPercent = 9077;

    // The addresses of the charity account.
    address public charityAddress;
    uint256 private charityPercent = 3;
    uint256 public charityBalance;


    /*** CONSTRUCTOR ***/
    function EtherAthlete() public {
        allowPriceUpdate = false;
        charityAddress = msg.sender;
        charityBalance = 0 ether;
    }
    
    /*** PUBLIC FUNCTIONS ***/

    /// @dev Required for ERC-721 compliance.
    function name() public pure returns (string) {
        return NAME;
    }
    /// @dev Required for ERC-721 compliance.
    function symbol() public pure returns (string) {
        return SYMBOL;
    }

    /// @dev Creates a new promo Person with the given name, with given _price and assignes it to an address.
    function createPromoPerson(address _owner, string _name, uint256 _price) public onlyCOO {
        require(promoCreatedCount < PROMO_CREATION_LIMIT);

        address personOwner = _owner;
        if (personOwner == address(0)) {
        personOwner = cooAddress;
        }

        if (_price <= 0) {
        _price = startingPrice;
        }

        promoCreatedCount++;
        _createPerson(_name, personOwner, _price);
    }

    function setAllowPriceUpdate(bool _bValue) public onlyCOO {
        allowPriceUpdate = _bValue;
    }

    /*** PRIVATE FUNCTIONS ***/
    /// Overrides Abstract internal method to calculate payment proportion for old owner
    /// out of selling price in current purchase
    function calcPaymentToOldOwner(uint256 sellingPrice) internal returns (uint256 payToOldOwner) {
        if (sellingPrice <= firstStepLimit) {
            // first stage
            payToOldOwner = uint256(SafeMath.div(SafeMath.mul(sellingPrice, defaultPlayerPercent),10000));
        } else if (sellingPrice <= secondStepLimit) {
            // second stage
            payToOldOwner = uint256(SafeMath.div(SafeMath.mul(sellingPrice, fsPlayerPercent),10000));
        } else {
            // third stage
            payToOldOwner = uint256(SafeMath.div(SafeMath.mul(sellingPrice, ssPlayerPercent),10000));
        }

        // Update the charity balance
        uint256 gainToHouse = SafeMath.sub(sellingPrice, payToOldOwner);
        charityBalance = SafeMath.add(charityBalance, SafeMath.div(SafeMath.mul(gainToHouse, charityPercent),100));
    }

    /// Overrides the abstract method to calculate the next selling price based on
    /// current selling prices of the asset.
    function calcNextSellingPrice(uint256 currentSellingPrice) internal returns (uint256 newSellingPrice) {
        if (currentSellingPrice < firstStepLimit) {
            // first stage
            newSellingPrice = SafeMath.div(SafeMath.mul(currentSellingPrice, defaultIncreasePercent), 100);
        } else if (currentSellingPrice < secondStepLimit) {
            // second stage
            newSellingPrice = SafeMath.div(SafeMath.mul(currentSellingPrice, fsIncreasePercent), 100);
        } else {
            // third stage
            newSellingPrice = SafeMath.div(SafeMath.mul(currentSellingPrice, ssIncreasePercent), 100);
        }
    }

    function setCharityAddress(address _charityAddress) public onlyCEO {
        charityAddress = _charityAddress;
    }

    function payout(address _to) public onlyCEO {
        uint256 amountToCharity = charityBalance;
        uint256 amount = SafeMath.sub(this.balance, charityBalance);
        charityBalance = 0;
        _payout(charityAddress, amountToCharity);
        _payout(_to, amount);
    }

    function updateTokenSellingPrice(uint256 _tokenId, uint256 sellingPrice) public {
        require(allowPriceUpdate);
        require(_owns(msg.sender, _tokenId));
        require(sellingPrice < personIndexToPrice[_tokenId]);
        require(sellingPrice >= startingPrice);
        personIndexToPrice[_tokenId] = sellingPrice;
    }
}
function calcPaymentToOldOwner(uint256 sellingPrice) internal returns (uint256 payToOldOwner) {
    if (sellingPrice <= firstStepLimit) {
        // first stage
        payToOldOwner = uint256(SafeMath.div(SafeMath.mul(sellingPrice, defaultPlayerPercent),10000));
    } else if (sellingPrice <= secondStepLimit) {
        // second stage
        payToOldOwner = uint256(SafeMath.div(SafeMath.mul(sellingPrice, fsPlayerPercent),10000));
    } else {
        // third stage
        payToOldOwner = uint256(SafeMath.div(SafeMath.mul(sellingPrice, ssPlayerPercent),10000));
    }

    // Update the charity balance
    uint256 gainToHouse = SafeMath.sub(sellingPrice, payToOldOwner);
    charityBalance = SafeMath.add(charityBalance, SafeMath.div(SafeMath.mul(gainToHouse, charityPercent),100));
}

/// Overrides Abstract internal method to calculate payment proportion for old owner
/// out of selling price in current purchase