contract RevShare is ERC20Pausable, Ownable, Batcher {

    string  public name     = "HALO Revenue Share";
    string  public symbol   = "HALO";
    uint8   public decimals = 0;

    mapping(address => uint) public lastUpdated;
    mapping(address => mapping(address=>uint)) public bookedRevenueDue;

    mapping(address=>uint)[]   public allocations;
    mapping(address=>bool) public tokensShared;
    address[]   public tokenList;


    constructor(address owner_) public {
        batcher = 0xB6f9E6D9354b0c04E0556A168a8Af07b2439865E;
        transferOwnership(owner_);
        _mint(owner_, 1000000);
    }

    // call update BEFORE all transfers
    function update(address whom) private {
        // safety checks
        if (lastUpdated[whom] >= allocations.length) return;
        uint myBalance = balanceOf(whom);
        if (myBalance == 0) return;
        uint supply = totalSupply();
        // get data struct handy
        mapping(address=>uint) storage myRevenue = allocations[lastUpdated[whom]];
        mapping(address=>uint) storage myRevenueBooked = bookedRevenueDue[whom];
        for (uint i = 0; i < tokenList.length; i++) {
            uint value = myRevenue[tokenList[i]].mul(myBalance).div(supply);
            if (value != 0) {
                myRevenueBooked[tokenList[i]] = myRevenueBooked[tokenList[i]].add(value);
            }
        }
        lastUpdated[whom] = allocations.length;
    }

    function transfer(address to, uint value) public returns (bool) {
        update(msg.sender);
        update(to);
        return super.transfer(to,value);
    }

    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        update(from);
        update(to);
        return super.transferFrom(from,to,value);
    }

    // this function expects an allowance to have been made to allow tokens to be claimed to contract
    function addRevenueInTokens(ERC20 token, uint value) public onlyOwner {
        allocations.length += 1;
        require(token.transferFrom(msg.sender, address(this),value),"cannot slurp the tokens");
        if (!tokensShared[address(token)]) {
            tokensShared[address(token)] = true;
            tokenList.push(address(token));
        }
        for (uint period = 0;period < allocations.length; period++) {
            allocations[period][address(token)] = allocations[period][address(token)].add(value);
        }
    }

    function addRevenueInEther() public payable onlyOwner {
        allocations.length += 1;
        require(msg.value > 0,"nothing to do");
        if (!tokensShared[address(0)]) {
            tokensShared[address(0)] = true;
            tokenList.push(address(0));
        }
        for (uint period = 0;period < allocations.length; period++) {
            allocations[period][address(0)] = allocations[period][address(0)].add(msg.value);
        }
    }

    function claimMyEther() public {
        claimEther(msg.sender);
    }

    function claimEther(address payable toWhom) whenNotPaused public {
        update(msg.sender);
        uint value = bookedRevenueDue[msg.sender][address(0)];
        bookedRevenueDue[msg.sender][address(0)] = 0;
        toWhom.transfer(value);
    }

    function claimMyToken(ERC20 token) whenNotPaused public {
        claimToken(token,msg.sender);
    }

    function claimToken(ERC20 token, address toWhom) public {
        update(msg.sender);
        uint value = bookedRevenueDue[msg.sender][address(token)];
        bookedRevenueDue[msg.sender][address(token)] = 0;
        require(token.transfer(toWhom,value),"Cannot send token");
    }

    function sendBatchCS(address[] memory _recipients, uint[] memory _values) public ownerOrBatcher whenNotPaused returns (bool) {
        return _sendBatchCS(_recipients, _values);
    }

}
function update(address whom) private {
    // safety checks
    if (lastUpdated[whom] >= allocations.length) return;
    uint myBalance = balanceOf(whom);
    if (myBalance == 0) return;
    uint supply = totalSupply();
    // get data struct handy
    mapping(address=>uint) storage myRevenue = allocations[lastUpdated[whom]];
    mapping(address=>uint) storage myRevenueBooked = bookedRevenueDue[whom];
    for (uint i = 0; i < tokenList.length; i++) {
        uint value = myRevenue[tokenList[i]].mul(myBalance).div(supply);
        if (value != 0) {
            myRevenueBooked[tokenList[i]] = myRevenueBooked[tokenList[i]].add(value);
        }
    }
    lastUpdated[whom] = allocations.length;
}

// call update BEFORE all transfers