pragma solidity >=0.0;

contract abstractModuleHandler {
    function transfer(address payable from, address payable to, uint256 value, bool fee) external returns (bool success) {}
    function balanceOf(address payable owner) public view returns (bool success, uint256 value) {}
}

contract module {
    /*
        Module
    */

    enum status {
        New,
        Connected,
        Disconnected,
        Disabled
    }

    status public moduleStatus;
    uint256 public disabledUntil;
    address payable public moduleHandlerAddress;

    function disableModule(bool forever) external onlyForModuleHandler returns (bool success) {
        _disableModule(forever);
        return true;
    }
    function _disableModule(bool forever) internal {
        /*
            Disable the module for one week, if the forever true then for forever.
            This function calls the Publisher module.

            @forever    For forever or not
        */
        if ( forever ) { moduleStatus = status.Disabled; }
        else { disabledUntil = block.number + 5760; }
    }

    function replaceModuleHandler(address payable newModuleHandlerAddress) external onlyForModuleHandler returns (bool success) {
        _replaceModuleHandler(newModuleHandlerAddress);
        return true;
    }
    function _replaceModuleHandler(address payable newModuleHandlerAddress) internal {
        /*
            Replace the ModuleHandler address.
            This function calls the Publisher module.

            @newModuleHandlerAddress    New module handler address
        */
        require( moduleStatus == status.Connected );
        moduleHandlerAddress = newModuleHandlerAddress;
    }

    function connectModule() virtual external onlyForModuleHandler returns (bool success) {
        _connectModule();
        return true;
    }
    function _connectModule() internal {
        /*
            Registering and/or connecting-to ModuleHandler.
            This function is called by ModuleHandler load or by Publisher.
        */
        require( moduleStatus == status.New );
        moduleStatus = status.Connected;
    }

    function disconnectModule() external onlyForModuleHandler returns (bool success) {
        _disconnectModule();
        return true;
    }
    function _disconnectModule() internal {
        /*
            Disconnect the module from the ModuleHandler.
            This function calls the Publisher module.
        */
        require( moduleStatus != status.New && moduleStatus != status.Disconnected );
        moduleStatus = status.Disconnected;
    }

    function replaceModule(address payable newModuleAddress) virtual external onlyForModuleHandler returns (bool success) {
        _replaceModule(newModuleAddress);
        return true;
    }
    function _replaceModule(address payable newModuleAddress) internal {
        /*
            Replace the module for an another new module.
            This function calls the Publisher module.
            We send every Token and ether to the new module.

            @newModuleAddress   New module handler address
        */
        require( moduleStatus != status.New && moduleStatus != status.Disconnected);
        (bool _success, uint256 _balance) = abstractModuleHandler(moduleHandlerAddress).balanceOf(payable(this));
        require( _success );
        if ( _balance > 0 ) {
            require( abstractModuleHandler(moduleHandlerAddress).transfer(payable(this), newModuleAddress, _balance, false) );
        }
        if ( address(this).balance > 0 ) {
            require( newModuleAddress.send(address(this).balance) );
        }
        moduleStatus = status.Disconnected;
    }

    function transferEvent(address payable from, address payable to, uint256
value) virtual external onlyForModuleHandler returns (bool success) {
        return true;
    }
    function newSchellingRoundEvent(uint256 roundID, uint256 reward) virtual external onlyForModuleHandler returns (bool success) {
        return true;
    }

    function registerModuleHandler(address payable _moduleHandlerAddress) internal {
        /*
            Module constructor function for registering ModuleHandler address.
        */
        moduleHandlerAddress = _moduleHandlerAddress;
    }
    function isModuleHandler(address payable addr) internal returns (bool ret) {
        /*
            Test for ModuleHandler address.
            If the module is not connected then returns always false.

            @addr   Address to check

            @ret    This is the module handler address or not
        */
        if ( moduleHandlerAddress == address(0x00) ) { return true; }
        if ( moduleStatus != status.Connected ) { return false; }
        return addr == moduleHandlerAddress;
    }
    function isActive() public view returns (bool success, bool active) {
        /*
            Check self for ready for functions or not.

            @success    Function call was successful or not
            @active     Ready for functions or not
        */
        return (true, moduleStatus == status.Connected && block.number >= disabledUntil);
    }
    modifier onlyForModuleHandler() {
        require( msg.sender == moduleHandlerAddress );
        _;
    }
    receive() external payable {
    }
}
