contract GATTokenSale is GATToken, GATTokenSaleConfig {

    using SafeMath for uint256;

    // Once finalized, tokens will be freely tradable
    bool public finalized;

    // Sale can be suspended or resumed by the owner
    bool public suspended;

    // Addresses for the bank, funding and reserves.
    address public bankAddress;
    address public fundingAddress;
    address public reserve1Address;
    address public reserve2Address;

    // Price of tokens per 1000 ETH
    uint256 public tokensPerKEther;

    // The bonus amount on token purchases
    // E.g. 120 means a 20% bonus will be applied.
    uint256 public bonus;

    // Total number of tokens that have been sold through the sale contract so far.
    uint256 public totalTokensSold;

    // Keep track of start time and end time for the sale. These have default
    // values when the contract is deployed but can be changed by owner as needed.
    uint256 public startTime;
    uint256 public endTime;


    // Events
    event TokensPurchased(address indexed beneficiary, uint256 cost, uint256 tokens);
    event TokensPerKEtherUpdated(uint256 newAmount);
    event BonusAmountUpdated(uint256 newAmount);
    event TimeWindowUpdated(uint256 newStartTime, uint256 newEndTime);
    event SaleSuspended();
    event SaleResumed();
    event TokenFinalized();
    event ContractTokensReclaimed(uint256 amount);


    function GATTokenSale(address _bankAddress, address _fundingAddress, address _reserve1Address, address _reserve2Address) public
        GATToken(SYMBOL, NAME, DECIMALS, 0)
    {
        // Can only create the contract is the sale has not yet started or ended.
        require(START_TIME >= currentTime());
        require(END_TIME > START_TIME);

        // Need valid wallet addresses
        require(_bankAddress    != address(0x0));
        require(_bankAddress    != address(this));
        require(_fundingAddress != address(0x0));
        require(_fundingAddress != address(this));
        require(_reserve1Address != address(0x0));
        require(_reserve1Address != address(this));
        require(_reserve2Address != address(0x0));
        require(_reserve2Address != address(this));

        uint256 salesTotal = TOKEN_PUBLIC_SALE_CAP.add(TOKEN_PRIVATE_SALE_CAP);
        require(salesTotal.add(TOKEN_FUTURE_CAP).add(TOKEN_FOUNDATION_CAP).add(TOKEN_RESERVE1_CAP).add(TOKEN_RESERVE2_CAP) == TOKEN_TOTAL_CAP);

        // Start in non-finalized state
        finalized = false;
        suspended = false;

        // Start and end times (used for presale).
        startTime = START_TIME;
        endTime   = END_TIME;

        // Initial pricing
        tokensPerKEther = TOKENS_PER_KETHER;

        // Bonus for contributions
        bonus = PRESALE_BONUS;

        // Initialize wallet addresses
        bankAddress    = _bankAddress;
        fundingAddress = _fundingAddress;
        reserve1Address = _reserve1Address;
        reserve2Address = _reserve2Address;

        // Assign initial balances
        balances[address(this)] = balances[address(this)].add(TOKEN_PRESALE_CAP);
        totalSupply = totalSupply.add(TOKEN_PRESALE_CAP);
        Transfer(0x0, address(this), TOKEN_PRESALE_CAP);

        balances[reserve1Address] = balances[reserve1Address].add(TOKEN_RESERVE1_CAP);
        totalSupply = totalSupply.add(TOKEN_RESERVE1_CAP);
        Transfer(0x0, reserve1Address, TOKEN_RESERVE1_CAP);

        balances[reserve2Address] = balances[reserve2Address].add(TOKEN_RESERVE2_CAP);
        totalSupply = totalSupply.add(TOKEN_RESERVE2_CAP);
        Transfer(0x0, reserve2Address, TOKEN_RESERVE2_CAP);

        uint256 bankBalance = TOKEN_TOTAL_CAP.sub(totalSupply);
        balances[bankAddress] = balances[bankAddress].add(bankBalance);
        totalSupply = totalSupply.add(bankBalance);
        Transfer(0x0, bankAddress, bankBalance);

        // The total supply that we calculated here should be the same as in the config.
        require(balanceOf(address(this))  == TOKEN_PRESALE_CAP);
        require(balanceOf(reserve1Address) == TOKEN_RESERVE1_CAP);
        require(balanceOf(reserve2Address) == TOKEN_RESERVE2_CAP);
        require(balanceOf(bankAddress)    == bankBalance);
        require(totalSupply == TOKEN_TOTAL_CAP);
    }


    function currentTime() public constant returns (uint256) {
        return now;
    }


    // Allows the owner to change the price for tokens.
    //
    function setTokensPerKEther(uint256 _tokensPerKEther) external onlyOwner returns(bool) {
        require(_tokensPerKEther > 0);

        // Set the tokensPerKEther amount for any new sale.
        tokensPerKEther = _tokensPerKEther;

        TokensPerKEtherUpdated(_tokensPerKEther);

        return true;
    }


    // Allows the owner to change the bonus amount applied to purchases.
    //
    function setBonus(uint256 _bonus) external onlyOwner returns(bool) {
        // 100 means no bonus
        require(_bonus >= 100);

        // 200 means 100% bonus
        require(_bonus <= 200);

        bonus = _bonus;

        BonusAmountUpdated(_bonus);

        return true;
    }


    // Allows the owner to change the time window for the sale.
    //
    function setTimeWindow(uint256 _startTime, uint256 _endTime) external onlyOwner returns(bool) {
        require(_startTime >= START_TIME);
        require(_endTime > _startTime);

        startTime = _startTime;
        endTime   = _endTime;

        TimeWindowUpdated(_startTime, _endTime);

        return true;
    }


    // Allows the owner to suspend / stop the sale.
    //
    function suspend() external onlyOwner returns(bool) {
        if (suspended == true) {
            return false;
        }

        suspended = true;

        SaleSuspended();

        return true;
    }


    // Allows the owner to resume the sale.
    //
    function resume() external onlyOwner returns(bool) {
        if (suspended == false) {
            return false;
        }

        suspended = false;

        SaleResumed();

        return true;
    }


    // Accept ether contributions during the token sale.
    //
    function () payable public {
        buyTokens(msg.sender);
    }


    // Allows the caller to buy tokens for another recipient (proxy purchase).
    // This can be used by exchanges for example.
    //
    function buyTokens(address beneficiary) public payable returns (uint256) {
        require(!suspended);
        require(beneficiary != address(0x0));
        require(beneficiary != address(this));
        require(currentTime() >= startTime);
        require(currentTime() <= endTime);
        require(msg.value >= CONTRIBUTION_MIN);
        require(msg.sender != fundingAddress);

        // Check if the sale contract still has tokens for sale.
        uint256 saleBalance = balanceOf(address(this));
        require(saleBalance > 0);

        // Calculate the number of tokens that the ether should convert to.
        uint256 tokens = msg.value.mul(tokensPerKEther).mul(bonus).div(10**(18 - DECIMALS + 3 + 2));
        require(tokens > 0);

        uint256 cost = msg.value;
        uint256 refund = 0;

        if (tokens > saleBalance) {
            // Not enough tokens left for sale to fulfill the full order.
            tokens = saleBalance;

            // Calculate the actual cost for the tokens that can be purchased.
            cost = tokens.mul(10**(18 - DECIMALS + 3 + 2)).div(tokensPerKEther.mul(bonus));

            // Calculate the amount of ETH refund to the contributor.
            refund = msg.value.sub(cost);
        }

        totalTokensSold = totalTokensSold.add(tokens);

        // Move tokens from the sale contract to the beneficiary
        balances[address(this)] = balances[address(this)].sub(tokens);
        balances[beneficiary]   = balances[beneficiary].add(tokens);
        Transfer(address(this), beneficiary, tokens);

        if (refund > 0) {
           msg.sender.transfer(refund);
        }

        // Transfer the contributed ether to the crowdsale wallets.
        uint256 contribution      = msg.value.sub(refund);
        uint256 reserveAllocation = contribution.div(20);

        fundingAddress.transfer(contribution.sub(reserveAllocation));
        reserve1Address.transfer(reserveAllocation);

        TokensPurchased(beneficiary, cost, tokens);

        return tokens;
    }


    // ERC20 transfer function, modified to only allow transfers once the sale has been finalized.
    //
    function transfer(address _to, uint256 _amount) public returns (bool success) {
        if (!isTransferAllowed(msg.sender, _to)) {
            return false;
        }

        return super.transfer(_to, _amount);
    }


    // ERC20 transferFrom function, modified to only allow transfers once the sale has been finalized.
    //
    function transferFrom(address _from, address _to, uint256 _amount) public returns (bool success) {
        if (!isTransferAllowed(_from, _to)) {
            return false;
        }

        return super.transferFrom(_from, _to, _amount);
    }


    // Internal helper to check if the transfer should be allowed
    //
    function isTransferAllowed(address _from, address _to) private view returns (bool) {
        if (finalized) {
            // We allow everybody to transfer tokens once the sale is finalized.
            return true;
        }

        if (_from == bankAddress || _to == bankAddress) {
            // We allow the bank to initiate transfers. We also allow it to be the recipient
            // of transfers before the token is finalized in case a recipient wants to send
            // back tokens. E.g. KYC requirements cannot be met.
            return true;
        }

        return false;
    }


    // Allows owner to transfer tokens assigned to the sale contract, back to the bank wallet.
    function reclaimContractTokens() external onlyOwner returns (bool) {
        uint256 tokens = balanceOf(address(this));

        if (tokens == 0) {
            return false;
        }

        balances[address(this)] = balances[address(this)].sub(tokens);
        balances[bankAddress]   = balances[bankAddress].add(tokens);
        Transfer(address(this), bankAddress, tokens);

        ContractTokensReclaimed(tokens);

        return true;
    }


    // Allows the owner to finalize the sale and allow tokens to be traded.
    //
    function finalize() external onlyOwner returns (bool) {
        require(!finalized);

        finalized = true;

        TokenFinalized();

        return true;
    }
}
// ----------------------------------------------------------------------------
// GAT Token Sample Implementation
//
// Copyright (c) 2017 GAT Systems Ltd.
// http://www.gatcoin.io/
//
// The MIT Licence.
// ----------------------------------------------------------------------------
// This is the main contract that drives the GAT token sale.
// It exposes the ERC20 interface along with various sale-related functions.
//
function () payable public {
    buyTokens(msg.sender);
}

// Accept ether contributions during the token sale.
//