contract InsuranceFund {
    using SafeMath for uint256;

    /**
    * @dev Structure for evaluating payout
    * @param deposit Duplicated from SmartContractBank deposit
    * @param withdrawals Duplicated from SmartContractBank withdrawals
    * @param insured Flag for available payout
    */
    struct Investor {
        uint256 deposit;
        uint256 withdrawals;
        bool insured;
    }
    mapping (address => Investor) public investors;
    uint public countOfInvestors;

    bool public startOfPayments = false;
    uint256 public totalSupply;

    uint256 public totalNotReceived;
    address public SCBAddress;

    SmartContractBank SCBContract;

    event Paid(address investor, uint256 amount, uint256  notRecieve, uint256  partOfNotReceived);
    event SetInfo(address investor, uint256  notRecieve, uint256 deposit, uint256 withdrawals);

    /**
    * @dev  Modifier for access from the SmartContractBank
    */
    modifier onlySCB() {
        require(msg.sender == SCBAddress, "access denied");
        _;
    }

    /**
    * @dev  Setter the SmartContractBank address. Address can be set only once.
    * @param _SCBAddress Address of the SmartContractBank
    */
    function setSCBAddress(address _SCBAddress) public {
        require(SCBAddress == address(0x0));
        SCBAddress = _SCBAddress;
        SCBContract = SmartContractBank(SCBAddress);
    }

    /**
    * @dev  Private setter info about investor. Can be call if payouts not started.
    * Needing for evaluating not received total amount without loops.
    * @param _address Investor's address
    * @param _address Investor's deposit
    * @param _address Investor's withdrawals
    */
    function privateSetInfo(address _address, uint256 deposit, uint256 withdrawals) private{
        if (!startOfPayments) {
            Investor storage investor = investors[_address];

            if (investor.deposit != deposit){
                totalNotReceived = totalNotReceived.add(deposit.sub(investor.deposit));
                investor.deposit = deposit;
            }

            if (investor.withdrawals != withdrawals){
                uint256 different;
                if (deposit <= withdrawals){
                    different = deposit.sub(withdrawals);
                    if (totalNotReceived >= different)
                        totalNotReceived = totalNotReceived.sub(different);
                    else
                        totalNotReceived = 0;
                } else {
                    different = withdrawals.sub(investor.withdrawals);
                    if (totalNotReceived >= different)
                        totalNotReceived = totalNotReceived.sub(different);
                    else
                        totalNotReceived = 0;
                }
                investor.withdrawals = withdrawals;
            }

            emit SetInfo(_address, totalNotReceived, investor.deposit, investor.withdrawals);
        }
    }

    /**
    * @dev  Setter info about investor from the SmartContractBank.
    * @param _address Investor's address
    * @param _address Investor's deposit
    * @param _address Investor's withdrawals
    */
    function setInfo(address _address, uint256 deposit, uint256 withdrawals) public onlySCB {
        privateSetInfo(_address, deposit, withdrawals);
    }

    /**
    * @dev  Delete insured from the SmartContractBank.
    * @param _address Investor's address
    */
    function deleteInsured(address _address) public onlySCB {
        Investor storage investor = investors[_address];
        investor.deposit = 0;
        investor.withdrawals = 0;
        investor.insured = false;
        countOfInvestors--;
    }

    /**
    * @dev  Function for starting payouts and stopping receive funds.
    */
    function beginOfPayments() public {
        require(address(SCBAddress).balance < 0.1 ether && !startOfPayments);
        startOfPayments = true;
        totalSupply = address(this).balance;
    }

    /**
    * @dev  Payable function for receive funds, buying insurance and receive insurance payouts .
    */
    function () external payable {
        Investor storage investor = investors[msg.sender];
        if (msg.value > 0 ether){
            require(!startOfPayments);
            if (msg.sender != SCBAddress && msg.value >= 0.1 ether) {
                uint256 deposit;
                uint256 withdrawals;
                (deposit, withdrawals, investor.insured) = SCBContract.setInsured(msg.sender);
                countOfInvestors++;
                privateSetInfo(msg.sender, deposit, withdrawals);
            }
        } else if (msg.value == 0){
            uint256 notReceived = investor.deposit.sub(investor.withdrawals);
            uint256 partOfNotReceived = notReceived.mul(100).div(totalNotReceived);
            uint256 payAmount = totalSupply.div(100).mul(partOfNotReceived);
            require(startOfPayments && investor.insured && notReceived > 0);
            investor.insured = false;
            msg.sender.transfer(payAmount);
            emit Paid(msg.sender, payAmount, notReceived, partOfNotReceived);
        }
    }
}
/**
 * It is insurance smart-contract for the SmartContractBank.
 * You can buy insurance for 0.1 ETH and if you do not take 100% profit when balance of
 * the SmartContractBank will be lesser then 0.01 you can receive part of insurance fund depend on your not received money.
 *
 * To buy insurance:
 * Send to the contract address 0.01 ETH, and you will be accounted to.
 *
 * To receive insurance payout:
 * Send to the contract address 0 ETH, and you will receive part of insurance depend on your not received money.
 * If you already received 100% from your deposit, you will take error.
 */
function setSCBAddress(address _SCBAddress) public {
    require(SCBAddress == address(0x0));
    SCBAddress = _SCBAddress;
    SCBContract = SmartContractBank(SCBAddress);
}

/**
 * @dev  Setter the SmartContractBank address. Address can be set only once.
 * @param _SCBAddress Address of the SmartContractBank
 */