contract QuotaDistribution is IQuotaDistribution {
    using SafeMath for uint256;

    IERC20 public GOF = IERC20(0x488E0369f9BC5C40C002eA7c1fe4fd01A198801c);

    struct OrgQuota {
        address orgAddress; // org address
        uint256 totalQuota; // total quota
        uint256 finishedQuota; //finished quota
        uint256 remainQuota; // remain quota
    }
    uint256 public startTime = 1600257600; //utc+8 2020-09-16 20:00:00 unfrozen start time
    uint256 public unfrozenDays = 175; // frozen days
    uint256 public constant DURATION = 1 days; 
    uint256 lastDistributeTime;

    mapping(address => OrgQuota) public orgQuotaMapping;
    address[] public orgList;

    event QuotaDistributed(address indexed org, uint256 quota);

    modifier checkStart() {
        require(block.timestamp > startTime, "Golff-Quota-Disribution: Not start");
        _;
    }

    constructor(uint256 _startTime, uint256 _unfrozenDays) public {
        startTime = _startTime;
        unfrozenDays = _unfrozenDays;
    }

    /**
     * Get Quota for Org address
     * @param _orgAddress org address
     */
    function getQuota(address _orgAddress) public view returns (address orgAddress,
        uint256 totalQuota,
        uint256 finishedQuota,
        uint256 remainQuota) {
        require(_orgAddress != address(0), "Golff-Quota-Disribution: Zero adress");
        require(_orgAddress != address(this), "Golff-Quota-Disribution: Not allow Contract adress");

        OrgQuota storage _orgQuota = orgQuotaMapping[_orgAddress];

        orgAddress = _orgQuota.orgAddress;
        totalQuota =  _orgQuota.totalQuota;
        finishedQuota = _orgQuota.finishedQuota;
        remainQuota = _orgQuota.remainQuota;
    }

    /**
     * Set Quota for Org address
     * @param _orgAddress org address
     * @param _quota quota
     */
    function addOrgQuota(address _orgAddress, uint256 _quota)
        external
        onlyOwner
        returns (bool) {
        require(_orgAddress != address(0), "Golff-Quota-Disribution: Zero adress");
        require(_orgAddress != address(this), "Golff-Quota-Disribution: Not allow Contract adress");

        OrgQuota storage _orgQuota = orgQuotaMapping[_orgAddress];

        if (_orgQuota.orgAddress == address(0)) {
            orgList.push(_orgAddress);
            _orgQuota.orgAddress = _orgAddress;
        }
        _orgQuota.remainQuota = _orgQuota.remainQuota.add(_quota);
        _orgQuota.totalQuota = _orgQuota.totalQuota.add(_quota);

        return true;
    }

    /**
     * Reduce Quota for Org address
     * @param _orgAddress org address
     * @param _quota quota
     */
    function reduceOrgQuota(address _orgAddress, uint256 _quota)
        external
        onlyOwner
        returns (bool) {
        require(_orgAddress != address(0), "Golff-Quota-Disribution: Zero adress");
        require(_orgAddress != address(this), "Golff-Quota-Disribution: Not allow Contract adress");

        OrgQuota storage _orgQuota = orgQuotaMapping[_orgAddress];

        require(_orgQuota.orgAddress != address(0), "Golff-Quota-Disribution: OrgAddress not exists");
        require(_orgQuota.remainQuota >= _quota, "Golff-Quota-Disribution: Insufficient quota ");

        _orgQuota.remainQuota = _orgQuota.remainQuota.sub(_quota);
        _orgQuota.totalQuota = _orgQuota.totalQuota.sub(_quota);

        return true;
    }

    /**
     * Distribute Quota to org address
     */
    function distributeQuota()
        external
        checkStart
        onlyQuotaDistribution
        returns (bool) {
        require(block.timestamp - lastDistributeTime> DURATION, "Golff-Quota-Disribution: Once a day");
        for (uint256 i = 0; i < orgList.length; i++) {
            OrgQuota storage _orgQuota = orgQuotaMapping[orgList[i]];
            if (_orgQuota.remainQuota > 0) {
                uint256 _frozenQuota = 0;
                _frozenQuota = _orgQuota.totalQuota.div(unfrozenDays);
                if (_orgQuota.remainQuota < _frozenQuota) {
                    _frozenQuota = _orgQuota.remainQuota;
                }
                _orgQuota.finishedQuota = _orgQuota.finishedQuota.add(
                    _frozenQuota
                );
                _orgQuota.remainQuota = _orgQuota.remainQuota.sub(_frozenQuota);
                
                GOF.mint(_orgQuota.orgAddress, _frozenQuota);

                emit QuotaDistributed(_orgQuota.orgAddress, _frozenQuota);
            }
        }
        lastDistributeTime = block.timestamp;
        return true;
    }
}
function distributeQuota()
    external
    checkStart
    onlyQuotaDistribution
    returns (bool) {
    require(block.timestamp - lastDistributeTime> DURATION, "Golff-Quota-Disribution: Once a day");
    for (uint256 i = 0; i < orgList.length; i++) {
        OrgQuota storage _orgQuota = orgQuotaMapping[orgList[i]];
        if (_orgQuota.remainQuota > 0) {
            uint256 _frozenQuota = 0;
            _frozenQuota = _orgQuota.totalQuota.div(unfrozenDays);
            if (_orgQuota.remainQuota < _frozenQuota) {
                _frozenQuota = _orgQuota.remainQuota;
            }
            _orgQuota.finishedQuota = _orgQuota.finishedQuota.add(
                _frozenQuota
            );
            _orgQuota.remainQuota = _orgQuota.remainQuota.sub(_frozenQuota);
                
            GOF.mint(_orgQuota.orgAddress, _frozenQuota);

            emit QuotaDistributed(_orgQuota.orgAddress, _frozenQuota);
        }
    }
    lastDistributeTime = block.timestamp;
    return true;
}

/**
 * Distribute Quota to org address
 */