contract BlindCroupierTokenDistribution is MultiOwnable {
    
    
    
    
    
    

    uint256 constant TOKEN = 10**7;
    uint256 constant MILLION = 10**6;

    uint256 constant MINIMUM_DEPOSIT = 100 finney; /** minimum deposit accepted to bank */
    uint256 constant PRESALE_TOKEN_PRICE = 0.00035 ether / TOKEN;
    uint256 constant SALE_INITIAL_TOKEN_PRICE = 0.0005 ether / TOKEN;

    uint256 constant TOKENS_FOR_PRESALE = 5 * MILLION * TOKEN; /** 5M tokens */
    uint256 constant TOKENS_PER_FIRST_PERIOD = 15 * MILLION * TOKEN; /** 15M tokens */
    uint256 constant TOKENS_PER_PERIOD = 1 * MILLION * TOKEN; /** 1M tokens */
    uint256 constant FIRST_PERIOD_DURATION = 161 hours; /** duration of 1st sale period */
    uint256 constant PERIOD_DURATION = 23 hours; /** duration of all other sale periods */
    uint256 constant PERIOD_PRICE_INCREASE = 5; /** `next_token_price = old_token_price + old_token_price * PERIOD_PRICE_INCREASE / 1000` */
    uint256 constant FULLY_SOLD_PRICE_INCREASE = 10; /** to increase price if ALL tokens sold */
    uint256 constant TOKENS_TO_INCREASE_NEXT_PRICE = 800; /** the price is increased if `sold_tokens > period_tokens * TOKENS_TO_INCREASE_NEXT_PRICE / 1000` */

    uint256 constant NEVER = 0;
    uint16 constant UNKNOWN_COUNTRY = 0;

    /**
     * State of Blind Croupier crowdsale
     */
    enum State {
        NotStarted,
        Presale,
        Sale
    }

    uint256 public totalUnclaimedTokens; /** total amount of tokens, TokenDistribution owns to investors */
    uint256 public totalTokensSold; /** total amount of Tokens sold during the TokenDistribution */
    uint256 public totalTokensDestroyed; /** total amount of Tokens destroyed by this contract */

    mapping(address => uint256) public unclaimedTokensForInvestor; /** unclaimed tokens for each investor */

    /**
     * One token sale period information
     */
    struct Period {
        uint256 startTime;
        uint256 endTime;
        uint256 tokenPrice;
        uint256 tokensSold;
    }

    /**
     * Emited when `investor` sends `amount` of money to the Bank
     * @param investor - address, sending the money
     * @param amount - Wei sent
     */
    event Deposited(address indexed investor, uint256 amount, uint256 tokenCount);

    /**
     * Emited when a new period is opened
     * @param periodIndex - index of new period
     * @param tokenPrice - price of WIN Token in new period
     */
    event PeriodStarted(uint periodIndex, uint256 tokenPrice, uint256 tokensToSale, uint256 startTime, uint256 endTime, uint256 now);

    /**
     * Emited when `investor` claims `claimed` tokens
     * @param investor - address getting the Tokens
     * @param claimed - amount of Tokens claimed
     */
    event TokensClaimed(address indexed investor, uint256 claimed);

    /** current Token sale period */
    uint public currentPeriod = 0;

    /** information about past and current periods, by periods index, starting from `0` */
    mapping(uint => Period) periods;

    /** WIN tokens contract  */
    WIN public win;

    /** The state of the crowdsale - `NotStarted`, `Presale`, `Sale` */
    State public state;

    /** the counter of investment by a country code (3-digit ISO 3166 code) */
    mapping(uint16 => uint256) investmentsByCountries;

    /**
     * Returns amount of Wei invested by the specified country
     * @param country - 3-digit country code
     */
    function getInvestmentsByCountry (uint16 country)   constant   returns (uint256 investment) {  
        investment = investmentsByCountries[country];
    }

    /**
     * Returns the Token price in the current period
     * @return tokenPrice - current Token price
     */
    function getTokenPrice ()   constant   returns (uint256 tokenPrice) {  
        tokenPrice = periods[currentPeriod].tokenPrice;
    }

    /**
     * Returns the Token price for the period requested
     * @param periodIndex - the period index
     * @return tokenPrice - Token price for the period
     */
    function getTokenPriceForPeriod (uint periodIndex)   constant   returns (uint256 tokenPrice) {  
        tokenPrice = periods[periodIndex].tokenPrice;
    }

    /**
     * Returns the amount of Tokens sold
     * @param period - period index to get tokens for
     * @return tokensSold - amount of tokens sold
     */
    function getTokensSold (uint period)   constant   returns (uint256 tokensSold) {  
        return periods[period].tokensSold;
    }

    /**
     * Returns `true` if TokenDistribution has enough tokens for the current period
     * and thus going on
     * @return active - `true` if TokenDistribution is going on, `false` otherwise
     */
    function isActive ()   constant   returns (bool active) {  
        return win.balanceOf(this) >= totalUnclaimedTokens + tokensForPeriod(currentPeriod) - periods[currentPeriod].tokensSold;
    }

    /**
     * Accepts money deposit and makes record
     * minimum deposit is MINIMUM_DEPOSIT
     * @param beneficiar - the address to receive Tokens
     * @param countryCode - 3-digit country code
     * @dev if `msg.value < MINIMUM_DEPOSIT` throws
     */
    function deposit (address beneficiar, uint16 countryCode)   payable  {  
        require(msg.value >= MINIMUM_DEPOSIT);
        require(state == State.Sale || state == State.Presale);

        /* this should end any finished period before starting any operations */
        tick();

        /* check if have enough tokens for the current period
         * if not, the call fails until tokens are deposited to the contract
         */
        require(isActive());

        uint256 tokensBought = msg.value / getTokenPrice();

        if(periods[currentPeriod].tokensSold + tokensBought >= tokensForPeriod(currentPeriod)) {
            tokensBought = tokensForPeriod(currentPeriod) - periods[currentPeriod].tokensSold;
        }

        uint256 moneySpent = getTokenPrice() * tokensBought;

        investmentsByCountries[countryCode] += moneySpent;

        if(tokensBought > 0) {
            assert(moneySpent <= msg.value);

            /* return the rest */
            if(msg.value > moneySpent) {
                msg.sender.transfer(msg.value - moneySpent);
            }

            periods[currentPeriod].tokensSold += tokensBought;
            unclaimedTokensForInvestor[beneficiar] += tokensBought;
            totalUnclaimedTokens += tokensBought;
            totalTokensSold += tokensBought;
            Deposited(msg.sender, moneySpent, tokensBought);
        }

        /* if all tokens are sold, get to the next period */
        tick();
    }

    /**
     * See `deposit` function
     */
    function() payable {
        deposit(msg.sender, UNKNOWN_COUNTRY);
    }

    /**
     * Creates the contract and sets the owners
     * @param owners_dot_recipient - array of 16 owner records  (MultiOwnable.Owner.recipient fields)
* @param owners_dot_share - array of 16 owner records  (MultiOwnable.Owner.share fields)
     */
    function BlindCroupierTokenDistribution (address[16] owners_dot_recipient, uint[16] owners_dot_share)   MultiOwnable(owners_dot_recipient, owners_dot_share)  {  
MultiOwnable.Owner[16] memory owners;

for(uint __recipient_iterator__ = 0; __recipient_iterator__ < owners_dot_recipient.length;__recipient_iterator__++)
  owners[__recipient_iterator__].recipient = address(owners_dot_recipient[__recipient_iterator__]);
for(uint __share_iterator__ = 0; __share_iterator__ < owners_dot_share.length;__share_iterator__++)
  owners[__share_iterator__].share = uint(owners_dot_share[__share_iterator__]);
        state = State.NotStarted;
    }

    /**
     * Begins the crowdsale (presale period)
     * @param tokenContractAddress - address of the `WIN` contract (token holder)
     * @dev must be called by one of owners
     */
    function startPresale (address tokenContractAddress)   onlyOneOfOwners  {  
        require(state == State.NotStarted);

        win = WIN(tokenContractAddress);

        assert(win.balanceOf(this) >= tokensForPeriod(0));

        periods[0] = Period(now, NEVER, PRESALE_TOKEN_PRICE, 0);
        PeriodStarted(0,
            PRESALE_TOKEN_PRICE,
            tokensForPeriod(currentPeriod),
            now,
            NEVER,
            now);
        state = State.Presale;
    }

    /**
     * Ends the presale and begins period 1 of the crowdsale
     * @dev must be called by one of owners
     */
    function endPresale ()   onlyOneOfOwners  {  
        require(state == State.Presale);
        state = State.Sale;
        nextPeriod();
    }

    /**
     * Returns a time interval for a specific `period`
     * @param  period - period index to count interval for
     * @return startTime - timestamp of period start time (INCLUSIVE)
     * @return endTime - timestamp of period end time (INCLUSIVE)
     */
    function periodTimeFrame (uint period)   constant   returns (uint256 startTime, uint256 endTime) {  
        require(period <= currentPeriod);

        startTime = periods[period].startTime;
        endTime = periods[period].endTime;
    }

    /**
     * Returns `true` if the time for the `period` has already passed
     */
    function isPeriodTimePassed (uint period)   constant   returns (bool finished) {  
        require(periods[period].startTime > 0);

        uint256 endTime = periods[period].endTime;

        if(endTime == NEVER) {
            return false;
        }

        return (endTime < now);
    }

    /**
     * Returns `true` if `period` has already finished (time passed or tokens sold)
     */
    function isPeriodClosed (uint period)   constant   returns (bool finished) {  
        return period < currentPeriod;
    }

    /**
     * Returns `true` if all tokens for the `period` has been sold
     */
    function isPeriodAllTokensSold (uint period)   constant   returns (bool finished) {  
        return periods[period].tokensSold == tokensForPeriod(period);
    }

    /**
     * Returns unclaimed Tokens count for the caller
     * @return tokens - amount of unclaimed Tokens for the caller
     */
    function unclaimedTokens ()   constant   returns (uint256 tokens) {  
        return unclaimedTokensForInvestor[msg.sender];
    }

    /**
     * Transfers all the tokens stored for this `investor` to his address
     * @param investor - investor to claim tokens for
     */
    function claimAllTokensForInvestor (address investor)   {  
        assert(totalUnclaimedTokens >= unclaimedTokensForInvestor[investor]);
        totalUnclaimedTokens -= unclaimedTokensForInvestor[investor];
        win.transfer(investor, unclaimedTokensForInvestor[investor]);
        TokensClaimed(investor, unclaimedTokensForInvestor[investor]);
        unclaimedTokensForInvestor[investor] = 0;
    }

    /**
     * Claims all the tokens for the sender
     * @dev efficiently calling `claimAllForInvestor(msg.sender)`
     */
    function claimAllTokens ()   {  
        claimAllTokensForInvestor(msg.sender);
    }

    /**
     * Returns the total token count for the period specified
     * @param  period - period number
     * @return tokens - total tokens count
     */
    function tokensForPeriod (uint period)   constant   returns (uint256 tokens) {  
        if(period == 0) {
            return TOKENS_FOR_PRESALE;
        } else if(period == 1) {
            return TOKENS_PER_FIRST_PERIOD;
        } else {
            return TOKENS_PER_PERIOD;
        }
    }

    /**
     * Returns the duration of the sale (not presale) period
     * @param  period - the period number
     * @return duration - duration in seconds
     */
    function periodDuration (uint period)   constant   returns (uint256 duration) {  
        require(period > 0);

        if(period == 1) {
            return FIRST_PERIOD_DURATION;
        } else {
            return PERIOD_DURATION;
        }
    }

    /**
     * Finishes the current period and starts a new one
     */
    function nextPeriod() internal {
        uint256 oldPrice = periods[currentPeriod].tokenPrice;
        uint256 newPrice;
        if(currentPeriod == 0) {
            newPrice = SALE_INITIAL_TOKEN_PRICE;
        } else if(periods[currentPeriod].tokensSold  == tokensForPeriod(currentPeriod)) {
            newPrice = Math.addPromille(oldPrice, FULLY_SOLD_PRICE_INCREASE);
        } else if(periods[currentPeriod].tokensSold >= Math.takePromille(tokensForPeriod(currentPeriod), TOKENS_TO_INCREASE_NEXT_PRICE)) {
            newPrice = Math.addPromille(oldPrice, PERIOD_PRICE_INCREASE);
        } else {
            newPrice = oldPrice;
        }

        /* destroy unsold tokens */
        if(periods[currentPeriod].tokensSold < tokensForPeriod(currentPeriod)) {
            uint256 toDestroy = tokensForPeriod(currentPeriod) - periods[currentPeriod].tokensSold;
            /* do not destroy if we don't have enough to pay investors */
            uint256 balance = win.balanceOf(this);
            if(balance < toDestroy + totalUnclaimedTokens) {
                toDestroy = (balance - totalUnclaimedTokens);
            }
            win.destroy(toDestroy);
            totalTokensDestroyed += toDestroy;
        }

        /* if we are force ending the period set in the future or without end time,
         * set end time to now
         */
        if(periods[currentPeriod].endTime > now ||
            periods[currentPeriod].endTime == NEVER) {
            periods[currentPeriod].endTime = now;
        }

        uint256 duration = periodDuration(currentPeriod + 1);

        periods[currentPeriod + 1] = Period(
            periods[currentPeriod].endTime,
            periods[currentPeriod].endTime + duration,
            newPrice,
            0);

        currentPeriod++;

        PeriodStarted(currentPeriod,
            newPrice,
            tokensForPeriod(currentPeriod),
            periods[currentPeriod].startTime,
            periods[currentPeriod].endTime,
            now);
    }

    /**
     * To be called as frequently as required by any external party
     * Will check if 1 or more periods have finished and move on to the next
     */
    function tick ()   {  
        if(!isActive()) {
            return;
        }

        while(state == State.Sale &&
            (isPeriodTimePassed(currentPeriod) ||
            isPeriodAllTokensSold(currentPeriod))) {
            nextPeriod();
        }
    }

    /**
     * Withdraws the money to be spent to Blind Croupier Project needs
     * @param  amount - amount of Wei to withdraw (total)
     */
    function withdraw (uint256 amount)   onlyOneOfOwners  {  
        require(this.balance >= amount);

        uint totalShares = 0;
        for(var idx = 0; idx < owners.length; idx++) {
            totalShares += owners[idx].share;
        }

        for(idx = 0; idx < owners.length; idx++) {
            owners[idx].recipient.transfer(amount * owners[idx].share / totalShares);
        }
    }
}
/**
 * @title Blind Croupier TokenDistribution
 * It possesses some `WIN` tokens.
 * The distribution is divided into many 'periods'.
 * The zero one is `Presale` with `TOKENS_FOR_PRESALE` tokens
 * It's ended when all tokens are sold or manually with `endPresale()` function
 * The length of first period is `FIRST_PERIOD_DURATION`.
 * The length of other periods is `PERIOD_DURATION`.
 * During each period, `TOKENS_PER_PERIOD` are offered for sale (`TOKENS_PER_FIRST_PERIOD` for the first one)
 * Investors send their money to the contract
 * and call `claimAllTokens()` function to get `WIN` tokens.
 * Period 0 Token price is `PRESALE_TOKEN_PRICE`
 * Period 1 Token price is `SALE_INITIAL_TOKEN_PRICE`
 * Period 2+ price is determined by the following rules:
 * If more than `TOKENS_TO_INCREASE_NEXT_PRICE * TOKENS_PER_PERIOD / 1000`
 * were sold during the period, the price of the Tokens for the next period
 * is increased by `PERIOD_PRICE_INCREASE` promille,
 * if ALL tokens were sold, price is increased by `FULLY_SOLD_PRICE_INCREASE` promille
 * Otherwise, the price remains the same.
 */
function claimAllTokens ()   {  
    claimAllTokensForInvestor(msg.sender);
}

/**
 * Claims all the tokens for the sender
 * @dev efficiently calling `claimAllForInvestor(msg.sender)`
 */