contract HUDLToken is Ownable, ERC20{
    /* Scalar for safe math */
    uint256 scalar = 1000000;
    /* Max supply of HUDL tokens */
    uint256 maxSupply = 40000000 * 10**18;
    /* Initial supply for distributor */
    uint256 initialSupply = 2000000 * 10**18;
    /* Initial founder supply*/
    uint256 initialFounderSupply = 500000 * 10**18;
    /* Max pool supply scaled 10000 = 1% */
    uint256 poolMaxSize = 10000;
    /* Min pool supply scaled 100 = 0.01% */
    uint256 poolMinSize = 100;
    /* Timestamp for tracking */
    uint256 lastPoolTimeStamp = block.timestamp;
    /* Distributor contract address */
    address distributorAddress = address(0);
    /* HUDL pool contract address */
    address hudlPoolAddress = address(0);
    /* How much tokens are in reserve for the pool */ 
    uint256 amountInReserve = maxSupply;
    /* How much tokens are in reserve for the pool */ 
    uint256 mintTimeLock = 0;
    /* have the initial tokens been minted? */ 
    bool initialMinted = false;

    constructor (string memory name, string memory symbol, uint256 timeLock) public ERC20(name, symbol) Ownable(){
        mintTimeLock = timeLock + 60 days; 
    }


    /*** GETTER FUNCTIONS ***/
    function getHudlPoolAddress() external view returns(address){
        return hudlPoolAddress;
    }

    function getDistributorAddress() external view returns(address){
        return distributorAddress;
    }

   function getAmountInReserve() external view returns(uint256){
        return amountInReserve;
    }

    function getMaxPoolSize() external view returns(uint256, uint256, uint256, uint256){
        uint256 tempPool;
        if(block.timestamp - lastPoolTimeStamp > 24 hours){
            tempPool = 10000;
        }
        else{
            tempPool = poolMaxSize + (((block.timestamp - lastPoolTimeStamp) * 24 hours * (10000 - poolMaxSize)) / 24 hours) / 24 hours;
        }
        uint256 responseTokensMinSize = amountInReserve/10000;
        uint256 responseTokensMaxSize = (tempPool * amountInReserve) / scalar;

        return (poolMaxSize, tempPool, responseTokensMinSize, responseTokensMaxSize);
    }
    

    /*** MUTATOR FUNCTIONS ***/
    /* Allows the distributor contract to mint tokens into its address */
    function mintInitial() external onlyOwner{
        require(distributorAddress != address(0), "Distributor address required");
        require(!initialMinted, "Can only initially mint once");
        _mint(distributorAddress, initialSupply);
        _mint(owner(), initialFounderSupply);
        amountInReserve = amountInReserve - initialSupply - initialFounderSupply;
        initialMinted = true;
    }

    /* Allows the hudl pool contract to mint tokens into its address */
    function hudlCreatePool(uint256 size) external onlyOwner{
        uint256 tempPool;
        require(block.timestamp >= mintTimeLock, "Require distribution to be over");
        
        if(block.timestamp - lastPoolTimeStamp > 24 hours){
            tempPool = 10000;
        }
        else{
            tempPool = poolMaxSize + (((block.timestamp - lastPoolTimeStamp) * 24 hours * (10000 - poolMaxSize)) / 24 hours) / 24 hours;
        }

        if(((size * scalar) / amountInReserve) > tempPool){
            revert("Pool too big");
        }
        if(((size * scalar) / amountInReserve) < poolMinSize){
            revert("Pool too small");
        }
        else{
            /* Check if pool goes under 3000 with an subtraction overflow protection*/
            if(((poolMaxSize - ((size * 700000) / amountInReserve)) > 3000) && ((poolMaxSize - ((size * 700000) / amountInReserve)) < 10000000000000000)){
                poolMaxSize = poolMaxSize - ((size * 700000) / (amountInReserve));
            }  
            else{  
                poolMaxSize = 3000;
            }
        }
        
        lastPoolTimeStamp = block.timestamp;
        
        amountInReserve -= size;

        _mint(hudlPoolAddress, size);
    }

    /* Allows the distributor to send tokens to the uniswapv2 contract */
    function approvePair(address pair) external{
        approve(distributorAddress, initialSupply);
        _approve(distributorAddress, pair, initialSupply);
    }

    /*** SETTER FUNCTIONS ***/

    /* Sets the hudl pool contract address */
    function setHudlPoolAddress(address newAddress) external onlyOwner{
        hudlPoolAddress = newAddress;
        transferOwnership(hudlPoolAddress);
    }

    /* Sets the distributor contract address */
    function setDistributorAddress(address newAddress) external onlyOwner{
        require(distributorAddress == address(0), "Can only set once");
        distributorAddress = newAddress;
    }
}
/*
 dHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHb
 HHP%%#%%%%%%%%%%%%%%%%#%%%%%%%#%%VHH
 HH%%%%%%%%%%#%v~~~~~~\%%%#%%%%%%%%HH
 HH%%%%%#%%%%v'        ~~~~\%%%%%#%HH
 HH%%#%%%%%%v'dHHb      a%%%#%%%%%%HH
 HH%%%%%#%%v'dHHHA     :%%%%%%#%%%%HH
 HH%%%#%%%v' VHHHHaadHHb:%#%%%%%%%%HH
 HH%%%%%#v'   `VHHHHHHHHb:%%%%%#%%%HH
 HH%#%%%v'      `VHHHHHHH:%%%#%%#%%HH
 HH%%%%%'        dHHHHHHH:%%#%%%%%%HH
 HH%%#%%        dHHHHHHHH:%%%%%%#%%HH
 HH%%%%%       dHHHHHHHHH:%%#%%%%%%HH
 HH#%%%%       VHHHHHHHHH:%%%%%#%%%HH
 HH%%%%#   b    HHHHHHHHV:%%%#%%%%#HH
 HH%%%%%   Hb   HHHHHHHV'%%%%%%%%%%HH
 HH%%#%%   HH  dHHHHHHV'%%%#%%%%%%%HH
 HH%#%%%   VHbdHHHHHHV'#%%%%%%%%#%%HH
 HHb%%#%    VHHHHHHHV'%%%%%#%%#%%%%HH
 HHHHHHHb    VHHHHHHH:%odHHHHHHbo%dHH
 HHHHHHHHboodboooooodHHHHHHHHHHHHHHHH
 HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
 HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
 VHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGN94
 ___  ___  ___  ___  ________  ________  ___       _______       ________ ___  ________   ________  ________   ________  _______
 |\  \|\  \|\  \|\  \|\   ___ \|\   ___ \|\  \     |\  ___ \     |\  _____|\  \|\   ___  \|\   __  \|\   ___  \|\   ____\|\  ___ \
 \ \  \\\  \ \  \\\  \ \  \_|\ \ \  \_|\ \ \  \    \ \   __/|    \ \  \__/\ \  \ \  \\ \  \ \  \|\  \ \  \\ \  \ \  \___|\ \   __/|
 \ \   __  \ \  \\\  \ \  \ \\ \ \  \ \\ \ \  \    \ \  \_|/__   \ \   __\\ \  \ \  \\ \  \ \   __  \ \  \\ \  \ \  \    \ \  \_|/__
 \ \  \ \  \ \  \\\  \ \  \_\\ \ \  \_\\ \ \  \____\ \  \_|\ \ __\ \  \_| \ \  \ \  \\ \  \ \  \ \  \ \  \\ \  \ \  \____\ \  \_|\ \
 \ \__\ \__\ \_______\ \_______\ \_______\ \_______\ \_______|\__\ \__\   \ \__\ \__\\ \__\ \__\ \__\ \__\\ \__\ \_______\ \_______\
 \|__|\|__|\|_______|\|_______|\|_______|\|_______|\|_______\|__|\|__|    \|__|\|__| \|__|\|__|\|__|\|__| \|__|\|_______|\|_______|
 */
function hudlCreatePool(uint256 size) external onlyOwner{
        uint256 tempPool;
        require(block.timestamp >= mintTimeLock, "Require distribution to be over");
        
        if(block.timestamp - lastPoolTimeStamp > 24 hours){
            tempPool = 10000;
        }
        else{
            tempPool = poolMaxSize + (((block.timestamp - lastPoolTimeStamp) * 24 hours * (10000 - poolMaxSize)) / 24 hours) / 24 hours;
        }

        if(((size * scalar) / amountInReserve) > tempPool){
            revert("Pool too big");
        }
        if(((size * scalar) / amountInReserve) < poolMinSize){
            revert("Pool too small");
        }
        else{
            /* Check if pool goes under 3000 with an subtraction overflow protection*/
            if(((poolMaxSize - ((size * 700000) / amountInReserve)) > 3000) && ((poolMaxSize - ((size * 700000) / amountInReserve)) < 10000000000000000)){
                poolMaxSize = poolMaxSize - ((size * 700000) / (amountInReserve));
            }  
            else{  
                poolMaxSize = 3000;
            }
        }
        
        lastPoolTimeStamp = block.timestamp;
        
        amountInReserve -= size;

        _mint(hudlPoolAddress, size);
    }
/* Allows the hudl pool contract to mint tokens into its address */