// SPDX-License-Identifier: GPL-3.0-or-later

// This file is part of the http://github.com/aaronbloomfield/ccc repository,
// and is released under the GPL 3.0 license.

import "./IERC165.sol";

pragma solidity ^0.8.24;

// This file is heavily adapted from the DAO interface at
// https://github.com/blockchainsllc/DAO/blob/develop/DAO.sol

interface IDAO is IERC165 {

    //------------------------------------------------------------
    // A struct to hold all of our proposal data

    struct Proposal {
        address recipient;      // The address where the `amount` will go to if the proposal is accepted
        uint amount;            // The amount to transfer to `recipient` if the proposal is accepted.
        string description;     // The amount to transfer to `recipient` if the proposal is accepted.
        uint votingDeadline;    // A UNIX timestamp, denoting the end of the voting period
        bool open;              // True if the proposal's votes have yet to be counted, otherwise False
        bool proposalPassed;    // True if the votes have been counted, and the majority said yes
        uint yea;               // Number of Tokens in favor of the proposal; updated upon each yea vote
        uint nay;               // Number of Tokens opposed to the proposal; updated upon each nay vote
        address creator;        // Address of the shareholder who created the proposal
    }

    //------------------------------------------------------------
    // These are all just public variables; some of which are set in the
    // constructor and never changed

    // Obtain a given proposal.   If one lists out the individual fields of
    // the Proposal struct, then one can just have this be a public mapping
    // (otherwise you run into problems with "Proposal memory"
    // versus "Proposal storage"
    //
    // @param i The proposal ID to obtain
    // @return The proposal for that ID
    function proposals(uint i) external view returns (address,uint,string memory,uint,bool,bool,uint,uint,address);

    // The minimum debate period that a generic proposal can have, in seconds;
    // this can be set to any reasonable for testing, but should be set to 10
    // minutes (600 seconds) for final submission.  This can be a constant.
    //
    // @return The minimum debating period in seconds
    function minProposalDebatePeriod() external view returns (uint);

    // NFT token contract address
    //
    // @return The contract address of the NFTManager (ERC-721 contract)
    function tokens() external view returns (address);

    // A string indicating the purpose of this DAO -- be creative!  This can
    // be a constant.
    //
    // @return A string describing the purpose of this DAO
    function purpose() external view returns (string memory);

    // Simple mapping to check if a shareholder has voted for it
    //
    // @param a The address of a member who voted
    // @param pid The proposal ID of a proposal
    // @return Whether the passed member voted yes for the passed proposal
    function votedYes(address a, uint pid) external view returns (bool);

    // Simple mapping to check if a shareholder has voted against it
    //
    // @param a The address of a member who voted
    // @param pid The proposal ID of a proposal
    // @return Whether the passed member voted no for the passed proposal
    function votedNo(address a, uint pid) external view returns (bool);

    // The total number of proposals ever created
    //
    // @return The total number of proposals ever created
    function numberOfProposals() external view returns (uint);

    // A string that states how one joins the DAO -- perhaps contacting the
    // deployer, perhaps some other secret means.  Make this something
    // creative!
    //
    // @return A description of what one has to do to join this DAO
    function howToJoin() external view returns (string memory);

    // This is the amount of ether (in wei) that has been reserved for
    // proposals.  This is increased by the proposal amount when a new
    // proposal is created, thus "reserving" those ether from being spent on
    // another proposal while this one is still being voted upon.  If a
    // proposal succeeds, then the proposal amount is paid out.  In either
    // case, once the voting period for the proposal ends, this amount is
    // reduced by the proposal amount.
    //
    // @return The amount of ether, in wei, reserved for proposals
    function reservedEther() external view returns (uint);

    // Who is the curator (owner / deployer) of this contract?
    //
    // @return The curator (deployer) of this contract
    function curator() external view returns (address);

    //------------------------------------------------------------
    // Functions to implement

    // This allows the function to receive ether without having a payable
    // function -- it doesn't have to have any code in its body, but it does
    // have to be present.
    receive() external payable;

    // `msg.sender` creates a proposal to send `_amount` Wei to `_recipient`
    // with the transaction data `_transactionData`.  This can only be called
    // by a member of the DAO, and should revert otherwise.
    //
    // @param recipient Address of the recipient of the proposed transaction
    // @param amount Amount of wei to be sent with the proposed transaction
    // @param description String describing the proposal
    // @param debatingPeriod Time used for debating a proposal, at least
    //        `minProposalDebatePeriod()` seconds long.  Note that the 
    //        provided parameter can *equal* the `minProposalDebatePeriod()` 
    //        as well.
    // @return The proposal ID
    function newProposal(address recipient, uint amount, string memory description, 
                          uint debatingPeriod) external payable returns (uint);

    // Vote on proposal `_proposalID` with `_supportsProposal`.  This can only
    // be called by a member of the DAO, and should revert otherwise.
    //
    // @param proposalID The proposal ID
    // @param supportsProposal true/false as to whether in support of the
    //        proposal
    function vote(uint proposalID, bool supportsProposal) external;

    // Checks whether proposal `_proposalID` with transaction data
    // `_transactionData` has been voted for or rejected, and transfers the
    // ETH in the case it has been voted for.  This can only be called by a
    // member of the DAO, and should revert otherwise.  It also reverts if
    // the proposal cannot be closed (time is not up, etc.).
    //
    // @param proposalID The proposal ID
    function closeProposal(uint proposalID) external;

    // Returns true if the passed address is a member of this DAO, false
    // otherwise.  This likely has to call the NFTManager, so it's not just a
    // public variable.  For this assignment, this should be callable by both
    // members and non-members.
    //
    // @param who An account address
    // @return A bool as to whether the passed address is a member of this DAO
    function isMember(address who) external view returns (bool);

    // Adds the passed member.  For this assignment, any current member of the
    // DAO can add members. Membership is indicated by an NFT token, so one
    // must be transferred to this member as part of this call.  This can only
    // be called by a member of the DAO, and should revert otherwise.
    // @param who The new member to add
    //
    // @param who An account address to have join the DAO
    function addMember(address who) external;

    // This is how one requests to join the DAO.  Presumably they called
    // howToJoin(), and fulfilled any requirement(s) therein.  In a real
    // application, this would put them into a list for the owner(s) to
    // approve or deny.  For our uses, this will automatically allow the
    // caller (`msg.sender`) to be a member of the DAO.  This functionality
    // is for grading purposes.  This function should revert if the caller is
    // already a member.
    function requestMembership() external;

    // also supportsInterface() from IERC165; it should support two
    // interfaces (IERC165 and IDAO)


    //------------------------------------------------------------
    // Events to emit

    // Called whenever newProposal() successfully completes
    // @param proposalID The proposal ID that was just created
    //
    // @param recipient The recipient address that will receive the ether if
    //        the proposal is successful
    // @param amount The amount of wei that the recipient will receive
    // @param description A short textual description of the proposal
    event NewProposal(uint indexed proposalID, address indexed recipient, uint indexed amount, string description);

    // Called whenever a proposal is voted upon
    //
    // @param proposalID Which proposal was voted upon
    // @param position Whether they were in support of the proposal or not
    // @param voter Who voted
    event Voted(uint indexed proposalID, bool indexed position, address indexed voter);

    // Called whenever a proposal is successfully closed via `closeProposal()`
    //
    // @param proposalID The proposal that was closed
    // @param result Whether the proposal was successful or not
    event ProposalClosed(uint indexed proposalID, bool indexed result);

}