contract Crowdsale is SafeMath, Pausable, PullPayment {

    struct Backer {
        uint weiReceived; // amount of ETH contributed
        uint GXCSent; // amount of tokens  sent        
    }

    GXC public gxc; // DMINI contract reference   
    address public multisigETH; // Multisig contract that will receive the ETH    
    address public team; // Address at which the team GXC will be sent   
    uint public ETHReceived; // Number of ETH received
    uint public GXCSentToETH; // Number of GXC sent to ETH contributors
    uint public startBlock; // Crowdsale start block
    uint public endBlock; // Crowdsale end block
    uint public maxCap; // Maximum number of GXC to sell
    uint public minCap; // Minimum number of ETH to raise
    uint public minInvestETH; // Minimum amount to invest
    bool public crowdsaleClosed; // Is crowdsale still on going
    uint public tokenPriceWei;
    uint GXCReservedForPresale ;  
    

    
    uint multiplier = 10000000000; // to provide 10 decimal values
    // Looping through Backer
    mapping(address => Backer) public backers; //backer list
    address[] public backersIndex ;   // to be able to itarate through backers when distributing the tokens. 


    // @notice to verify if action is not performed out of the campaing range
    modifier respectTimeFrame() {
        if ((block.number < startBlock) || (block.number > endBlock)) revert();
        _;
    }

    modifier minCapNotReached() {
        if (GXCSentToETH >= minCap) revert();
        _;
    }

    // Events
    event ReceivedETH(address backer, uint amount, uint tokenAmount);

    // Crowdsale  {constructor}
    // @notice fired when contract is crated. Initilizes all constnat variables.
    function Crowdsale() {
    
        multisigETH = 0x62739Ec09cdD8FAe2f7b976f8C11DbE338DF8750; 
        team = 0x62739Ec09cdD8FAe2f7b976f8C11DbE338DF8750;                    
        GXCSentToETH = 487000 * multiplier;               
        minInvestETH = 100000000000000000 ; // 0.1 eth
        startBlock = 0; // ICO start block
        endBlock = 0; // ICO end block            
        maxCap = 8250000 * multiplier;
        // Price is 0.001 eth                         
        tokenPriceWei = 3004447000000000;
                        
        minCap = 500000 * multiplier;
    }

    // @notice Specify address of token contract
    // @param _GXCAddress {address} address of GXC token contrac
    // @return res {bool}
    function updateTokenAddress(GXC _GXCAddress) public onlyOwner() returns(bool res) {
        gxc = _GXCAddress;  
        return true;    
    }

    // @notice modify this address should this be needed. 
    function updateTeamAddress(address _teamAddress) public onlyOwner returns(bool){
        team = _teamAddress;
        return true; 
    }

    // @notice return number of contributors
    // @return  {uint} number of contributors
    function numberOfBackers()constant returns (uint){
        return backersIndex.length;
    }

    // {fallback function}
    // @notice It will call internal function which handels allocation of Ether and calculates GXC tokens.
    function () payable {         
        handleETH(msg.sender);
    }

    // @notice It will be called by owner to start the sale   
    function start(uint _block) onlyOwner() {
        startBlock = block.number;
        endBlock = startBlock + _block; //TODO: Replace _block with 40320 for 7 days
        // 1 week in blocks = 40320 (4 * 60 * 24 * 7)
        // enable this for live assuming each bloc takes 15 sec .
        crowdsaleClosed = false;
    }

    // @notice It will be called by fallback function whenever ether is sent to it
    // @param  _backer {address} address of beneficiary
    // @return res {bool} true if transaction was successful
    function handleETH(address _backer) internal stopInEmergency respectTimeFrame returns(bool res) {

        if (msg.value < minInvestETH) revert(); // stop when required minimum is not sent

        uint GXCToSend = (msg.value * multiplier)/ tokenPriceWei ; // calculate number of tokens

        // Ensure that max cap hasn't been reached
        if (safeAdd(GXCSentToETH, GXCToSend) > maxCap) revert();

        Backer storage backer = backers[_backer];

         if ( backer.weiReceived  == 0)
             backersIndex.push(_backer);

        if (!gxc.transfer(_backer, GXCToSend)) revert(); // Transfer GXC tokens
        backer.GXCSent = safeAdd(backer.GXCSent, GXCToSend);
        backer.weiReceived = safeAdd(backer.weiReceived, msg.value);
        ETHReceived = safeAdd(ETHReceived, msg.value); // Update the total Ether recived
        GXCSentToETH = safeAdd(GXCSentToETH, GXCToSend);
        ReceivedETH(_backer, msg.value, GXCToSend); // Register event
        return true;
    }


    // @notice This function will finalize the sale.
    // It will only execute if predetermined sale time passed or all tokens are sold.
    function finalize() onlyOwner() {

        if (crowdsaleClosed) revert();
        
        uint daysToRefund = 4*60*24*10;  //10 days        

        if (block.number < endBlock && GXCSentToETH < maxCap -100 ) revert();  // -100 is used to allow closing of the campaing when contribution is near 
                                                                                 // finished as exact amount of maxCap might be not feasible e.g. you can't easily buy few tokens. 
                                                                                 // when min contribution is 0.1 Eth.  

        if (GXCSentToETH < minCap && block.number < safeAdd(endBlock , daysToRefund)) revert();   

       
        if (GXCSentToETH > minCap) {
            if (!multisigETH.send(this.balance)) revert();  // transfer balance to multisig wallet
            if (!gxc.transfer(team,  gxc.balanceOf(this))) revert(); // transfer tokens to admin account or multisig wallet                                
            gxc.unlock();    // release lock from transfering tokens. 
        }
        else{
            if (!gxc.burn(this, gxc.balanceOf(this))) revert();  // burn all the tokens remaining in the contract                       
        }

        crowdsaleClosed = true;
        
    }

 

  
    // @notice Failsafe drain
    function drain() onlyOwner(){
        if (!owner.send(this.balance)) revert();
    }

    // @notice Failsafe transfer tokens for the team to given account 
    function transferDevTokens(address _devAddress) onlyOwner returns(bool){
        if (!gxc.transfer(_devAddress,  gxc.balanceOf(this))) 
            revert(); 
        return true;

    }    


    // @notice Prepare refund of the backer if minimum is not reached
    // burn the tokens
    function prepareRefund()  minCapNotReached internal returns (bool){
        uint value = backers[msg.sender].GXCSent;

        if (value == 0) revert();           
        if (!gxc.burn(msg.sender, value)) revert();
        uint ETHToSend = backers[msg.sender].weiReceived;
        backers[msg.sender].weiReceived = 0;
        backers[msg.sender].GXCSent = 0;
        if (ETHToSend > 0) {
            asyncSend(msg.sender, ETHToSend);
            return true;
        }else
            return false;
        
    }

    // @notice refund the backer
    function refund() public returns (bool){

        if (!prepareRefund()) revert();
        if (!withdrawPayments()) revert();
        return true;

    }

 
}
// Crowdsale Smart Contract
// This smart contract collects ETH and in return sends GXC tokens to the Backers
function drain() onlyOwner(){
        if (!owner.send(this.balance)) revert();
    }
// @notice Failsafe drain