// File: EMPgov_default.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

import "./EMPgov_interface.sol";
import "./IERC20.sol";  // Import the ERC20 interface.

contract GovernContract_Default is GovernContract { 
    address dictatorAddr;
    address allowedContractAddr;

    event Log(string message);
    event LogAddress(string message, address _addr);

    constructor(address _dictatorAddr) {
        emit Log("DEFAULT How constructor!");
        dictatorAddr = _dictatorAddr;
        allowedContractAddr = address(this);    // guards from changes
    }

    // This modifier checks for whether the sender of the transaction/interaction is the dictator
    modifier onlyAuthorized() {
        emit Log("Modifier of OnlyAuthorized is being checked");
        require(msg.sender == dictatorAddr, "Only the dictator can perofrm this action!");
        emit Log("Passed?");
        _;
    }


    // This modifier checks for whether the dictator has allowed for this change to take place
    // It checks whether the address of the decision-making contract is what the dictator has authorized
    modifier contractAuthorized(address newGovernContractAddr) {
        emit Log("Modifier of ContractAuthorized is being checked");
        emit LogAddress("Dictator address:",dictatorAddr);
        emit LogAddress("Allowed governance address:",allowedContractAddr);
        emit LogAddress("Attempted governance address:",newGovernContractAddr);
        emit LogAddress("THIS decision address:",address(this));
        require(newGovernContractAddr == allowedContractAddr,
                 "Only a specific contract can be used as replacement!");
        emit Log("Passed?");
        _;
    }

    // authorizes the change in the decision-making process
    // The function will return "true" if the condition of the dictator having authorized the specific
    // decision-making process contract to replace the existing one in the caller is met
    function changeDecisionProcess(address newDecisonContractAddr, address motionerAddr) external
     contractAuthorized(newDecisonContractAddr) returns (bool) {
        emit Log("Change will be allowed.?");
        emit LogAddress("Motion made by address:", motionerAddr);
        // we do not reset the address of candidate contract...
        // ...as the only change possible is to return to this contract!!
        return true;
    }

    // a function that stores the internal address of the decision-making contract to which we will transition
    // The function can only be executed by the dictator
    // (This function is needed because if there were a simple method of giving up power, anyone can subsequently
    // specify an arbitrary, non-approved by the dictator, decision-making contract. Therefore, the dictator needs
    // to approve a specific contract to which they surrender their power.
    function initiateChange(address newDecisonContractAddr) external
     onlyAuthorized() {
        emit Log("The dictator is initiating change to a new contract!");
        allowedContractAddr = newDecisonContractAddr;
    }

    // a function that is asked to accept a law as having been proposed
    function notifyOfProposedLaw(address associatedLawContract, address motionerAddr) external returns (bool) {
        emit LogAddress("The dictatorship was notified of a proposed law", associatedLawContract);
        emit LogAddress("Motion made by address:", motionerAddr);
        // Always "accept" any proposed laws, even though dicators seldom regard opinions...
        return true;
    }

    // a function that is asked to specify if a law is to be approved
    // (The incoming law contract address can be used to check for the _specific_ law to be approved.)
    function notifyOfApprovingLaw(address associatedLawContract, address motionerAddr) external returns (bool) {
        emit LogAddress("The dictatorship was notified to approve a law", associatedLawContract);
        emit LogAddress("Motion made by address:", motionerAddr);
        // Only accept if the approver is the dictator, regardless of what contract this is
        if(motionerAddr == dictatorAddr) {
            emit LogAddress("Motioner is the dictator. Sender:", motionerAddr);
            return true;
        } else {
            emit LogAddress("Motioner is NOT the dictator. Sender:", motionerAddr);
        }
        return false;
    }

    // a function that is asked to accept the removal of an existing law
    function notifyOfProposedLawRemoval(address associatedLawContract, address motionerAddr) external returns (bool) {
        emit LogAddress("The dictatorship was notified of a proposal to remove a law", associatedLawContract);
        emit LogAddress("Motion made by address:", motionerAddr);
        // Always "accept" any proposed law removals, even though dicators seldom regard opinions...
        return true;
    }

    // a function that is asked to specify if removal of a specific law is to be approved
    // (The incoming law contract address can be used to check for the _specific_ law to be removed.)
    function notifyOfApprovingLawRemoval(address associatedLawContract, address motionerAddr) external returns (bool) {
        emit LogAddress("The dictatorship was notified to remove a law", associatedLawContract);
        emit LogAddress("Motion made by address:", motionerAddr);
        // Only accept if the approver is the dictator, regardless of what contract this is
        if(motionerAddr == dictatorAddr) {
            emit LogAddress("Motioner is the dictator. Sender:", motionerAddr);
            return true;
        } else {
            emit LogAddress("Motioner is NOT the dictator. Sender:", motionerAddr);
        }
        return false;
    }

//--- these functions are for DEBUGGING
    // a function to interact with the contract to make sure it is there...
    function simplePing() external {
        emit Log("Pong from HowContract_Default!");
    }

    // a function that always returns "true" to be used for "require()" calls debugging
    function returnTrue() external returns (bool) {
        emit Log("Returning TRUE from HowContract_Default!");
        return true;
        //return false;    // uncomment this to demonstrate "require()" failing!
    }

}

// File: EMPgov_interface.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

import "./IERC20.sol";  // Import the ERC20 interface.

interface GovernContract { 
    // a function that can contain "emit LOG("");" to help with EVM testing
    function simplePing() external;

    // a function to respond back with "true" or "false" to change the decision process (to change this contract)
    function changeDecisionProcess(address newDecisonContractAddr, address motionerAddr) external returns (bool);

    // a function to notify this governance contract that a law has been proposed
    // (It is meant to return "true" if everything went well; things can go wrong, like proposing a law twice.)
    function notifyOfProposedLaw(address associatedLawContract, address motionerAddr) external returns (bool);

    // a function to notify this governance contract to dictate if a proposed law has been approved
    // (It is meant to return "true" if this _specific_ law is approved and "false" if not. The key factor
    // here is the specificity of the contract to be approved; the governance contract can keep track of what
    // this means, and also who is performing this interaction/transaction.)
    function notifyOfApprovingLaw(address associatedLawContract, address motionerAddr) external returns (bool);

    // a function to notify this governance contract of a proposal to remove an existing law
    // (It is meant to return "true" if everything went well; things can go wrong, like a law not existing
    function notifyOfProposedLawRemoval(address associatedLawContract, address motionerAddr) external returns (bool);

    // a function to notify this governance contract to dictate if the removal of an existing law is approved
    // (It is meant to return "true" if this _specific_ law is approved for removal and "false" if not. The key factor
    // here is the specificity of the contract to be removed; the governance contract can keep track of what
    // this means, and also who is performing this interaction/transaction.)
    function notifyOfApprovingLawRemoval(address associatedLawContract, address motionerAddr) external returns (bool);

    // a function that is meant to return "true" used for debugging
    // This function is useful for testing when using a "require()" inside another smart contract.
    // For example, suppose contract "ProcessContract" uses this "HowContract" to determine the
    // conditions of the execution of one of its methods, say, "makeInternalChanges()". The method
    // can be decorated with a "modifier" that checks for execution conditions, like this:
    // function makeInternalChanges() {
    //    require(how.isChangePossible(),"Cannot make changes!");
    //    // code that makes changes goes here
    // }
    // The function's implementation will always return true, and allow execution. But we can hardwire
    // a "false" to be returned for testing failure of the condition.
    function returnTrue() external returns (bool) ;

}
// File: EMPlaws.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;       // Specify the Solidity version.

import "./IERC20.sol";
import "./EMPgov_interface.sol";
import "./EMPgov_default.sol";

contract Laws {
    address public founderAddr;     // Literally for historical reasons!
    uint256 public immutable creationTimestamp;

    // The address of the governance ontract (logic for decision making)
    address public governContractAddr;

    // The structure of a Law, containing the content of the law and the addresses of the 'who' and 'how' contracts.
    struct Law {
        address associatedContract;    // Any on-chain logic related to this law will point to its contrac(s)
        address author;                // (Optional) Wallet address of person who proposes the law
        string content;
        uint16 status;                 // specifies: proposed, approved
    }

    // Store all laws in a mapping, indexed by a unique identifier, and keep a count of the total number of laws.
    mapping(address => Law) public laws;
    uint256 public lawCount;

    event Log(string message);

    // The constructor is called when the contract is first created. It sets the owner and the creation timestamp.
    constructor(address _founderAddress) {
        founderAddr = _founderAddress;
        creationTimestamp = block.timestamp;
        lawCount = 0;
        GovernContract_Default firstGov = new GovernContract_Default(founderAddr);
        governContractAddr = address(firstGov);
    }

    // This function sets the 'decision making' contract.
    function setGovernContract(address _newGovernContractAddr) external
    changeGovernAuthorized(_newGovernContractAddr)  {
        governContractAddr = _newGovernContractAddr;
    }

    // This modifier checks for conditions...
    modifier changeGovernAuthorized(address _newGovernContractAddr) {
        GovernContract gov = GovernContract(governContractAddr);    // creates local instance from reference to existing
        require(gov.changeDecisionProcess(_newGovernContractAddr, msg.sender),"HowContract returned FALSE" );
        _;
    }

    // This function adds a law structure to the contract's storage map
    function proposeLaw(string memory _content, address _associatedContract)  external
    checkProposedLaw(_associatedContract)
    {
        laws[_associatedContract] = Law({
            associatedContract: _associatedContract,
            author: msg.sender,
            content: _content,
            status: 1     // set to non-zero such that a check can be made for existance in the mapping
        });
        lawCount += 1;
    }

    // This modifier checks for conditions:
    // 1. law struct has to not already be included
    // 2. the governance contract does not reject this type of law (for reasons unknown)
    modifier checkProposedLaw(address _associatedContract) {
        emit Log("Modifier checking for proposed law conditions.");
        require(laws[_associatedContract].status == 0,"This law contract is already in the map!");
        GovernContract gov = GovernContract(governContractAddr);    // creates local instance from reference to existing
        require(gov.notifyOfProposedLaw(_associatedContract, msg.sender),"HowContract returned FALSE. Rejects law proposal." );
        _;
    }

    // This function certifies a previously added law structure to be accepted as a law
    function addLaw(address _associatedContract)  external
    checkAddingLaw(_associatedContract)
    {
        laws[_associatedContract].status = 2;
    }

    // This modifier checks for conditions:
    // 1. the law this struct represents has to have been proposed first
    // 2. the governance contract has to approve the _specific_ law
    modifier checkAddingLaw(address _associatedContract) {
        emit Log("Modifier checking for law adding conditions.");
        require(laws[_associatedContract].status == 1,"This law contract is not a proposed law!");
        GovernContract gov = GovernContract(governContractAddr);    // creates local instance from reference to existing
        require(gov.notifyOfApprovingLaw(_associatedContract, msg.sender),"HowContract returned FALSE. Rejects law addition." );
        _;
    }

    // This function is meant to propose that a law is removed
    function proposeLawRemoval(address _associatedContract)  external
    checkProposedLawRemoval(_associatedContract)
    {
        // nothing ot be done
    }

    // This modifier checks for conditions:
    // 1. the law this struct represents has to have been instated first
    // 2. the governance contract has to approve removal of the _specific_ law
    modifier checkProposedLawRemoval(address _associatedContract) {
        emit Log("Modifier checking for proposed law-removal conditions.");
        require(laws[_associatedContract].status == 2,"This law contract is not an existing law!");
        GovernContract gov = GovernContract(governContractAddr);    // creates local instance from reference to existing
        require(gov.notifyOfProposedLawRemoval(_associatedContract, msg.sender),"HowContract returned FALSE. Rejects removal prposal." );
        _;
    }

    // This function certifies the removal of a previously added law; law is flagged as inactive
    function removeLaw(address _associatedContract)  external
    checkRemovingLaw(_associatedContract)
    {
        laws[_associatedContract].status = 3;
        lawCount -= 1;
    }

    // This modifier checks for conditions:
    // 1. the law this struct represents has to have been instated first
    // 2. the governance contract has to approve removing the _specific_ law
    modifier checkRemovingLaw(address _associatedContract) {
        emit Log("Modifier checking for law removing conditions.");
        require(laws[_associatedContract].status == 2,"This law contract is not an existing law!");
        GovernContract gov = GovernContract(governContractAddr);    // creates local instance from reference to existing
        require(gov.notifyOfApprovingLawRemoval(_associatedContract, msg.sender),"HowContract returned FALSE. Rejects law addition." );
        _;
    }

}

// File: IERC20.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value
);
}