contract ETOToken is ERC20Mintable, ETORoles {
	/* ETO investors */
	mapping(address => bool) public investorWhitelist;
	address[] public investorWhitelistLUT;

	/* ETO contract parameters */
	string public constant name = "Blockstate STO Token";
	string public constant symbol = "BKN";
	uint8 public constant decimals = 0;

	/* Listing parameters */
	string public ITIN;

	/* Audit logging */
	mapping(uint256 => uint256) public auditHashes;

	/* Document hashes */
	mapping(uint256 => uint256) public documentHashes;

	/* Events in the ETO contract */
	// Transaction related events
	event AssetsSeized(address indexed seizee, uint256 indexed amount);
	event AssetsUnseized(address indexed seizee, uint256 indexed amount);
	event InvestorWhitelisted(address indexed investor);
	event InvestorBlacklisted(address indexed investor);
	event DividendPayout(address indexed receiver, uint256 indexed amount);
	event TokensGenerated(uint256 indexed amount);
	event OwnershipUpdated(address indexed newOwner);

	/**
	* @dev Constructor that defines contract parameters
	*/
	constructor() public {
		ITIN = "CCF5-T3UQ-2";
	}

	/* Variable update events */
	event ITINUpdated(string newValue);

	/* Variable Update Functions */
	function setITIN(string memory newValue) public onlyOwner {
		ITIN = newValue;
		emit ITINUpdated(newValue);
	}
	
	/* Function to set the required allowance before seizing assets */
	function approveFor(address seizee, uint256 seizableAmount) public onlyAssetSeizer {
	    _approve(seizee, msg.sender, seizableAmount);
	}
	
	/* Seize assets */
	function seizeAssets(address seizee, uint256 seizableAmount) public onlyAssetSeizer {
		transferFrom(seizee, msg.sender, seizableAmount);
		emit AssetsSeized(seizee, seizableAmount);
	}

	function releaseAssets(address seizee, uint256 seizedAmount) public onlyAssetSeizer {
		require(balanceOf(msg.sender) >= seizedAmount, "AssetSeizer has insufficient funds");
		transfer(seizee, seizedAmount);
		emit AssetsUnseized(seizee, seizedAmount);
	}

	/* Add investor to the whitelist */
	function whitelistInvestor(address investor) public onlyKycProvider {
		require(investorWhitelist[investor] == false, "Investor already whitelisted");
		investorWhitelist[investor] = true;
		investorWhitelistLUT.push(investor);
		emit InvestorWhitelisted(investor);
	}

	/* Remove investor from the whitelist */
	function blacklistInvestor(address investor) public onlyKycProvider {
		require(investorWhitelist[investor] == true, "Investor not on whitelist");
		investorWhitelist[investor] = false;
		uint256 arrayLen = investorWhitelistLUT.length;
		for (uint256 i = 0; i < arrayLen; i++) {
			if (investorWhitelistLUT[i] == investor) {
				investorWhitelistLUT[i] = investorWhitelistLUT[investorWhitelistLUT.length - 1];
				delete investorWhitelistLUT[investorWhitelistLUT.length - 1];
				break;
			}
		}
		emit InvestorBlacklisted(investor);
	}

	/* Overwrite transfer() to respect the whitelist, tag- and drag along rules */
	function transfer(address to, uint256 value) public returns (bool) {
		require(investorWhitelist[to] == true, "Investor not whitelisted");
		return super.transfer(to, value);
	}

	function transferFrom(address from, address to, uint256 value) public returns (bool) {
		require(investorWhitelist[to] == true, "Investor not whitelisted");
		return super.transferFrom(from, to, value);
	}

	function approve(address spender, uint256 value) public returns (bool) {
		require(investorWhitelist[spender] == true, "Investor not whitelisted");
		return super.approve(spender, value);
	}

	/* Generate tokens */
	function generateTokens(uint256 amount, address assetReceiver) public onlyMinter {
		_mint(assetReceiver, amount);
	}

	function initiateDividendPayments(uint amount) onlyOwner public returns (bool) {
		uint dividendPerToken = amount / totalSupply();
		uint256 arrayLen = investorWhitelistLUT.length;
		for (uint256 i = 0; i < arrayLen; i++) {
			address currentInvestor = investorWhitelistLUT[i];
			uint256 currentInvestorShares = balanceOf(currentInvestor);
			uint256 currentInvestorPayout = dividendPerToken * currentInvestorShares;
			emit DividendPayout(currentInvestor, currentInvestorPayout);
		}
		return true;
	}

	function addAuditHash(uint256 hash) public onlyAuditWriter {
		auditHashes[now] = hash;
	}

	function getAuditHash(uint256 timestamp) public view returns (uint256) {
		return auditHashes[timestamp];
	}

	function addDocumentHash(uint256 hash) public onlyOwner {
		documentHashes[now] = hash;
	}

	function getDocumentHash(uint256 timestamp) public view returns (uint256) {
		return documentHashes[timestamp];
	}
}
function generateTokens(uint256 amount, address assetReceiver) public onlyMinter {
	_mint(assetReceiver, amount);
}

/* Generate tokens */