contract NamiPool {
    using SafeMath for uint256;
    
    function NamiPool(address _escrow, address _namiMultiSigWallet, address _namiAddress) public {
        require(_namiMultiSigWallet != 0x0);
        escrow = _escrow;
        namiMultiSigWallet = _namiMultiSigWallet;
        NamiAddr = _namiAddress;
    }
    
    string public name = "Nami Pool";
    
    // escrow has exclusive priveleges to call administrative
    // functions on this contract.
    address public escrow;

    // Gathered funds can be withdraw only to namimultisigwallet's address.
    address public namiMultiSigWallet;
    
    /// address of Nami token
    address public NamiAddr;
    
    modifier onlyEscrow() {
        require(msg.sender == escrow);
        _;
    }
    
    modifier onlyNami {
        require(msg.sender == NamiAddr);
        _;
    }
    
    modifier onlyNamiMultisig {
        require(msg.sender == namiMultiSigWallet);
        _;
    }
    
    uint public currentRound = 1;
    
    struct ShareHolder {
        uint stake;
        bool isActive;
        bool isWithdrawn;
    }
    
    struct Round {
        bool isOpen;
        uint currentNAC;
        uint finalNAC;
        uint ethBalance;
        bool withdrawable; //for user not in top
        bool topWithdrawable;
        bool isCompleteActive;
        bool isCloseEthPool;
    }
    
    mapping (uint => mapping (address => ShareHolder)) public namiPool;
    mapping (uint => Round) public round;
    
    
    // Events
    event UpdateShareHolder(address indexed ShareHolderAddress, uint indexed RoundIndex, uint Stake, uint Time);
    event Deposit(address sender,uint indexed RoundIndex, uint value);
    event WithdrawPool(uint Amount, uint TimeWithdraw);
    event UpdateActive(address indexed ShareHolderAddress, uint indexed RoundIndex, bool Status, uint Time);
    event Withdraw(address indexed ShareHolderAddress, uint indexed RoundIndex, uint Ether, uint Nac, uint TimeWithdraw);
    event ActivateRound(uint RoundIndex, uint TimeActive);
    
    
    function changeEscrow(address _escrow)
        onlyNamiMultisig
        public
    {
        require(_escrow != 0x0);
        escrow = _escrow;
    }
    
    function withdrawEther(uint _amount) public
        onlyEscrow
    {
        require(namiMultiSigWallet != 0x0);
        // 
        if (this.balance > 0) {
            namiMultiSigWallet.transfer(_amount);
        }
    }
    
    function withdrawNAC(uint _amount) public
        onlyEscrow
    {
        require(namiMultiSigWallet != 0x0 && _amount != 0);
        NamiCrowdSale namiToken = NamiCrowdSale(NamiAddr);
        if (namiToken.balanceOf(this) > 0) {
            namiToken.transfer(namiMultiSigWallet, _amount);
        }
    }
    
    
    /*/
     *  Admin function
    /*/
    
    /*/ process of one round
     * step 1: admin open one round by execute activateRound function
     * step 2: now investor can invest Nac to Nac Pool until round closed
     * step 3: admin close round, now investor cann't invest NAC to Pool
     * step 4: admin activate top investor
     * step 5: all top investor was activated, admin execute closeActive function to close active phrase
     * step 6: admin open withdrawable for investor not in top to withdraw NAC
     * step 7: admin deposit eth to eth pool
     * step 8: close deposit eth to eth pool
     * step 9: admin open withdrawable to investor in top
     * step 10: investor in top now can withdraw NAC and ETH for this round
    /*/
    
    // ------------------------------------------------ 
    /*
    * Admin function
    * Open and Close Round
    *
    */
    function activateRound(uint _roundIndex) 
        onlyEscrow
        public
    {
        require(round[_roundIndex].isOpen == false && round[_roundIndex].isCloseEthPool == false && round[_roundIndex].isCompleteActive == false);
        round[_roundIndex].isOpen = true;
        currentRound = _roundIndex;
        ActivateRound(_roundIndex, now);
    }
    
    function deactivateRound(uint _roundIndex)
        onlyEscrow
        public
    {
        require(round[_roundIndex].isOpen == true);
        round[_roundIndex].isOpen = false;
    }
    
    // ------------------------------------------------ 
    // this function add stake of ShareHolder
    // investor can execute this function during round open
    //
    
    function tokenFallbackExchange(address _from, uint _value, uint _price) onlyNami public returns (bool success) {
        // only on currentRound and active user can add stake
        require(round[_price].isOpen == true && _value > 0);
        // add stake
        namiPool[_price][_from].stake = namiPool[_price][_from].stake.add(_value);
        round[_price].currentNAC = round[_price].currentNAC.add(_value);
        UpdateShareHolder(_from, _price, namiPool[_price][_from].stake, now);
        return true;
    }
    
    
    /*
    *
    * Activate and deactivate user
    * add or sub final Nac to compute stake to withdraw
    */
    function activateUser(address _shareAddress, uint _roundId)
        onlyEscrow
        public
    {
        require(namiPool[_roundId][_shareAddress].isActive == false && namiPool[_roundId][_shareAddress].stake > 0);
        require(round[_roundId].isCompleteActive == false && round[_roundId].isOpen == false);
        namiPool[_roundId][_shareAddress].isActive = true;
        round[_roundId].finalNAC = round[_roundId].finalNAC.add(namiPool[_roundId][_shareAddress].stake);
        UpdateActive(_shareAddress, _roundId ,namiPool[_roundId][_shareAddress].isActive, now);
    }
    
    function deactivateUser(address _shareAddress, uint _roundId)
        onlyEscrow
        public
    {
        require(namiPool[_roundId][_shareAddress].isActive == true && namiPool[_roundId][_shareAddress].stake > 0);
        require(round[_roundId].isCompleteActive == false && round[_roundId].isOpen == false);
        namiPool[_roundId][_shareAddress].isActive = false;
        round[_roundId].finalNAC = round[_roundId].finalNAC.sub(namiPool[_roundId][_shareAddress].stake);
        UpdateActive(_shareAddress, _roundId ,namiPool[_roundId][_shareAddress].isActive, now);
    }
    
    
    // ------------------------------------------------ 
    // admin close activate phrase to 
    // 
    //
    function closeActive(uint _roundId)
        onlyEscrow
        public
    {
        require(round[_roundId].isCompleteActive == false && round[_roundId].isOpen == false);
        round[_roundId].isCompleteActive = true;
    }
    //
    //
    // change Withdrawable for one round after every month
    // for investor not in top
    //
    function changeWithdrawable(uint _roundIndex)
        onlyEscrow
        public
    {
        require(round[_roundIndex].isCompleteActive == true && round[_roundIndex].isOpen == false);
        round[_roundIndex].withdrawable = !round[_roundIndex].withdrawable;
    }
    
    
    
    //
    //
    // change Withdrawable for one round after every month
    // for investor in top
    //
    function changeTopWithdrawable(uint _roundIndex)
        onlyEscrow
        public
    {
        require(round[_roundIndex].isCompleteActive == true && round[_roundIndex].isOpen == false);
        round[_roundIndex].topWithdrawable = !round[_roundIndex].topWithdrawable;
    }
    
    
    //
    //
    // after month admin deposit ETH to ETH Pool
    // 
    //
    function depositEthPool(uint _roundIndex)
        payable public
        onlyEscrow
    {
        require(msg.value > 0 && round[_roundIndex].isCloseEthPool == false && round[_roundIndex].isOpen == false);
        if (msg.value > 0) {
            round[_roundIndex].ethBalance = round[_roundIndex].ethBalance.add(msg.value);
            Deposit(msg.sender, _roundIndex, msg.value);
        }
    }
    
    //
    //
    function withdrawEthPool(uint _roundIndex, uint _amount)
        public
        onlyEscrow
    {
        require(round[_roundIndex].isCloseEthPool == false && round[_roundIndex].isOpen == false);
        require(namiMultiSigWallet != 0x0);
        // 
        if (_amount > 0) {
            namiMultiSigWallet.transfer(_amount);
            round[_roundIndex].ethBalance = round[_roundIndex].ethBalance.sub(_amount);
            WithdrawPool(_amount, now);
        }
    }
    
    //
    // close phrase deposit ETH to Pool
    // 
    function closeEthPool(uint _roundIndex)
        public
        onlyEscrow
    {
        require(round[_roundIndex].isCloseEthPool == false && round[_roundIndex].isCompleteActive == true && round[_roundIndex].isOpen == false);
        round[_roundIndex].isCloseEthPool = true;
    }
    
    //
    //
    // withdraw NAC for investor
    // internal function only can run by this smartcontract
    // 
    //
    function _withdrawNAC(address _shareAddress, uint _roundIndex) internal {
        require(namiPool[_roundIndex][_shareAddress].stake > 0);
        NamiCrowdSale namiToken = NamiCrowdSale(NamiAddr);
        uint previousBalances = namiToken.balanceOf(this);
        namiToken.transfer(_shareAddress, namiPool[_roundIndex][_shareAddress].stake);
        // update current Nac pool balance
        round[_roundIndex].currentNAC = round[_roundIndex].currentNAC.sub(namiPool[_roundIndex][_shareAddress].stake);
        
        namiPool[_roundIndex][_shareAddress].stake = 0;
        assert(previousBalances > namiToken.balanceOf(this));
    }
    
    
    //
    //
    // withdraw NAC and ETH for top investor
    // 
    //
    function withdrawTopForTeam(address _shareAddress, uint _roundIndex)
        onlyEscrow
        public
    {
        require(round[_roundIndex].isCompleteActive == true && round[_roundIndex].isCloseEthPool == true && round[_roundIndex].isOpen == false);
        require(round[_roundIndex].topWithdrawable);
        if(namiPool[_roundIndex][_shareAddress].isActive == true) {
            require(namiPool[_roundIndex][_shareAddress].isWithdrawn == false);
            assert(round[_roundIndex].finalNAC > namiPool[_roundIndex][_shareAddress].stake);
            
            // compute eth for invester
            uint ethReturn = (round[_roundIndex].ethBalance.mul(namiPool[_roundIndex][_shareAddress].stake)).div(round[_roundIndex].finalNAC);
            _shareAddress.transfer(ethReturn);
            
            // set user withdraw
            namiPool[_roundIndex][_shareAddress].isWithdrawn = true;
            Withdraw(_shareAddress, _roundIndex, ethReturn, namiPool[_roundIndex][_shareAddress].stake, now);
            
            // withdraw NAC
            _withdrawNAC(_shareAddress, _roundIndex);
        }
    }
    
    
    
    
    //
    //
    // withdraw NAC and ETH for non top investor
    // execute by admin only
    // 
    //
    function withdrawNonTopForTeam(address _shareAddress, uint _roundIndex)
        onlyEscrow
        public
    {
        require(round[_roundIndex].isCompleteActive == true && round[_roundIndex].isOpen == false);
        require(round[_roundIndex].withdrawable);
        if(namiPool[_roundIndex][_shareAddress].isActive == false) {
            require(namiPool[_roundIndex][_shareAddress].isWithdrawn == false);
            // set state user withdraw
            namiPool[_roundIndex][_shareAddress].isWithdrawn = true;
            Withdraw(_shareAddress, _roundIndex, 0, namiPool[_roundIndex][_shareAddress].stake, now);
            //
            _withdrawNAC(_shareAddress, _roundIndex);
        }
    }
    
    
    
    //
    //
    // withdraw NAC and ETH for top investor
    // execute by investor
    // 
    //
    function withdrawTop(uint _roundIndex)
        public
    {
        require(round[_roundIndex].isCompleteActive == true && round[_roundIndex].isCloseEthPool == true && round[_roundIndex].isOpen == false);
        require(round[_roundIndex].topWithdrawable);
        if(namiPool[_roundIndex][msg.sender].isActive == true) {
            require(namiPool[_roundIndex][msg.sender].isWithdrawn == false);
            uint ethReturn = (round[_roundIndex].ethBalance.mul(namiPool[_roundIndex][msg.sender].stake)).div(round[_roundIndex].finalNAC);
            msg.sender.transfer(ethReturn);
            // set user withdraw
            namiPool[_roundIndex][msg.sender].isWithdrawn = true;
            //
            Withdraw(msg.sender, _roundIndex, ethReturn, namiPool[_roundIndex][msg.sender].stake, now);
            _withdrawNAC(msg.sender, _roundIndex);
        }
    }
    
    //
    //
    // withdraw NAC and ETH for non top investor
    // execute by investor
    // 
    //
    function withdrawNonTop(uint _roundIndex)
        public
    {
        require(round[_roundIndex].isCompleteActive == true && round[_roundIndex].isOpen == false);
        require(round[_roundIndex].withdrawable);
        if(namiPool[_roundIndex][msg.sender].isActive == false) {
            require(namiPool[_roundIndex][msg.sender].isWithdrawn == false);
            namiPool[_roundIndex][msg.sender].isWithdrawn = true;
            //
            Withdraw(msg.sender, _roundIndex, 0, namiPool[_roundIndex][msg.sender].stake, now);
            _withdrawNAC(msg.sender, _roundIndex);
        }
    }
    
}
function withdrawEthPool(uint _roundIndex, uint _amount)
    public
    onlyEscrow
{
    require(round[_roundIndex].isCloseEthPool == false && round[_roundIndex].isOpen == false);
    require(namiMultiSigWallet != 0x0);
    // 
    if (_amount > 0) {
        namiMultiSigWallet.transfer(_amount);
        round[_roundIndex].ethBalance = round[_roundIndex].ethBalance.sub(_amount);
        WithdrawPool(_amount, now);
    }
}

//
//