contract Yomama is ERC20, Ownable, Pausable {
    using SafeMath for uint256;


    uint256 public constant halfLifeTimeInSeconds = 86400;

    uint256 public burnStartTimeStamp;

    address public stakingVaultAddress = address(0);

    bool public burnEnabled = false;

    mapping(address => bool) public merchantAddresses;

    uint256 private constant startingBurnPercentage = 50;

    // plateau value of 3.125 using 3 digits precision
    uint256 private constant _plateauBurnFactor = 3125;
    
    // just to do div/mul with 3 digits precision
    uint256 private constant _PRECISIONCONVERT = 1000;
    
    uint256 private _PRECISIONCONVERT_TOPERCENT;

    constructor(
        address _presaleAddress,
        address _liquidityAddress,
        address _yieldFarmingAddress,
        address _teamAddress,
        address _promotionsAddress
    )
    public
    Ownable()
    ERC20("yomama.finance", "YOMA")
    {   
        _PRECISIONCONVERT_TOPERCENT = _PRECISIONCONVERT.mul(100);   
        _mintAllTokens(_presaleAddress, _liquidityAddress, _yieldFarmingAddress, _teamAddress, _promotionsAddress);
        _pause();
    }

    /**
     * @dev Sets a merchant address (liquidity provision)
     */
    function addMerchantAddress(address _address, bool _burnFlag) public onlyOwner {
        merchantAddresses[_address] = _burnFlag;
    }

    function _mintAllTokens(
        address _presaleAddress,
        address _liquidityAddress,
        address _yieldFarmingAddress,
        address _teamAddress,
        address _promotionsAddress
    ) 
    internal
    {
        // presale 7000 tokens
        _mint(_presaleAddress, 7000 * 1e18);

        // liquidity provision 2500 tokens
        _mint(_liquidityAddress, 2500 * 1e18);

        // yield farming vault 9800 tokens
        _mint(_yieldFarmingAddress, 9800 * 1e18);

        // team 500 tokens
        _mint(_teamAddress, 500 * 1e18);

        // promotions 200 tokens
        _mint(_promotionsAddress, 200 * 1e18);       
    }

    function _isMerchant(address _address) internal view returns (bool) {
        return merchantAddresses[_address];
    }

    // updates the staking vault address on contract deploy
    function setStakingVaultAddress(address _address) external onlyOwner {
        stakingVaultAddress = _address;
    }

    function _transfer(address sender, address recipient, uint256 amount)
        internal
        virtual
        override
    {   
        // if sending from merchant address
        if(_isMerchant(sender)){
            uint256 amountToBurn = amount.mul(getCurrentBurn()).div(_PRECISIONCONVERT_TOPERCENT);
            super._transfer(sender, stakingVaultAddress, amountToBurn);
            amount = amount.sub(amountToBurn);
        }
        super._transfer(sender, recipient, amount);
    }

    function getCurrentBurn() internal view returns (uint256) {
        // burn calculation with the help of ABDKMath library

        // get time since burn started
        uint256 _timeSinceBurnStart = now - burnStartTimeStamp;
        
        uint256 burnf;

        // if 4 days have passed
        if(_timeSinceBurnStart>345600){
            burnf = _plateauBurnFactor;
        } else{
            int128 _normalizedTime = ABDKMath64x64.divu(_timeSinceBurnStart,86400);
            int128 _halfLifeFraction = ABDKMath64x64.exp_2(_normalizedTime);
            
            // calculate the burn fraction with PRECISIONCONVERT
            int128 burnFraction = ABDKMath64x64.mul(ABDKMath64x64.div(ABDKMath64x64.fromUInt(50), _halfLifeFraction), ABDKMath64x64.fromUInt(_PRECISIONCONVERT));
            
            burnf = uint256(ABDKMath64x64.toUInt(burnFraction));
        }
        return burnf;
    }

    // starts the burn timer on initial liquidity provision using blocktime
    function startBurnTimer() external onlyOwner {
        require(!burnEnabled, "Burn can only be enabled once");
        burnStartTimeStamp = now;
        burnEnabled = true;
    }

}
/**
 * YOMAMA is so fat, she needed an entire maths library to provide the calculations
 * 
 * 
 */
function setStakingVaultAddress(address _address) external onlyOwner {
    stakingVaultAddress = _address;
}

// updates the staking vault address on contract deploy