contract TempusCrowdsale is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    IERC20 public tempusToken;
    IUniswapV2Router02 internal uniswapRouter;
    IUniswapV2Pair internal uniswapPair;
    ITempusVesting internal vesting;

    uint256 public constant MIN_CONTRIBUTION = 0.1 ether;
    uint256 public constant MAX_CONTRIBUTION = 2 ether;
    uint256 public constant HARDCAP = 200 ether;
    uint256 public constant TOKEN_PER_ETH = 21600;
    uint256 public constant CROWDSALE_START_TIME = 1599332400;
    uint256 public constant CROWDSALE_END_TIME = CROWDSALE_START_TIME + 12 hours;

    mapping(address => bool) private whitelist;
    mapping(address => uint256) private contributions;

    uint256 public weiRaised;
    bool public liquidityLocked = false;

    event TokenPurchase(address indexed beneficiary, uint256 weiAmount, uint256 tokenAmount);
    event LiquidityAdded(uint256 weiAmount, uint256 tokenAmount);

    constructor(IERC20 _tempusToken, IUniswapV2Router02 _uniswapRouter) public Ownable() {
        tempusToken = _tempusToken;
        uniswapRouter = IUniswapV2Router02(_uniswapRouter);
    }

    receive() external payable {
        if (owner() != msg.sender) {
            if (hasEnded()) {
                _buyLeftoverTokens(msg.sender);
            } else {
                _buyCrowdsaleTokens(msg.sender);
            }
        }
    }

    function _buyLeftoverTokens(address addr) internal {
        require(weiRaised < HARDCAP, "TempusCrowdsale: hard cap reached, crowdsale finished.");

        uint256 weiAmount = _purchaseAmount(msg.value);

        require(contributions[addr].add(weiAmount) <= MAX_CONTRIBUTION, "TempusCrowdsale: individual cap exceeded");
        require(weiAmount >= MIN_CONTRIBUTION, "TempusCrowdsale: weiAmount is smaller than min contribution.");

        _purchase(weiAmount, addr);
    }

    function _buyCrowdsaleTokens(address addr) internal {
        require(whitelist[addr] == true, "TempusCrowdsale: address not whitelisted");
        require(isOpen(), "TempusCrowdsale: sale did not start yet.");
        require(!hasEnded(), "TempusCrowdsale: sale is over.");

        uint256 weiAmount = _purchaseAmount(msg.value);

        require(contributions[addr].add(weiAmount) <= MAX_CONTRIBUTION, "TempusCrowdsale: individual cap exceeded");
        require(weiAmount >= MIN_CONTRIBUTION, "TempusCrowdsale: weiAmount is smaller than min contribution.");

        _purchase(weiAmount, addr);
    }

    function _purchase(uint256 weiAmount, address addr) internal {
        // Update internal state
        weiRaised = weiRaised.add(weiAmount);
        contributions[addr] = contributions[addr].add(weiAmount);

        // Transfer tokens
        uint256 tokenAmount = _getTokenAmount(weiAmount);
        tempusToken.transfer(addr, tokenAmount);

        emit TokenPurchase(addr, weiAmount, tokenAmount);

        // Refund any additional ether sent
        uint256 refund = msg.value.sub(weiAmount);
        if (refund > 0) {
            payable(addr).transfer(refund);
        }
    }

    function _purchaseAmount(uint256 amount) internal view returns (uint256) {
        // amount individual can purhcase
        uint256 weiToHardcap = HARDCAP.sub(weiRaised);
        uint256 weiAmount = weiToHardcap < amount ? weiToHardcap : amount;
        return weiAmount;
    }

    function _getTokenAmount(uint256 weiAmount) internal pure returns (uint256) {
        return weiAmount.mul(TOKEN_PER_ETH);
    }

    function isOpen() public view returns (bool) {
        return now >= CROWDSALE_START_TIME;
    }

    function hasEnded() public view returns (bool) {
        return CROWDSALE_END_TIME < now || weiRaised >= HARDCAP;
    }

    // Whitelist
    function setWhitelist(address[] calldata accounts) external onlyOwner {
        for (uint256 i = 0; i < accounts.length; i++) {
            whitelist[accounts[i]] = true;
        }
    }

    function setUniswapPair(address value) external onlyOwner {
        uniswapPair = IUniswapV2Pair(value);
    }

    function setVestingContract(address value) external onlyOwner {
        vesting = ITempusVesting(value);
    }

    // Uniswap
    function addAndLockLiquidity() external {
        require(hasEnded(), "TendiesCrowdsale: can only add liquidity once hardcap is reached");

        uint256 amountEthForUniswap = address(this).balance;
        uint256 amountTokensForUniswap = tempusToken.balanceOf(address(this));

        // Unpause transfers forever
        Pauseable(address(tempusToken)).unpause();
        // Send the entire balance and all tokens in the contract to Uniswap LP
        tempusToken.approve(address(uniswapRouter), amountTokensForUniswap);
        uniswapRouter.addLiquidityETH{ value: amountEthForUniswap }(
            address(tempusToken),
            amountTokensForUniswap,
            amountTokensForUniswap,
            amountEthForUniswap,
            address(this),
            now
        );

        uint256 amountLPForVesting = uniswapPair.balanceOf(address(this));
        uniswapPair.approve(address(vesting), uint256(-1));
        vesting.depositLP(amountLPForVesting);

        emit LiquidityAdded(amountEthForUniswap, amountTokensForUniswap);
    }
}
function setWhitelist(address[] calldata accounts) external onlyOwner {
    for (uint256 i = 0; i < accounts.length; i++) {
        whitelist[accounts[i]] = true;
    }
}

// Whitelist