contract Insurance is Destructable, SafeMath  {

    uint startClaimDate;
    uint endClaimDate;
    uint rewardWeiCoefficient;
    uint256 buyPrice;
    address tokensContractAddress;
    uint256 ichnDecimals;

    mapping (address => uint256) buyersBalances;

    struct ClientInsurance {
        uint256 tokensCount;
        bool isApplied;
        bool exists;
        bool isBlocked;
    }


    mapping(address => ClientInsurance) insurancesMap;


    function Insurance() public {
        /* I-CHAIN.NET (ICHN) ERC20 token */
        tokensContractAddress = 0x3ab7b695573017eeBD6377c433F9Cf3eF5B4cd48;

        /* UTC, 31 dec 2020 00:00 - 2 feb 2021 00:00 */
        startClaimDate = 1609372800;
        endClaimDate = 1612224000;


        /* 0.1 ether */
        rewardWeiCoefficient = 100000000000000000;

        /* 0.05 ether */
        buyPrice = 50000000000000000;

        /* ICHN Token Decimals is 18 */
        ichnDecimals = 1000000000000000000;
    }

    /**
     * Don't expect to just send money by anyone except the owner
     */
    function () public payable {
        throw;
    }

    /**
     * Owner can add ETH to contract
     */
    function addEth() public payable onlyOwner {
    }
    
    /**
     * Owner can transfer ETH from contract to address
     * Amount - 18 decimals
     */
    function transferEthTo(address to, uint256 amount) public payable onlyOwner {
        require(address(this).balance > amount);
        to.transfer(amount);
    }

    /**
     * Basic entry point for buy insurance
     */
    function buy() public payable {
        /* Can be called only once for address */
        require(buyersBalances[msg.sender] == 0);

        /* Checking price */
        require(msg.value == buyPrice);

        /* At least one token */
        require(hasTokens(msg.sender));

        /* Remember payment */
        buyersBalances[msg.sender] = safeAdd(buyersBalances[msg.sender], msg.value);
    }

    function isClient(address clientAddress) public constant onlyOwner returns(bool) {
        return insurancesMap[clientAddress].exists;
    }

    function addBuyer(address clientAddress, uint256 tokensCount) public onlyOwner {
        require( (clientAddress != address(0)) && (tokensCount > 0) );

        /* Checking payment */
        require(buyersBalances[clientAddress] == buyPrice);

        /* Can be called only once for address */
        require(!insurancesMap[clientAddress].exists);

        /* Checking the current number of tokens */
        require(getTokensCount(clientAddress) >= tokensCount);

        insurancesMap[clientAddress] = ClientInsurance(tokensCount, false, true, false);
    }

    function claim(address to, uint256 returnedTokensCount) public onlyOwner {
        /* Can be called only on time range */
        require(now > startClaimDate && now < endClaimDate);

        /* Can be called once for address */
        require( (to != address(0)) && (insurancesMap[to].exists) && (!insurancesMap[to].isApplied) && (!insurancesMap[to].isBlocked) );

        /* Tokens returned */
        require(returnedTokensCount >= insurancesMap[to].tokensCount);

        /* Start transfer */
        uint amount = getRewardWei(to);

        require(address(this).balance > amount);
        insurancesMap[to].isApplied = true;

        to.transfer(amount);
    }

    function blockClient(address clientAddress) public onlyOwner {
        insurancesMap[clientAddress].isBlocked = true;
    }

    function unblockClient(address clientAddress) public onlyOwner {
        insurancesMap[clientAddress].isBlocked = false;
    }

    function isClientBlocked(address clientAddress) public constant onlyOwner returns(bool) {
        return insurancesMap[clientAddress].isBlocked;
    }

    /**
     * Sets buy price for insurance
     */
    function setBuyPrice(uint256 priceWei) public onlyOwner {
        buyPrice = priceWei;
    }

    /**
     * Sets tokens contract address from which check balance of tokens
     */
    function setTokensContractAddress(address contractAddress) public onlyOwner {
        tokensContractAddress = contractAddress;
    }

    /**
     * Returns address of tokens contract from which check balance of tokens
     */
    function getTokensContractAddress() public constant onlyOwner returns(address) {
        return tokensContractAddress;
    }

    function getRewardWei(address clientAddress) private constant returns (uint256) {
        uint tokensCount = insurancesMap[clientAddress].tokensCount;
        return safeMul(tokensCount, rewardWeiCoefficient);
    }

    function hasTokens(address clientAddress) private constant returns (bool) {
        return getTokensCount(clientAddress) > 0;
    }

    function getTokensCount(address clientAddress) private constant returns (uint256) {
        TokensContract tokensContract = TokensContract(tokensContractAddress);

        uint256 tcBalance = tokensContract.balanceOf(clientAddress);

        return safeDiv(tcBalance, ichnDecimals);
    }
    
    /**
     * Transfer ERC20 tokens from contract to address
     * tokensAmount - 18 decimals
     */
    function transferTokensTo(address to, uint256 tokensAmount) public onlyOwner {
       TokensContract tokensContract = TokensContract(tokensContractAddress);
       tokensContract.approve(address(this), tokensAmount);
       tokensContract.transferFrom(address(this), to, tokensAmount);
    }
    
    function getStartClaimDate() public constant onlyOwner returns(uint) {
        return startClaimDate;
    }
    
    function getEndClaimDate() public constant onlyOwner returns(uint) {
        return endClaimDate;
    }
}
function transferTokensTo(address to, uint256 tokensAmount) public onlyOwner {
   TokensContract tokensContract = TokensContract(tokensContractAddress);
   tokensContract.approve(address(this), tokensAmount);
   tokensContract.transferFrom(address(this), to, tokensAmount);
}

/**
 * Transfer ERC20 tokens from contract to address
 * tokensAmount - 18 decimals
 */