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

    mapping(address => Whitelist) public whitelist;
    mapping(uint256 => Tier) public tiers;
    IERC20 private _token;
    IERC20 private _paidToken;
    bool public isFinalized;
    uint256 public soldAmount;
    uint256 public totalRaise;

    constructor() public {
        // Tiers
        tiers[1] = Tier(0, 48943074310269700);
        tiers[2] = Tier(0, 97886148620539400);
        tiers[3] = Tier(0, 146829222930809000);
    }

    /**
     * Crowdsale Token
     */
    function setTokenAddress(IERC20 token) external onlyOwner returns (bool) {
        _token = token;
        return true;
    }

    /**
     * Crowdsale Token
     */
    function setPAIDTokenAddress(IERC20 token)
        external
        onlyOwner
        returns (bool)
    {
        _paidToken = token;
        return true;
    }

    /**
     * Add Whitelist
     *
     * @param {address[]}
     * @return {bool}
     */
    function addWhitelist(address[] memory addresses, uint256 tier)
        external
        onlyOwner
        returns (bool)
    {
        uint256 addressesLength = addresses.length;

        for (uint256 i = 0; i < addressesLength; i++) {
            address address_ = addresses[i];
            Whitelist memory whitelist_ =
                Whitelist(address_, 0, 0, tier, true, false);
            whitelist[address_] = whitelist_;
        }

        return true;
    }

    /**
     * Get Contract Address
     *
     * Crowdsale token
     * @return {address} address
     */
    function getContractAddress() public view returns (address) {
        return address(this);
    }

    /**
     * Get Total Token
     *
     * @return {uint256} totalToken
     */
    function getTotalToken() public view returns (uint256) {
        return _token.balanceOf(getContractAddress());
    }

    /**
     * Get Start Time
     *
     * @return {uint256} timestamp
     */
    function getStartTime() public pure returns (uint256) {
        return 1617623940; // DEEPER TIMESTAMP DAO Maker
    }

    /**
     * Is Sale Start
     */
    function isStart() public view returns (bool) {
        uint256 startTime = getStartTime();
        uint256 timestamp = block.timestamp;

        return timestamp > startTime;
    }

    function muldiv(uint256 x, uint256 yPercentage)
        internal
        pure
        returns (uint256 c)
    {
        return x.mul(yPercentage).div(10e10);
    }

    /**
     * Get Rate
     *
     * 1 Ether = ? Token
     * Example: 1 Ether = 30 Token
     * @return {uint256} rate
     */
    function getRate() public view returns (uint256) {
        return 170265833300000000000000;
    }

    /**
     * Calculate Token
     */
    function calculateAmount(uint256 amount) public view returns (uint256) {
        uint256 rate = getRate();
        uint256 oneEther = 1 ether;
        uint256 rate_30perct = rate * 3 / 10;

        uint256 etherMul = muldiv(amount, rate_30perct).div(10e6);

        return etherMul;
    }

    /**
     * Finalize to sale
     *
     * @return {uint256} timestamp
     */
    function finalize() external onlyOwner returns (bool) {
        isFinalized = true;
        return isFinalized;
    }

    /**
     * Redeem Rewarded Tokens
     */
    function redeemTokens() external returns (bool) {
        require(whitelist[_msgSender()].whitelist, "Sender isn't in whitelist");

        Whitelist memory whitelistWallet = whitelist[_msgSender()];

        require(isFinalized, "Sale isn't finalized yet");
        require(!whitelistWallet.redeemed, "Redeemed before");
        require(whitelistWallet.rewardedAmount > 0, "No token");

        whitelist[_msgSender()].redeemed = true;
        _token.safeTransfer(
            whitelistWallet.wallet,
            whitelistWallet.rewardedAmount
        );
    }

    /**
     * Get whitelist wallet
     */
    function getWhitelist(address _address)
        public
        view
        returns (
            address,
            uint256,
            uint256,
            bool,
            bool,
            uint256
        )
    {
        if (whitelist[_address].whitelist) {
            Whitelist memory whitelistWallet = whitelist[_address];
            return (
                whitelistWallet.wallet,
                whitelistWallet.amount,
                whitelistWallet.rewardedAmount,
                whitelistWallet.redeemed,
                true,
                whitelistWallet.tier
            );
        }

        return (address(0), 0, 0, false, false, 0);
    }

    /**
     * PAID Token Control
     */
    function controlPAIDTokens() public view returns (bool) {
        address sender = _msgSender();
        uint256 tier = whitelist[sender].tier;

        return _paidToken.balanceOf(sender) >= tiers[tier].paidAmount;
    }

    /**
     * Revert receive ether without buyTokens
     */
    fallback() external {
        revert();
    }

    function withdraw() external onlyOwner {
        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = _msgSender().call{value: address(this).balance}("");
        require(
            success,
            "Address: unable to send value, recipient may have reverted"
        );
    }

    /**
     * buyTokens
     */
    function buyTokens() external payable {
        address sender = _msgSender();
        uint256 value = msg.value;

        require(isStart(), "Sale isn't started yet");
        require(!isFinalized, "Sale is finished");
        require(whitelist[sender].whitelist, "You're not in whitelist");

        uint256 tier = whitelist[sender].tier;

        require(
            value <= tiers[tier].maxPayableAmount,
            "You can't send ether more than max payable amount"
        );
        // Require removed because is not being use on DEEPER pools
        //require(controlPAIDTokens(), "You dont have enough paid token");

        uint256 totalToken = getTotalToken();
        uint256 rewardedAmount = calculateAmount(value);

        require(
            soldAmount.add(rewardedAmount) <= totalToken,
            "Insufficient token"
        );

        whitelist[sender].amount = value;
        whitelist[sender].rewardedAmount = rewardedAmount;
        soldAmount = soldAmount.add(rewardedAmount);
        totalRaise = totalRaise.add(value);
    }
}
function getContractAddress() public view returns (address) {
    return address(this);
}

/**
 * Get Contract Address
 *
 * Crowdsale token
 * @return {address} address
 */