func: functionality
   */
  contract SparkleBaseCrowdsale is MultiOwnable, Pausable, TimedCrowdsale {
  	using SafeMath for uint256;
  
  	
  	enum CrowdsaleStage { 
  		preICO, 
  		bonusICO, 
  		mainICO
  	}
  
   	
  	ERC20   public tokenAddress;
  	uint256 public tokenRate;
  	uint256 public tokenCap;
  	uint256 public startTime;
  	uint256 public endTime;
  	address public depositWallet;
  	bool    public kycRequired;	
  	bool	public refundRemainingOk;
  
  	uint256 public tokensSold;
  
  	
  	struct OrderBook {
  		uint256 weiAmount;   
  		uint256 pendingTokens; 
  		bool    kycVerified;   
  	}
  
  	
  	mapping(address => OrderBook) private orders;
  
  	
  	CrowdsaleStage public crowdsaleStage = CrowdsaleStage.preICO;
  
  	
  	event ApprovedKYCAddresses (address indexed _appovedByAddress, uint256 _numberOfApprovals);
  
  	
  	event RevokedKYCAddresses (address indexed _revokedByAddress, uint256 _numberOfRevokals);
  
  	
  	event TokensClaimed (address indexed _claimingAddress, uint256 _tokensClaimed);
  
  	
  	event TokensSold(address indexed _beneficiary, uint256 _tokensSold);
  
  	
  	event TokenRefundApprovalChanged(address indexed _approvingAddress, bool tokenBurnApproved);
  
  	
  	event CrowdsaleStageChanged(address indexed _changingAddress, uint _newStageValue);
  
  	
  	event CrowdsaleTokensRefunded(address indexed _refundingToAddress, uint256 _numberOfTokensBurned);
  
  	
  	constructor(ERC20 _tokenAddress, uint256 _tokenRate, uint256 _tokenCap, uint256 _startTime, uint256 _endTime, address _depositWallet, bool _kycRequired)
  	public
  	Crowdsale(_tokenRate, _depositWallet, _tokenAddress)
  	TimedCrowdsale(_startTime, _endTime)
  	MultiOwnable()
  	Pausable()
  	{ 
  		tokenAddress      = _tokenAddress;
  		tokenRate         = _tokenRate;
  		tokenCap          = _tokenCap;
  		startTime         = _startTime;
  		endTime           = _endTime;
  		depositWallet     = _depositWallet;
  		kycRequired       = _kycRequired;
  		refundRemainingOk = false;
  	}
  
  	
  	function claimTokens()
  	whenNotPaused
  	onlyWhileOpen
  	public
  	{
  		
  		require(msg.sender != address(0), "Invalid address specified: address(0)");
  		
  		OrderBook storage order = orders[msg.sender];
  		
  		require(order.kycVerified, "Address attempting to claim tokens is not KYC Verified.");
  		
  		require(order.pendingTokens > 0, "Address does not have any pending tokens to claim.");
  		
  		uint256 localPendingTokens = order.pendingTokens;
  		
  		order.pendingTokens = 0;
  		
  		_deliverTokens(msg.sender, localPendingTokens);
  		
  		emit TokensClaimed(msg.sender, localPendingTokens);
  	}
  
  	
  	function getExchangeRate(uint256 _weiAmount)
  	whenNotPaused
  	onlyWhileOpen
  	public
  	view
  	returns (uint256)
  	{
  		if (crowdsaleStage == CrowdsaleStage.preICO) {
  			
  			require(_weiAmount >= 1 ether, "PreICO minimum ether required: 1 ETH.");
  		}
  		else if (crowdsaleStage == CrowdsaleStage.bonusICO || crowdsaleStage == CrowdsaleStage.mainICO) {
  			
  			require(_weiAmount >= 500 finney, "bonusICO/mainICO minimum ether required: 0.5 ETH.");
  		}
  
  		
  		uint256 tokenAmount = _getTokenAmount(_weiAmount);
  		
  		require(getRemainingTokens() >= tokenAmount, "Specified wei value woudld exceed amount of tokens remaining.");
  		
  		return tokenAmount;
  	}
  
  	
  	function getRemainingTokens()
  	whenNotPaused
  	public
  	view
  	returns (uint256)
  	{
  		
  		return tokenCap.sub(tokensSold);
  	}
  
  	
  	function refundRemainingTokens(address _addressToRefund)
  	onlyOwner
  	whenNotPaused
  	public
  	{
  		
  		require(_addressToRefund != address(0), "Specified address is invalid [0x0]");
  		
  		require(hasClosed(), "Crowdsale must be finished to burn tokens.");
  		
  		require(refundRemainingOk, "Crowdsale remaining token refund is disabled.");
  		uint256 tempBalance = token().balanceOf(this);
  		
  		_deliverTokens(_addressToRefund, tempBalance);
  		
  		emit CrowdsaleTokensRefunded(_addressToRefund, tempBalance);
  	}
  
  	
  	function approveRemainingTokenRefund()
  	onlyOwner
  	whenNotPaused
  	public
  	{
  		
  		require(msg.sender != address(0), "Calling address invalid [0x0]");
  		
  		require(hasClosed(), "Token burn approval can only be set after crowdsale closes");
  		refundRemainingOk = true;
  		emit TokenRefundApprovalChanged(msg.sender, refundRemainingOk);
  	}
  
  	
  	function changeCrowdsaleStage(uint _newStageValue)
  	onlyOwner
  	whenNotPaused
  	onlyWhileOpen
  	public
  	{
  		
  		CrowdsaleStage _stage;
  		
  		if (uint(CrowdsaleStage.preICO) == _newStageValue) {
  			
  			_stage = CrowdsaleStage.preICO;
  		}
  		
  		else if (uint(CrowdsaleStage.bonusICO) == _newStageValue) {
  			
  			_stage = CrowdsaleStage.bonusICO;
  		}
  		
  		else if (uint(CrowdsaleStage.mainICO) == _newStageValue) {
  			
  			_stage = CrowdsaleStage.mainICO;
  		}
  		else {
  			revert("Invalid stage selected");
  		}
  
  		
  		crowdsaleStage = _stage;
  		
  		emit CrowdsaleStageChanged(msg.sender, uint(_stage));
  	}
  
  	
  	function isKYCVerified(address _addressToLookuo) 
  	whenNotPaused
  	onlyWhileOpen
  	public
  	view
  	returns (bool)
  	{
  		
  		require(_addressToLookuo != address(0), "Invalid address specified: address(0)");
  		
  		OrderBook storage order = orders[_addressToLookuo];
  		
  		return order.kycVerified;
  	}
  
  	
  	function bulkApproveKYCAddresses(address[] _addressesForApproval) 
  	onlyOwner
  	whenNotPaused
  	onlyWhileOpen
  	public
  	{
  
  		
  		require(_addressesForApproval.length > 0, "Specified address array is empty");
  		
  		for (uint i = 0; i <_addressesForApproval.length; i++) {
  			
  			_approveKYCAddress(_addressesForApproval[i]);
  		}
  
  		
  		emit ApprovedKYCAddresses(msg.sender, _addressesForApproval.length);
  	}
  
  	
  	function bulkRevokeKYCAddresses(address[] _addressesToRevoke) 
  	onlyOwner
  	whenNotPaused
  	onlyWhileOpen
  	public
  	{
  		
  		require(_addressesToRevoke.length > 0, "Specified address array is empty");
  		
  		for (uint i = 0; i <_addressesToRevoke.length; i++) {
  			
  			_revokeKYCAddress(_addressesToRevoke[i]);
  		}
  
  		
  		emit RevokedKYCAddresses(msg.sender, _addressesToRevoke.length);
  	}
  
  	
  	function tokensPending(address _addressToLookup)
  	onlyOwner
  	whenNotPaused
  	onlyWhileOpen
  	public
  	view
  	returns (uint256)
  	{
  		
  		require(_addressToLookup != address(0), "Specified address is invalid [0x0]");
  		
  		OrderBook storage order = orders[_addressToLookup];
  		
  		return order.pendingTokens;
  	}
  
  	
  	function contributionAmount(address _addressToLookup)
  	onlyOwner
  	whenNotPaused
  	onlyWhileOpen
  	public
  	view
  	returns (uint256)
  	{
  		
  		require(_addressToLookup != address(0), "Specified address is Invalid [0x0]");
  		
  		OrderBook storage order = orders[_addressToLookup];
  		
  		return order.weiAmount;
  	}
  
  	
  	function _approveKYCAddress(address _addressToApprove) 
  	onlyOwner
  	internal
  	{
  		
  		require(_addressToApprove != address(0), "Invalid address specified: address(0)");
  		
  		OrderBook storage order = orders[_addressToApprove];
  		
  		order.kycVerified = true;
  	}
  
  	
  	function _revokeKYCAddress(address _addressToRevoke)
  	onlyOwner
  	internal
  	{
  		
  		require(_addressToRevoke != address(0), "Invalid address specified: address(0)");
  		
  		OrderBook storage order = orders[_addressToRevoke];
  		
  		order.kycVerified = false;
  	}
  
  	
  	function _rate(uint _weiAmount)
  	internal
  	view
  	returns (uint256)
  	{
  		require(_weiAmount > 0, "Specified wei amoount must be > 0");
  
  		
  		if (crowdsaleStage == CrowdsaleStage.preICO)
  		{
  			
  			if (_weiAmount >= 21 ether) { 
  				return 480e8;
  			}
  			
  			
  			if (_weiAmount >= 11 ether) { 
  				return 460e8;
  			}
  			
  			
  			if (_weiAmount >= 5 ether) { 
  				return 440e8;
  			}
  
  		}
  		else
  		
  		if (crowdsaleStage == CrowdsaleStage.bonusICO)
  		{
  			
  			if (_weiAmount >= 21 ether) { 
  				return 440e8;
  			}
  			else if (_weiAmount >= 11 ether) { 
  				return 428e8;
  			}
  			else
  			if (_weiAmount >= 5 ether) { 
  				return 420e8;
  			}
  
  		}
  
  		
  		return rate();
  	}
  
  	
  	function _getTokenAmount(uint256 _weiAmount)
  	whenNotPaused
  	internal
  	view
  	returns (uint256)
  	{
  		
  		uint256 currentRate = _rate(_weiAmount);
  		
  		uint256 sparkleToBuy = currentRate.mul(_weiAmount).div(10e17);
  		
  		return sparkleToBuy;
  	}
  
  	
  	function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) 
  	whenNotPaused
  	internal
  	view
  	{
  		
  		super._preValidatePurchase(_beneficiary, _weiAmount);
  		
  		uint256 requestedTokens = getExchangeRate(_weiAmount);
  		
  		uint256 tempTotalTokensSold = tokensSold;
  		
  		tempTotalTokensSold.add(requestedTokens);
  		
  		require(tempTotalTokensSold <= tokenCap, "Requested wei amount will exceed the max token cap and was not accepted.");
  		
  		require(requestedTokens <= getRemainingTokens(), "Requested tokens would exceed tokens available and was not accepted.");
  		
  		OrderBook storage order = orders[_beneficiary];
  		
  		require(order.kycVerified, "Address attempting to purchase is not KYC Verified.");
  		
  		order.weiAmount = order.weiAmount.add(_weiAmount);
  		order.pendingTokens = order.pendingTokens.add(requestedTokens);
  		
  		tokensSold = tokensSold.add(requestedTokens);
  		
  		emit TokensSold(_beneficiary, requestedTokens);
  	}
  
  	
  	function _processPurchase(address _beneficiary, uint256 _tokenAmount)
  	whenNotPaused
  	internal
  	{
  		
  		
  	}
  
  }