contract Crowdsale is Pausable {
   using SafeMath for uint256;

   // The token being sold
   CostumeToken public token;

   // 12.15.2017 - 12:00:00 GMT
   uint256 public startTime = 1513339200;

   // 1.31.2018 - 12:00:00 GMT
   uint256 public endTime = 1517400000;

   // Costume Wallet
   address public wallet;

   // Set tier rates
   uint256 public rate = 3400;
   uint256 public rateTier2 = 3200;
   uint256 public rateTier3 = 3000;
   uint256 public rateTier4 = 2800;

   // The maximum amount of wei for each tier
   // 20 Million Intervals
   uint256 public limitTier1 = 20e24;
   uint256 public limitTier2 = 40e24;
   uint256 public limitTier3 = 60e24;

   // 80 Million Tokens available for crowdsale
   uint256 public constant maxTokensRaised = 80e24;

   // The amount of wei raised
   uint256 public weiRaised = 0;

   // The amount of tokens raised
   uint256 public tokensRaised = 0;

   // 0.1 ether minumum per contribution
   uint256 public constant minPurchase = 100 finney;

   // Crowdsale tokens not purchased
   bool public remainingTransfered = false;

   // The number of transactions
   uint256 public numberOfTransactions;

   // -- DATA-SETS

   // Amount each address paid for tokens
   mapping(address => uint256) public crowdsaleBalances;

   // Amount of tokens each address received
   mapping(address => uint256) public tokensBought;

   // -- EVENTS

   // Trigger TokenPurchase event
   event TokenPurchase(address indexed buyer, uint256 value, uint256 amountOfTokens);

   // Crowdsale Ended
   event Finalized();

   // -- MODIFIERS

   // Only allow the execution of the function before the crowdsale starts
   modifier beforeStarting() {
      require(now < startTime);
      _;
   }

   // Main Constructor
   // @param _wallet - Fund wallet address
   // @param _tokenAddress - Associated token address
   // @param _startTime - Crowdsale start time
   // @param _endTime - Crowdsale end time
   function Crowdsale(
      address _wallet,
      address _tokenAddress,
      uint256 _startTime,
      uint256 _endTime
   ) public {
      require(_wallet != address(0));
      require(_tokenAddress != address(0));

      if (_startTime > 0 && _endTime > 0) {
          require(_startTime < _endTime);
      }

      wallet = _wallet;
      token = CostumeToken(_tokenAddress);

      if (_startTime > 0) {
          startTime = _startTime;
      }

      if (_endTime > 0) {
          endTime = _endTime;
      }

   }

   /// Buy tokens fallback
   function () external payable {
      buyTokens();
   }

   /// Buy tokens main
   function buyTokens() public payable whenNotPaused {
      require(validPurchase());

      uint256 tokens = 0;
      uint256 amountPaid = adjustAmountValue();

      if (tokensRaised < limitTier1) {

         // Tier 1
         tokens = amountPaid.mul(rate);

         // If the amount of tokens that you want to buy gets out of this tier
         if (tokensRaised.add(tokens) > limitTier1) {

            tokens = adjustTokenTierValue(amountPaid, limitTier1, 1, rate);
         }

      } else if (tokensRaised >= limitTier1 && tokensRaised < limitTier2) {

         // Tier 2
         tokens = amountPaid.mul(rateTier2);

          // Breaks tier cap
         if (tokensRaised.add(tokens) > limitTier2) {
            tokens = adjustTokenTierValue(amountPaid, limitTier2, 2, rateTier2);
         }

      } else if (tokensRaised >= limitTier2 && tokensRaised < limitTier3) {

         // Tier 3
         tokens = amountPaid.mul(rateTier3);

         // Breaks tier cap
         if (tokensRaised.add(tokens) > limitTier3) {
            tokens = adjustTokenTierValue(amountPaid, limitTier3, 3, rateTier3);
         }

      } else if (tokensRaised >= limitTier3) {

         // Tier 4
         tokens = amountPaid.mul(rateTier4);

      }

      weiRaised = weiRaised.add(amountPaid);
      tokensRaised = tokensRaised.add(tokens);
      token.distributeCrowdsaleTokens(msg.sender, tokens);

      // Keep the records
      tokensBought[msg.sender] = tokensBought[msg.sender].add(tokens);

      // Broadcast event
      TokenPurchase(msg.sender, amountPaid, tokens);

      // Update records
      numberOfTransactions = numberOfTransactions.add(1);

      forwardFunds(amountPaid);
   }

   // Forward funds to fund wallet
   function forwardFunds(uint256 amountPaid) internal whenNotPaused {

     // Send directly to dev wallet
     wallet.transfer(amountPaid);
   }

   // Adjust wei based on tier, refund if necessaey
   function adjustAmountValue() internal whenNotPaused returns(uint256) {
      uint256 amountPaid = msg.value;
      uint256 differenceWei = 0;

      // Check final tier
      if(tokensRaised >= limitTier3) {
         uint256 addedTokens = tokensRaised.add(amountPaid.mul(rateTier4));

         // Have we reached the max?
         if(addedTokens > maxTokensRaised) {

            // Find the amount over the max
            uint256 difference = addedTokens.sub(maxTokensRaised);
            differenceWei = difference.div(rateTier4);
            amountPaid = amountPaid.sub(differenceWei);
         }
      }

      // Update balances dataset
      crowdsaleBalances[msg.sender] = crowdsaleBalances[msg.sender].add(amountPaid);

      // Transfer at the end
      if (differenceWei > 0) msg.sender.transfer(differenceWei);

      return amountPaid;
   }

   // Set / change tier rates
   // @param tier1 - tier4 - Rate per tier
   function setTierRates(uint256 tier1, uint256 tier2, uint256 tier3, uint256 tier4)
      external onlyOwner whenNotPaused {

      require(tier1 > 0 && tier2 > 0 && tier3 > 0 && tier4 > 0);
      require(tier1 > tier2 && tier2 > tier3 && tier3 > tier4);

      rate = tier1;
      rateTier2 = tier2;
      rateTier3 = tier3;
      rateTier4 = tier4;
   }

   // Adjust token per tier, return wei if necessay
   // @param amount - Amount buyer paid
   // @param tokensThisTier - Tokens in tier
   // @param tierSelected - The current tier
   // @param _rate - Current rate
   function adjustTokenTierValue(
      uint256 amount,
      uint256 tokensThisTier,
      uint256 tierSelected,
      uint256 _rate
   ) internal returns(uint256 totalTokens) {
      require(amount > 0 && tokensThisTier > 0 && _rate > 0);
      require(tierSelected >= 1 && tierSelected <= 4);

      uint weiThisTier = tokensThisTier.sub(tokensRaised).div(_rate);
      uint weiNextTier = amount.sub(weiThisTier);
      uint tokensNextTier = 0;
      bool returnTokens = false;

      // If there's excessive wei for the last tier, refund those
      if(tierSelected != 4) {

         tokensNextTier = calculateTokensPerTier(weiNextTier, tierSelected.add(1));

      } else {

         returnTokens = true;

      }

      totalTokens = tokensThisTier.sub(tokensRaised).add(tokensNextTier);

      // Do the transfer at the end
      if (returnTokens) msg.sender.transfer(weiNextTier);
   }

   // Return token amount based on wei paid
   // @param weiPaid - Amount buyer paid
   // @param tierSelected - The current tier
   function calculateTokensPerTier(uint256 weiPaid, uint256 tierSelected)
        internal constant returns(uint256 calculatedTokens)
    {
      require(weiPaid > 0);
      require(tierSelected >= 1 && tierSelected <= 4);

      if (tierSelected == 1) {

         calculatedTokens = weiPaid.mul(rate);

      } else if (tierSelected == 2) {

         calculatedTokens = weiPaid.mul(rateTier2);

      } else if (tierSelected == 3) {

         calculatedTokens = weiPaid.mul(rateTier3);

      } else {

         calculatedTokens = weiPaid.mul(rateTier4);
     }
   }

   // Confirm valid purchase
   function validPurchase() internal constant returns(bool) {
      bool withinPeriod = now >= startTime && now <= endTime;
      bool nonZeroPurchase = msg.value > 0;
      bool withinTokenLimit = tokensRaised < maxTokensRaised;
      bool minimumPurchase = msg.value >= minPurchase;

      return withinPeriod && nonZeroPurchase && withinTokenLimit && minimumPurchase;
   }

   // Check if sale ended
   function hasEnded() public constant returns(bool) {
       return now > endTime || tokensRaised >= maxTokensRaised;
   }

   // Finalize if ended
   function completeCrowdsale() external onlyOwner whenNotPaused {
       require(hasEnded());

       // Transfer left over tokens
       transferTokensLeftOver();

       // Call finalized event
       Finalized();
   }

   // Transfer any remaining tokens from Crowdsale
   function transferTokensLeftOver() internal {
       require(!remainingTransfered);
       require(maxTokensRaised > tokensRaised);

       remainingTransfered = true;

       uint256 remainingTokens = maxTokensRaised.sub(tokensRaised);
       token.distributeCrowdsaleTokens(msg.sender, remainingTokens);
   }

   // Change dates before crowdsale has started
   // @param _startTime - New start time
   // @param _endTime - New end time
   function changeDates(uint256 _startTime, uint256 _endTime)
        external onlyOwner beforeStarting
    {

       if (_startTime > 0 && _endTime > 0) {
           require(_startTime < _endTime);
       }

       if (_startTime > 0) {
           startTime = _startTime;
       }

       if (_endTime > 0) {
           endTime = _endTime;
       }
   }

   // Change the end date
   // @param _endTime - New end time
   function changeEndDate(uint256 _endTime) external onlyOwner {
       require(_endTime > startTime);
       require(_endTime > now);
       require(!hasEnded());

       if (_endTime > 0) {
           endTime = _endTime;
       }
   }

}
function buyTokens() public payable whenNotPaused {
   require(validPurchase());

   uint256 tokens = 0;
   uint256 amountPaid = adjustAmountValue();

   if (tokensRaised < limitTier1) {

      // Tier 1
      tokens = amountPaid.mul(rate);

      // If the amount of tokens that you want to buy gets out of this tier
      if (tokensRaised.add(tokens) > limitTier1) {

         tokens = adjustTokenTierValue(amountPaid, limitTier1, 1, rate);
      }

   } else if (tokensRaised >= limitTier1 && tokensRaised < limitTier2) {

      // Tier 2
      tokens = amountPaid.mul(rateTier2);

       // Breaks tier cap
      if (tokensRaised.add(tokens) > limitTier2) {
         tokens = adjustTokenTierValue(amountPaid, limitTier2, 2, rateTier2);
      }

   } else if (tokensRaised >= limitTier2 && tokensRaised < limitTier3) {

      // Tier 3
      tokens = amountPaid.mul(rateTier3);

      // Breaks tier cap
      if (tokensRaised.add(tokens) > limitTier3) {
         tokens = adjustTokenTierValue(amountPaid, limitTier3, 3, rateTier3);
      }

   } else if (tokensRaised >= limitTier3) {

      // Tier 4
      tokens = amountPaid.mul(rateTier4);

   }

   weiRaised = weiRaised.add(amountPaid);
   tokensRaised = tokensRaised.add(tokens);
   token.distributeCrowdsaleTokens(msg.sender, tokens);

   // Keep the records
   tokensBought[msg.sender] = tokensBought[msg.sender].add(tokens);

   // Broadcast event
   TokenPurchase(msg.sender, amountPaid, tokens);

   // Update records
   numberOfTransactions = numberOfTransactions.add(1);

   forwardFunds(amountPaid);
}

/// Buy tokens main