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
	{
		
		
	}

}