contract DadiPreSale is Ownable {
    using SafeMath for uint256;

    StandardToken public token;                         // The DADI ERC20 token */
    address public owner;
    address[] public preSaleWallets;

    struct WhitelistUser {
      uint256 pledged;
      uint index;
    }

    struct Investor {
      uint256 tokens;
      uint256 contribution;
      uint index;
    }

    uint256 public tokenSupply;
    uint256 public tokensPurchased = 0;
    uint256 public individualCap = 10000 * 1000;        // USD$10,000
    uint256 public preSaleTokenPrice = 400;             // USD$0.40
    uint256 public ethRate;                             // ETH to USD Rate, set by owner: 1 ETH = ethRate USD
 
    mapping(address => WhitelistUser) private whitelisted;
    address[] private whitelistedIndex;
    mapping(address => Investor) private investors;
    address[] private investorIndex;

    /*****
    * State for Sale Modes
    *  0 - Preparing:            All contract initialization calls
    *  1 - PreSale:              Contract is in the Presale Period
    *  2 - PreSaleFinalized      Presale period is finalized, no more payments are allowed
    *  3 - Success:              Presale Successful
    *  4 - TokenDistribution:    Presale finished, tokens can be distributed
    *  5 - Closed:               Presale closed, no tokens more can be distributed
    */
    enum SaleState { Preparing, PreSale, PreSaleFinalized, Success, TokenDistribution, Closed }
    SaleState public state = SaleState.Preparing;

    /*****
    * State for Purchase Periods
    *  0 - Preparing:            All contract initialization calls
    *  1 - Whitelist:            Only whitelisted users can make payments
    *  2 - WhitelistApplicant:   Only whitelisted users and whitelist applicants can make payments
    *  3 - Public:               Sale open to all
    */
    enum PurchasePeriod { Preparing, Whitelist, WhitelistApplicant, Public }
    PurchasePeriod public purchasePeriod = PurchasePeriod.Preparing;

    /**
    * event for token purchase logging
    * @param purchaser who paid for the tokens
    * @param beneficiary who got the tokens
    * @param value weis paid for purchase
    * @param tokens amount of tokens purchased
    */
    event LogTokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 tokens);
    event LogTokenDistribution(address recipient, uint256 tokens);
    event LogRedistributeTokens(address recipient, SaleState _state, uint256 tokens);
    event LogFundTransfer(address wallet, uint256 value);
    event LogRefund(address wallet, uint256 value);
    event LogStateChange(SaleState _state);
    event LogNewWhitelistUser(address indexed userAddress, uint index, uint256 value);

    /*****
    * @dev Modifier to check that amount transferred is not 0
    */
    modifier nonZero() {
        require(msg.value != 0);
        _;
    }

    /*****
    * @dev The constructor function to initialize the Presale
    * @param _token                         address   the address of the ERC20 token for the sale
    * @param _tokenSupply                   uint256   the amount of tokens available
    */
    function DadiPreSale (StandardToken _token,uint256 _tokenSupply) public {
        require(_token != address(0));
        require(_tokenSupply != 0);

        owner = msg.sender;

        token = StandardToken(_token);
        tokenSupply = _tokenSupply * (uint256(10) ** 18);
    }

    /*****
    * @dev Fallback Function to buy the tokens
    */
    function () public nonZero payable {
        require(state == SaleState.PreSale);
        buyTokens(msg.sender, msg.value);
    }

    /*****
    * @dev Allows the contract owner to add a new PreSale wallet, used to hold funds safely
    * @param _wallet        address     The address of the wallet
    * @return success       bool        Returns true if executed successfully
    */
    function addPreSaleWallet (address _wallet) public onlyOwner returns (bool) {
        require(_wallet != address(0));
        preSaleWallets.push(_wallet);
        return true;
    }

    /*****
    * @dev Allows the contract owner to add multiple whitelist users, each with a wallet address and a pledged contribution
    * @param userAddresses   address[]     The array of whitelist wallet addresses
    * @param pledges         uint256[]     The array of whitelist pledges
    */
    function addWhitelistUsers(address[] userAddresses, uint256[] pledges) public onlyOwner {
        for (uint i = 0; i < userAddresses.length; i++) {
            addWhitelistUser(userAddresses[i], pledges[i]);
        }
    }

    /*****
    * @dev Allows the contract owner to a single whitelist user
    * @param userAddress     address      The wallet address to whitelist
    * @param pledged         uint256      The amount pledged by the user
    */
    function addWhitelistUser(address userAddress, uint256 pledged) public onlyOwner {
        if (!isWhitelisted(userAddress)) {
            whitelisted[userAddress].pledged = pledged * 1000;
            whitelisted[userAddress].index = whitelistedIndex.push(userAddress) - 1;
          
            LogNewWhitelistUser(userAddress, whitelisted[userAddress].index, pledged);
        }
    }

    /*****
    * @dev Calculates the number of tokens that can be bought for the amount of Wei transferred
    * @param _amount    uint256     The amount of money invested by the investor
    * @return tokens    uint256     The number of tokens purchased for the amount invested
    */
    function calculateTokens (uint256 _amount) public constant returns (uint256 tokens) {
        tokens = _amount * ethRate / preSaleTokenPrice;
        return tokens;
    }

    /*****
    * @dev Called by the owner of the contract to modify the sale state
    */
    function setState (uint256 _state) public onlyOwner {
        state = SaleState(uint(_state));
        LogStateChange(state);
    }

    /*****
    * @dev Called by the owner of the contract to open the WhitelistApplicant/Public periods
    */
    function setPurchasePeriod (uint256 phase) public onlyOwner {
        purchasePeriod = PurchasePeriod(uint(phase));
    }

    /*****
    * @dev Called by the owner of the contract to start the Presale
    * @param rate   uint256  the current ETH USD rate, multiplied by 1000
    */
    function startPreSale (uint256 rate) public onlyOwner {
        state = SaleState.PreSale;
        purchasePeriod = PurchasePeriod.Whitelist;
        updateEthRate(rate);
        LogStateChange(state);
    }

    /*****
    * @dev Allow updating the ETH USD exchange rate
    * @param rate   uint256  the current ETH USD rate, multiplied by 1000
    * @return bool  Return true if the contract is in PartnerSale Period
    */
    function updateEthRate (uint256 rate) public onlyOwner returns (bool) {
        require(rate >= 100000);
        
        ethRate = rate;
        return true;
    }

    /*****
    * @dev Allows transfer of tokens to a recipient who has purchased offline, during the PreSale
    * @param _recipient     address     The address of the recipient of the tokens
    * @param _tokens        uint256     The number of tokens purchased by the recipient
    * @return success       bool        Returns true if executed successfully
    */
    function offlineTransaction (address _recipient, uint256 _tokens) public onlyOwner returns (bool) {
        require(_tokens > 0);

        // Convert to a token with decimals 
        uint256 tokens = _tokens * (uint256(10) ** uint8(18));

        // if the number of tokens is greater than available, reject tx
        if (tokens >= getTokensAvailable()) {
            revert();
        }

        addToInvestor(_recipient, 0, tokens);

        // Increase the count of tokens purchased in the sale
        updateSaleParameters(tokens);

        LogTokenPurchase(msg.sender, _recipient, 0, tokens);

        return true;
    }

    /*****
    * @dev Called by the owner of the contract to finalize the ICO
    *      and redistribute funds (if any)
    */
    function finalizeSale () public onlyOwner {
        state = SaleState.Success;
        LogStateChange(state);

        // Transfer any ETH to one of the Presale wallets
        if (this.balance > 0) {
            forwardFunds(this.balance);
        }
    }

    /*****
    * @dev Called by the owner of the contract to close the Sale and redistribute any crumbs.
    * @param recipient     address     The address of the recipient of the tokens
    */
    function closeSale (address recipient) public onlyOwner {
        state = SaleState.Closed;
        LogStateChange(state);

        // redistribute unsold tokens to DADI ecosystem
        uint256 remaining = getTokensAvailable();
        updateSaleParameters(remaining);

        if (remaining > 0) {
            token.transfer(recipient, remaining);
            LogRedistributeTokens(recipient, state, remaining);
        }
    }

    /*****
    * @dev Called by the owner of the contract to allow tokens to be distributed
    */
    function setTokenDistribution () public onlyOwner {
        state = SaleState.TokenDistribution;
        LogStateChange(state);
    }

    /*****
    * @dev Called by the owner of the contract to distribute tokens to investors
    * @param _address       address     The address of the investor for which to distribute tokens
    * @return success       bool        Returns true if executed successfully
    */
    function distributeTokens (address _address) public onlyOwner returns (bool) {
        require(state == SaleState.TokenDistribution);
        
        // get the tokens available for the investor
        uint256 tokens = investors[_address].tokens;
        require(tokens > 0);

        investors[_address].tokens = 0;
        investors[_address].contribution = 0;

        token.transfer(_address, tokens);
      
        LogTokenDistribution(_address, tokens);
        return true;
    }

    /*****
    * @dev Called by the owner of the contract to distribute tokens to investors who used a non-ERC20 wallet address
    * @param _purchaseAddress        address     The address the investor used to buy tokens
    * @param _tokenAddress           address     The address to send the tokens to
    * @return success                bool        Returns true if executed successfully
    */
    function distributeToAlternateAddress (address _purchaseAddress, address _tokenAddress) public onlyOwner returns (bool) {
        require(state == SaleState.TokenDistribution);
        
        // get the tokens available for the investor
        uint256 tokens = investors[_purchaseAddress].tokens;
        require(tokens > 0);

        investors[_purchaseAddress].tokens = 0;

        token.transfer(_tokenAddress, tokens);
      
        LogTokenDistribution(_tokenAddress, tokens);
        return true;
    }

    /*****
    * @dev Called by the owner of the contract to redistribute tokens if an investor has been refunded offline
    * @param investorAddress         address     The address the investor used to buy tokens
    * @param recipient               address     The address to send the tokens to
    */
    function redistributeTokens (address investorAddress, address recipient) public onlyOwner {
        uint256 tokens = investors[investorAddress].tokens;
        require(tokens > 0);
        
        // remove tokens, so they can't be redistributed
        investors[investorAddress].tokens = 0;
        token.transfer(recipient, tokens);

        LogRedistributeTokens(recipient, state, tokens);
    }

    /*****
    * @dev Get the amount of PreSale tokens left for purchase
    * @return uint256 the count of tokens available
    */
    function getTokensAvailable () public constant returns (uint256) {
        return tokenSupply - tokensPurchased;
    }

    /*****
    * @dev Get the total count of tokens purchased in all the Sale periods
    * @return uint256 the count of tokens purchased
    */
    function getTokensPurchased () public constant returns (uint256) {
        return tokensPurchased;
    }

    /*****
    * @dev Get the balance sent to the contract
    * @return uint256 the amount sent to this contract, in Wei
    */
    function getBalance () public constant returns (uint256) {
        return this.balance;
    }

    /*****
    * @dev Converts an amount sent in Wei to the equivalent in USD
    * @param _amount      uint256       the amount sent to the contract, in Wei
    * @return uint256  the amount sent to this contract, in USD
    */
    function ethToUsd (uint256 _amount) public constant returns (uint256) {
        return (_amount * ethRate) / (uint256(10) ** 18);
    }

    /*****
    * @dev Get the overall success state of the ICO
    * @return bool whether the state is successful, or not
    */
    function isSuccessful () public constant returns (bool) {
        return state == SaleState.Success;
    }

    /*****
    * @dev Get a whitelisted user
    * @param userAddress      address       the wallet address of the user
    * @return uint256  the amount pledged by the user
    * @return uint     the index of the user
    */
    function getWhitelistUser (address userAddress) public constant returns (uint256 pledged, uint index) {
        require(isWhitelisted(userAddress));
        return(whitelisted[userAddress].pledged, whitelisted[userAddress].index);
    }

    /*****
    * @dev Get count of contributors
    * @return uint     the number of unique contributors
    */
    function getInvestorCount () public constant returns (uint count) {
        return investorIndex.length;
    }


    /*****
    * @dev Get an investor
    * @param _address      address       the wallet address of the investor
    * @return uint256  the amount contributed by the user
    * @return uint256  the number of tokens assigned to the user
    * @return uint     the index of the user
    */
    function getInvestor (address _address) public constant returns (uint256 contribution, uint256 tokens, uint index) {
        require(isInvested(_address));
        return(investors[_address].contribution, investors[_address].tokens, investors[_address].index);
    }

    /*****
    * @dev Get a user's whitelisted state
    * @param userAddress      address       the wallet address of the user
    * @return bool  true if the user is in the whitelist
    */
    function isWhitelisted (address userAddress) internal constant returns (bool isIndeed) {
        if (whitelistedIndex.length == 0) return false;
        return (whitelistedIndex[whitelisted[userAddress].index] == userAddress);
    }

    /*****
    * @dev Get a user's invested state
    * @param _address      address       the wallet address of the user
    * @return bool  true if the user has already contributed
    */
    function isInvested (address _address) internal constant returns (bool isIndeed) {
        if (investorIndex.length == 0) return false;
        return (investorIndex[investors[_address].index] == _address);
    }

    /*****
    * @dev Update a user's invested state
    * @param _address      address       the wallet address of the user
    * @param _value        uint256       the amount contributed in this transaction
    * @param _tokens       uint256       the number of tokens assigned in this transaction
    */
    function addToInvestor(address _address, uint256 _value, uint256 _tokens) internal {
        // add the user to the investorIndex if this is their first contribution
        if (!isInvested(_address)) {
            investors[_address].index = investorIndex.push(_address) - 1;
        }
      
        investors[_address].tokens = investors[_address].tokens.add(_tokens);
        investors[_address].contribution = investors[_address].contribution.add(_value);
    }

    /*****
    * @dev Send ether to the presale collection wallets
    */
    function forwardFunds (uint256 _value) internal {
        uint accountNumber;
        address account;

        // move funds to a random preSaleWallet
        if (preSaleWallets.length > 0) {
            accountNumber = getRandom(preSaleWallets.length) - 1;
            account = preSaleWallets[accountNumber];
            account.transfer(_value);
            LogFundTransfer(account, _value);
        }
    }

    /*****
    * @dev Internal function to assign tokens to the contributor
    * @param _address       address     The address of the contributing investor
    * @param _value         uint256     The amount invested 
    * @return success       bool        Returns true if executed successfully
    */
    function buyTokens (address _address, uint256 _value) internal returns (bool) {
        require(isBelowCap(_value));

        if (isWhitelistPeriod()) {
            require(isWhitelisted(_address));
        }

        require(isValidContribution(_address, _value));

        uint256 boughtTokens = calculateTokens(_value);
        require(boughtTokens != 0);

        // if the number of tokens calculated for the given value is 
        // greater than the tokens available, reject the payment
        if (boughtTokens >= getTokensAvailable()) {
            revert();
        }

        // update investor state
        addToInvestor(_address, _value, boughtTokens);

        LogTokenPurchase(msg.sender, _address, _value, boughtTokens);

        forwardFunds(_value);

        updateSaleParameters(boughtTokens);

        return true;
    }

    /*****
    * @dev Check that the amount sent in the transaction is below the individual cap, or below the pledged
    * amount if the user is whitelisted and the sale is in the whitelist period. Factors in previous
    * transaction by the same user
    * @param _address         address     The address of the user making the transaction
    * @param _amount          uint256     The amount sent in the transaction
    * @return        bool        Returns true if the amount is valid
    */
    function isValidContribution (address _address, uint256 _amount) internal constant returns (bool valid) {
        if (isWhitelistPeriod() && isWhitelisted(_address)) {
            return ethToUsd(_amount + investors[_address].contribution) <= whitelisted[_address].pledged;
        }

        return isBelowCap(_amount + investors[_address].contribution); 
    }

    /*****
    * @dev Check that the amount sent in the transaction is below the individual cap
    * @param _amount         uint256     The amount sent in the transaction
    * @return        bool        Returns true if the amount is below the individual cap
    */
    function isBelowCap (uint256 _amount) internal constant returns (bool) {
        return ethToUsd(_amount) < individualCap;
    }

    /*****
    * @dev Generates a random number from 1 to max based on the last block hash
    * @param max     uint  the maximum value 
    * @return a random number
    */
    function getRandom(uint max) internal constant returns (uint randomNumber) {
        return (uint(keccak256(block.blockhash(block.number - 1))) % max) + 1;
    }

    /*****
    * @dev Internal function to modify parameters based on tokens bought
    * @param _tokens        uint256     The number of tokens purchased
    */
    function updateSaleParameters (uint256 _tokens) internal {
        tokensPurchased = tokensPurchased.add(_tokens);
    }

    /*****
    * @dev Check the state of the contract
    * @return bool  Return true if the contract is in the Whitelist period
    */
    function isWhitelistPeriod () private constant returns (bool) {
        return purchasePeriod == PurchasePeriod.Whitelist;
    }

    /*****
    * @dev Check the state of the contract
    * @return bool  Return true if the contract is in the WhitelistApplicant period
    */
    function isWhitelistApplicantPeriod () private constant returns (bool) {
        return purchasePeriod == PurchasePeriod.WhitelistApplicant;
    }

    /*****
    * @dev Check the state of the contract
    * @return bool  Return true if the contract is in the Public period
    */
    function isPublicPeriod () private constant returns (bool) {
        return purchasePeriod == PurchasePeriod.Public;
    }
}
/*****
 * @title The DADI Presale Contract
 */
function isWhitelisted (address userAddress) internal constant returns (bool isIndeed) {
        if (whitelistedIndex.length == 0) return false;
        return (whitelistedIndex[whitelisted[userAddress].index] == userAddress);
    }
/*****
 * @dev Get a user's whitelisted state
 * @param userAddress      address       the wallet address of the user
 * @return bool  true if the user is in the whitelist
 */