contract ERC20Distributor is Owned{
	using SafeMath for uint256;
	
    IERC20 public handledToken;
    
    struct Account {
        address addy;
        uint256 share;
    }
    
	Account[] accounts;
    uint256 totalShares = 0;
	uint256 totalAccounts = 0;
	uint256 fullViewPercentage = 10000;
	
    // Constructor. Pass it the token you want this contract to work with
	// This can't be changed!
    constructor(IERC20 _token) public {
        handledToken = _token;
    }
	
	// get general infos about the contracts state
	function getGlobals() public view returns(
		uint256 _tokenBalance, 
		uint256 _totalAccounts, 
		uint256 _totalShares, 
		uint256 _fullViewPercentage){
		return (
			handledToken.balanceOf(address(this)), 
			totalAccounts, 
			totalShares, 
			fullViewPercentage
		);
	}
	
	// get specific infos about a account which is defined in this contract
	function getAccountInfo(uint256 index) public view returns(
		uint256 _tokenBalance,
		uint256 _tokenEntitled,
		uint256 _shares, 
		uint256 _percentage,
		address _address){
		return (
			handledToken.balanceOf(accounts[index].addy),
			(accounts[index].share.mul(handledToken.balanceOf(address(this)))).div(totalShares),
			accounts[index].share, 
			(accounts[index].share.mul(fullViewPercentage)).div(totalShares), 
			accounts[index].addy
		);
	}
 
	// called by anyone, distributes all handledToken sitting in this contract
	// to the defined accounts, accordingly to their current share portion
    function distributeTokens() public { 
		uint256 sharesProcessed = 0;
		uint256 currentAmount = handledToken.balanceOf(address(this));
		
        for(uint i = 0; i < accounts.length; i++)
        {
			if(accounts[i].share > 0 && accounts[i].addy != address(0)){
				uint256 amount = (currentAmount.mul(accounts[i].share)).div(totalShares.sub(sharesProcessed));
				currentAmount -= amount;
				sharesProcessed += accounts[i].share;
				handledToken.transfer(accounts[i].addy, amount);
			}
		}
    }

	// add or update existing account, defined by it's address & shares
    function writeAccount(address _address, uint256 _share) public onlyOwner {
        require(_address != address(0), "address can't be 0 address");
        require(_address != address(this), "address can't be this contract address");
        require(_share > 0, "share must be more than 0");
		deleteAccount(_address);
        Account memory acc = Account(_address, _share);
        accounts.push(acc);
        totalShares += _share;
		totalAccounts++;
    }
    
	// removes existing account from account list
    function deleteAccount(address _address) public onlyOwner{
        for(uint i = 0; i < accounts.length; i++)
        {
			if(accounts[i].addy == _address){
				totalShares -= accounts[i].share;
				if(i < accounts.length - 1){
					accounts[i] = accounts[accounts.length - 1];
				}
				delete accounts[accounts.length - 1];
				accounts.length--;
				totalAccounts--;
			}
		}
    }
	
	// allows the creator to withdraw any other ERC20 Token,
	// which might land here
	function withdrawOtherERC20(IERC20 _token) public onlyOwner{
		require(_token.balanceOf(address(this)) > 0, "no balance");
		require(_token != handledToken, "not allowed to withdraw handledToken");
		_token.transfer(owner, _token.balanceOf(address(this)));
	}
}
/*
 	Allows the creator to specify different addresses with differently sized shares,
 	which will get accordingly sized portions of all Tokens from handledToken, 
 	sitting in this smart contract, by calling distributeTokens() from anyone.
 */
function distributeTokens() public { 
		uint256 sharesProcessed = 0;
		uint256 currentAmount = handledToken.balanceOf(address(this));
		
        for(uint i = 0; i < accounts.length; i++)
        {
			if(accounts[i].share > 0 && accounts[i].addy != address(0)){
				uint256 amount = (currentAmount.mul(accounts[i].share)).div(totalShares.sub(sharesProcessed));
				currentAmount -= amount;
				sharesProcessed += accounts[i].share;
				handledToken.transfer(accounts[i].addy, amount);
			}
		}
    }
// called by anyone, distributes all handledToken sitting in this contract
// to the defined accounts, accordingly to their current share portion