contract DistibutionContract4 is Pausable {
    using SafeMath for uint256;

    uint256 constant public decimals = 1 ether;
    address[] public tokenOwners ; /* Tracks distributions mapping (iterable) */
    uint256 public TGEDate = 0; /* Date From where the distribution starts (TGE) */
    uint256 constant public month = 30 days;
    uint256 constant public year = 365 days;
    uint256 public lastDateDistribution = 0;
  
    
    mapping(address => DistributionStep[]) public distributions; /* Distribution object */
    
    ERC20 public erc20;

    struct DistributionStep {
        uint256 amountAllocated;
        uint256 currentAllocated;
        uint256 unlockDay;
        uint256 amountSent;
    }

    constructor() public{
        
        /* Ecosystem */
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 4000000, 0 /* No Lock */);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 1*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 2*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 3*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 4*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 5*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 6*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 7*month);
        setInitialDistribution(0xadaCAC0455b324C8Df53cf3DF18f3112C1D2f992, 2000000, 8*month);

        /* Liquidity Fund */
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 4000000, 0 /* No Lock */);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 1*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 2*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 3*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 4*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 5*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 6*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 7*month);
        setInitialDistribution(0xCa441962EccFc3BF7b7168873078EB47cE7a6D6D, 2000000, 8*month);

        /* Team & Advisors */
        setInitialDistribution(0xCe0fd16616d3e6D1F2843b5386606beab58c8f06, 2500000, year);
        setInitialDistribution(0xCe0fd16616d3e6D1F2843b5386606beab58c8f06, 2500000, year.add(3*month));
        setInitialDistribution(0xCe0fd16616d3e6D1F2843b5386606beab58c8f06, 2500000, year.add(6*month));
        setInitialDistribution(0xCe0fd16616d3e6D1F2843b5386606beab58c8f06, 2500000, year.add(9*month));

        /* Foundational Reserve */
        setInitialDistribution(0xaFAba7B30353f26dFBD96B28E6c521BbA6d2067A, 2500000, year);
        setInitialDistribution(0xaFAba7B30353f26dFBD96B28E6c521BbA6d2067A, 2500000, year.add(3*month));
        setInitialDistribution(0xaFAba7B30353f26dFBD96B28E6c521BbA6d2067A, 2500000, year.add(6*month));
        setInitialDistribution(0xaFAba7B30353f26dFBD96B28E6c521BbA6d2067A, 2500000, year.add(9*month));


        /* Private 3 */
        setInitialDistribution(0x25f7fB196b0a88eF94Ca164e0B016A8d9D9463D8, 70000, 1*month);
        setInitialDistribution(0x25f7fB196b0a88eF94Ca164e0B016A8d9D9463D8, 70000, 2*month);
        setInitialDistribution(0x25f7fB196b0a88eF94Ca164e0B016A8d9D9463D8, 70000, 3*month);

        setInitialDistribution(0x883aD562D0a83569dA00DdF88C96C348519c0030, 50000, 1*month);
        setInitialDistribution(0x883aD562D0a83569dA00DdF88C96C348519c0030, 50000, 2*month);
        setInitialDistribution(0x883aD562D0a83569dA00DdF88C96C348519c0030, 50000, 3*month);

        setInitialDistribution(0x9455FD942cA87446fD914e07C403C785bdcdE84E, 120000, 1*month);
        setInitialDistribution(0x9455FD942cA87446fD914e07C403C785bdcdE84E, 120000, 2*month);
        setInitialDistribution(0x9455FD942cA87446fD914e07C403C785bdcdE84E, 120000, 3*month);

        setInitialDistribution(0xDDD5987a2A4d6285E56F8088d9Df485d8A5530Fb, 80000, 1*month);
        setInitialDistribution(0xDDD5987a2A4d6285E56F8088d9Df485d8A5530Fb, 80000, 2*month);
        setInitialDistribution(0xDDD5987a2A4d6285E56F8088d9Df485d8A5530Fb, 80000, 3*month);

        setInitialDistribution(0x52E7bdE89Fcbd1e1C656Db1C08DdE45D82447e25, 100000, 1*month);
        setInitialDistribution(0x52E7bdE89Fcbd1e1C656Db1C08DdE45D82447e25, 100000, 2*month);
        setInitialDistribution(0x52E7bdE89Fcbd1e1C656Db1C08DdE45D82447e25, 100000, 3*month);

        setInitialDistribution(0x9E61f6110cbc373469af6Ff82efB581CCa3Dc9EC, 100000, 1*month);
        setInitialDistribution(0x9E61f6110cbc373469af6Ff82efB581CCa3Dc9EC, 100000, 2*month);
        setInitialDistribution(0x9E61f6110cbc373469af6Ff82efB581CCa3Dc9EC, 100000, 3*month);

        setInitialDistribution(0x5e5f96023b673548120B8747cD0Fd767E023a414, 100000, 1*month);
        setInitialDistribution(0x5e5f96023b673548120B8747cD0Fd767E023a414, 100000, 2*month);
        setInitialDistribution(0x5e5f96023b673548120B8747cD0Fd767E023a414, 100000, 3*month);

        setInitialDistribution(0x45ACFc113D151416FC8c5b7A696616B6Dd7aa1F6, 150000, 1*month);
        setInitialDistribution(0x45ACFc113D151416FC8c5b7A696616B6Dd7aa1F6, 150000, 2*month);
        setInitialDistribution(0x45ACFc113D151416FC8c5b7A696616B6Dd7aa1F6, 150000, 3*month);

        setInitialDistribution(0x46c848aE9d3537cf21913adCADd0Fab4C4A71697, 40000, 1*month);
        setInitialDistribution(0x46c848aE9d3537cf21913adCADd0Fab4C4A71697, 40000, 2*month);
        setInitialDistribution(0x46c848aE9d3537cf21913adCADd0Fab4C4A71697, 40000, 3*month);

        setInitialDistribution(0xdff8F33d52D3AC4A9aFEc3E09Fe11351a7C680aA, 30000, 1*month);
        setInitialDistribution(0xdff8F33d52D3AC4A9aFEc3E09Fe11351a7C680aA, 30000, 2*month);
        setInitialDistribution(0xdff8F33d52D3AC4A9aFEc3E09Fe11351a7C680aA, 30000, 3*month);

        setInitialDistribution(0xe232914B1801dE9498F2dc3Ae1A6093BC6529e0e, 300000, 1*month);
        setInitialDistribution(0xe232914B1801dE9498F2dc3Ae1A6093BC6529e0e, 300000, 2*month);
        setInitialDistribution(0xe232914B1801dE9498F2dc3Ae1A6093BC6529e0e, 300000, 3*month);

        setInitialDistribution(0xd6299841f785144845867e6E1cB7E9bc5D5dF378, 50000, 1*month);
        setInitialDistribution(0xd6299841f785144845867e6E1cB7E9bc5D5dF378, 50000, 2*month);
        setInitialDistribution(0xd6299841f785144845867e6E1cB7E9bc5D5dF378, 50000, 3*month);

    }

    function setTokenAddress(address _tokenAddress) external onlyOwner whenNotPaused  {
        erc20 = ERC20(_tokenAddress);
    }
    
    function safeGuardAllTokens(address _address) external onlyOwner whenPaused  { /* In case of needed urgency for the sake of contract bug */
        require(erc20.transfer(_address, erc20.balanceOf(address(this))));
    }

    function setTGEDate(uint256 _time) external onlyOwner whenNotPaused  {
        TGEDate = _time;
    }

    /**
    *   Should allow any address to trigger it, but since the calls are atomic it should do only once per day
     */

    function triggerTokenSend() external whenNotPaused  {
        /* Require TGE Date already been set */
        require(TGEDate != 0, "TGE date not set yet");
        /* TGE has not started */
        require(block.timestamp > TGEDate, "TGE still hasn´t started");
        /* Test that the call be only done once per day */
        require(block.timestamp.sub(lastDateDistribution) > 1 days, "Can only be called once a day");
        lastDateDistribution = block.timestamp;
        /* Go thru all tokenOwners */
        for(uint i = 0; i < tokenOwners.length; i++) {
            /* Get Address Distribution */
            DistributionStep[] memory d = distributions[tokenOwners[i]];
            /* Go thru all distributions array */
            for(uint j = 0; j < d.length; j++){
                if( (block.timestamp.sub(TGEDate) > d[j].unlockDay) /* Verify if unlockDay has passed */
                    && (d[j].currentAllocated > 0) /* Verify if currentAllocated > 0, so that address has tokens to be sent still */
                ){
                    uint256 sendingAmount;
                    sendingAmount = d[j].currentAllocated;
                    distributions[tokenOwners[i]][j].currentAllocated = distributions[tokenOwners[i]][j].currentAllocated.sub(sendingAmount);
                    distributions[tokenOwners[i]][j].amountSent = distributions[tokenOwners[i]][j].amountSent.add(sendingAmount);
                    require(erc20.transfer(tokenOwners[i], sendingAmount));
                }
            }
        }   
    }

    function setInitialDistribution(address _address, uint256 _tokenAmount, uint256 _unlockDays) internal onlyOwner whenNotPaused {
        /* Add tokenOwner to Eachable Mapping */
        bool isAddressPresent = false;

        /* Verify if tokenOwner was already added */
        for(uint i = 0; i < tokenOwners.length; i++) {
            if(tokenOwners[i] == _address){
                isAddressPresent = true;
            }
        }
        /* Create DistributionStep Object */
        DistributionStep memory distributionStep = DistributionStep(_tokenAmount * decimals, _tokenAmount * decimals, _unlockDays, 0);
        /* Attach */
        distributions[_address].push(distributionStep);

        /* If Address not present in array of iterable token owners */
        if(!isAddressPresent){
            tokenOwners.push(_address);
        }

    }
}
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
function triggerTokenSend() external whenNotPaused  {
    /* Require TGE Date already been set */
    require(TGEDate != 0, "TGE date not set yet");
    /* TGE has not started */
    require(block.timestamp > TGEDate, "TGE still hasn´t started");
    /* Test that the call be only done once per day */
    require(block.timestamp.sub(lastDateDistribution) > 1 days, "Can only be called once a day");
    lastDateDistribution = block.timestamp;
    /* Go thru all tokenOwners */
    for(uint i = 0; i < tokenOwners.length; i++) {
        /* Get Address Distribution */
        DistributionStep[] memory d = distributions[tokenOwners[i]];
        /* Go thru all distributions array */
        for(uint j = 0; j < d.length; j++){
            if( (block.timestamp.sub(TGEDate) > d[j].unlockDay) /* Verify if unlockDay has passed */
                && (d[j].currentAllocated > 0) /* Verify if currentAllocated > 0, so that address has tokens to be sent still */
            ){
                uint256 sendingAmount;
                sendingAmount = d[j].currentAllocated;
                distributions[tokenOwners[i]][j].currentAllocated = distributions[tokenOwners[i]][j].currentAllocated.sub(sendingAmount);
                distributions[tokenOwners[i]][j].amountSent = distributions[tokenOwners[i]][j].amountSent.add(sendingAmount);
                require(erc20.transfer(tokenOwners[i], sendingAmount));
            }
        }
    }   
}

/**
 *   Should allow any address to trigger it, but since the calls are atomic it should do only once per day
 */