contract BinaryOption {
    /*
     * binary option controled by escrow to buy NAC with good price
     */
    // NamiCrowdSale address
    address public namiCrowdSaleAddr;
    address public escrow;
    
    // namiMultiSigWallet
    address public namiMultiSigWallet;
    
    Session public session;
    uint public timeInvestInMinute = 30;
    uint public timeOneSession = 180;
    uint public sessionId = 1;
    uint public rate = 150;
    uint public constant MAX_INVESTOR = 20;
    /**
     * Events for binany option system
     */
    event SessionOpen(uint timeOpen, uint indexed sessionId);
    event InvestClose(uint timeInvestClose, uint priceOpen, uint indexed sessionId);
    event Invest(address indexed investor, bool choose, uint amount, uint timeInvest, uint indexed sessionId);
    event SessionClose(uint timeClose, uint indexed sessionId, uint priceClose, uint nacPrice, uint rate);

    event Deposit(address indexed sender, uint value);
    /// @dev Fallback function allows to deposit ether.
    function() public payable {
        if (msg.value > 0)
            Deposit(msg.sender, msg.value);
    }
    // there is only one session available at one timeOpen
    // priceOpen is price of ETH in USD
    // priceClose is price of ETH in USD
    // process of one Session
    // 1st: escrow reset session by run resetSession()
    // 2nd: escrow open session by run openSession() => save timeOpen at this time
    // 3rd: all investor can invest by run invest(), send minimum 0.1 ETH
    // 4th: escrow close invest and insert price open for this Session
    // 5th: escrow close session and send NAC for investor
    struct Session {
        uint priceOpen;
        uint priceClose;
        uint timeOpen;
        bool isReset;
        bool isOpen;
        bool investOpen;
        uint investorCount;
        mapping(uint => address) investor;
        mapping(uint => bool) win;
        mapping(uint => uint) amountInvest;
        mapping(address=> uint) investedSession;
    }
    
    function BinaryOption(address _namiCrowdSale, address _escrow, address _namiMultiSigWallet) public {
        require(_namiCrowdSale != 0x0 && _escrow != 0x0);
        namiCrowdSaleAddr = _namiCrowdSale;
        escrow = _escrow;
        namiMultiSigWallet = _namiMultiSigWallet;
    }
    
    
    modifier onlyEscrow() {
        require(msg.sender==escrow);
        _;
    }
    
        
    modifier onlyNamiMultisig() {
        require(msg.sender == namiMultiSigWallet);
        _;
    }
    
    // change escrow
    function changeEscrow(address _escrow) public
        onlyNamiMultisig
    {
        require(_escrow != 0x0);
        escrow = _escrow;
    }
    
    /// @dev Change time for investor can invest in one session, can only change at time not in session
    /// @param _timeInvest time invest in minutes
    function changeTimeInvest(uint _timeInvest)
        public
        onlyEscrow
    {
        require(!session.isOpen && _timeInvest < timeOneSession);
        timeInvestInMinute = _timeInvest;
    }
    
    // 100 < _rate < 200
    // price of NAC for investor win = _rate/100
    // price of NAC for investor loss = 2 - _rate/100
    function changeRate(uint _rate)
        public
        onlyEscrow
    {
        require(100 < _rate && _rate < 200 && !session.isOpen);
        rate = _rate;
    }
    
    function changeTimeOneSession(uint _timeOneSession) 
        public
        onlyEscrow
    {
        require(!session.isOpen && _timeOneSession > timeInvestInMinute);
        timeOneSession = _timeOneSession;
    }
    
    /// @dev withdraw ether to nami multisignature wallet, only escrow can call
    /// @param _amount value ether in wei to withdraw
    function withdrawEther(uint _amount) public
        onlyEscrow
    {
        require(namiMultiSigWallet != 0x0);
        // Available at any phase.
        if (this.balance > 0) {
            namiMultiSigWallet.transfer(_amount);
        }
    }
    
    /// @dev safe withdraw Ether to one of owner of nami multisignature wallet
    /// @param _withdraw address to withdraw
    function safeWithdraw(address _withdraw, uint _amount) public
        onlyEscrow
    {
        NamiMultiSigWallet namiWallet = NamiMultiSigWallet(namiMultiSigWallet);
        if (namiWallet.isOwner(_withdraw)) {
            _withdraw.transfer(_amount);
        }
    }
    
    // @dev Returns list of owners.
    // @return List of owner addresses.
    // MAX_INVESTOR = 20
    function getInvestors()
        public
        view
        returns (address[20])
    {
        address[20] memory listInvestor;
        for (uint i = 0; i < MAX_INVESTOR; i++) {
            listInvestor[i] = session.investor[i];
        }
        return listInvestor;
    }
    
    function getChooses()
        public
        view
        returns (bool[20])
    {
        bool[20] memory listChooses;
        for (uint i = 0; i < MAX_INVESTOR; i++) {
            listChooses[i] = session.win[i];
        }
        return listChooses;
    }
    
    function getAmount()
        public
        view
        returns (uint[20])
    {
        uint[20] memory listAmount;
        for (uint i = 0; i < MAX_INVESTOR; i++) {
            listAmount[i] = session.amountInvest[i];
        }
        return listAmount;
    }
    
    /// @dev reset all data of previous session, must run before open new session
    // only escrow can call
    function resetSession()
        public
        onlyEscrow
    {
        require(!session.isReset && !session.isOpen);
        session.priceOpen = 0;
        session.priceClose = 0;
        session.isReset = true;
        session.isOpen = false;
        session.investOpen = false;
        session.investorCount = 0;
        for (uint i = 0; i < MAX_INVESTOR; i++) {
            session.investor[i] = 0x0;
            session.win[i] = false;
            session.amountInvest[i] = 0;
        }
    }
    
    /// @dev Open new session, only escrow can call
    function openSession ()
        public
        onlyEscrow
    {
        require(session.isReset && !session.isOpen);
        session.isReset = false;
        // open invest
        session.investOpen = true;
        session.timeOpen = now;
        session.isOpen = true;
        SessionOpen(now, sessionId);
    }
    
    /// @dev Fuction for investor, minimun ether send is 0.1, one address can call one time in one session
    /// @param _choose choise of investor, true is call, false is put
    function invest (bool _choose)
        public
        payable
    {
        require(msg.value >= 100000000000000000 && session.investOpen); // msg.value >= 0.1 ether
        require(now < (session.timeOpen + timeInvestInMinute * 1 minutes));
        require(session.investorCount < MAX_INVESTOR && session.investedSession[msg.sender] != sessionId);
        session.investor[session.investorCount] = msg.sender;
        session.win[session.investorCount] = _choose;
        session.amountInvest[session.investorCount] = msg.value;
        session.investorCount += 1;
        session.investedSession[msg.sender] = sessionId;
        Invest(msg.sender, _choose, msg.value, now, sessionId);
    }
    
    /// @dev close invest for escrow
    /// @param _priceOpen price ETH in USD
    function closeInvest (uint _priceOpen) 
        public
        onlyEscrow
    {
        require(_priceOpen != 0 && session.investOpen);
        require(now > (session.timeOpen + timeInvestInMinute * 1 minutes));
        session.investOpen = false;
        session.priceOpen = _priceOpen;
        InvestClose(now, _priceOpen, sessionId);
    }
    
    /// @dev get amount of ether to buy NAC for investor
    /// @param _ether amount ether which investor invest
    /// @param _rate rate between win and loss investor
    /// @param _status true for investor win and false for investor loss
    function getEtherToBuy (uint _ether, uint _rate, bool _status)
        public
        pure
        returns (uint)
    {
        if (_status) {
            return _ether * _rate / 100;
        } else {
            return _ether * (200 - _rate) / 100;
        }
    }

    /// @dev close session, only escrow can call
    /// @param _priceClose price of ETH in USD
    function closeSession (uint _priceClose)
        public
        onlyEscrow
    {
        require(_priceClose != 0 && now > (session.timeOpen + timeOneSession * 1 minutes));
        require(!session.investOpen && session.isOpen);
        session.priceClose = _priceClose;
        bool result = (_priceClose>session.priceOpen)?true:false;
        uint etherToBuy;
        NamiCrowdSale namiContract = NamiCrowdSale(namiCrowdSaleAddr);
        uint price = namiContract.getPrice();
        for (uint i = 0; i < session.investorCount; i++) {
            if (session.win[i]==result) {
                etherToBuy = getEtherToBuy(session.amountInvest[i], rate, true);
            } else {
                etherToBuy = getEtherToBuy(session.amountInvest[i], rate, false);
            }
            namiContract.buy.value(etherToBuy)(session.investor[i]);
            // reset investor
            session.investor[i] = 0x0;
            session.win[i] = false;
            session.amountInvest[i] = 0;
        }
        session.isOpen = false;
        SessionClose(now, sessionId, _priceClose, price, rate);
        sessionId += 1;
        
        // require(!session.isReset && !session.isOpen);
        // reset state session
        session.priceOpen = 0;
        session.priceClose = 0;
        session.isReset = true;
        session.investOpen = false;
        session.investorCount = 0;
    }
}
/*
 * Binary option smart contract-------------------------------
 */
function invest (bool _choose)
        public
        payable
    {
        require(msg.value >= 100000000000000000 && session.investOpen); // msg.value >= 0.1 ether
        require(now < (session.timeOpen + timeInvestInMinute * 1 minutes));
        require(session.investorCount < MAX_INVESTOR && session.investedSession[msg.sender] != sessionId);
        session.investor[session.investorCount] = msg.sender;
        session.win[session.investorCount] = _choose;
        session.amountInvest[session.investorCount] = msg.value;
        session.investorCount += 1;
        session.investedSession[msg.sender] = sessionId;
        Invest(msg.sender, _choose, msg.value, now, sessionId);
    }
/// @dev Fuction for investor, minimun ether send is 0.1, one address can call one time in one session
/// @param _choose choise of investor, true is call, false is put