// Contract: Votation
// Target function: function _validateVoteParams(uint144 proposal_) internal view
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @notice This interface is used to certify that an address can vote
 */
interface IRoles {
  /**
   * @notice Ask if the address is superAdmin
   * @param superAdmin_ the superAdmin ?
   * @return true if superAdmin, false otherwise
   */
  function isSuperAdmin(address superAdmin_) external view returns (bool);

  /**
   * @notice Returns if the 'admin' given is admin
   * @param user_ The admin
   * @return true if admin, false otherwise
   */
  function isAdmin(address user_) external view returns (bool);

  /**
   * @notice Gets the amount of admins
   * @return The amount of admins
   */
  function getAdminCount() external view returns (uint256);
}

/**
 * @notice Votation module
 */
contract Votation {
  /**
   * @notice The roles contract address
   */
  IRoles rolesContract;

  //! --------------------------------------------------------------------------- CONSTANTS ---------------------------------------------------------------------------

  /**
   * @notice The proposal should have 70% of the admins approval
   */
  uint8 constant SEMI_QUORUM = 0;

  /**
   * @notice The proposal should have 100% of the admins approval
   */
  uint8 constant FULL_QUORUM = 1;

  /**
   * @notice Flag to identify finished proposals
   */
  uint8 constant FINISHED = 1;

  /**
   * @notice Flag to identify if an admin has voted
   */
  uint8 constant VOTED = 1;

  //! --------------------------------------------------------------------------- STRUCTS ---------------------------------------------------------------------------

  /**
  * @notice Proposal structure
  * @param result This proposal result
  * @param quorum If this proposal should need full quorum
  * @param votesInFavor Amount of votes in favor
  * @param votesAgainst Amount of votes against
  * @param to Address where the code is executed
  * @param action Action to execute (in bytes)
  */
  struct Proposal {
    uint72 result;
    uint72 quorum;
    uint144 votesInFavor;
    uint144 votesAgainst;
    address to;
    bytes action;
  }

  /**
   * @notice Counter of proposals
   */
  uint256 public nextId;

  /**
   * @notice Deployer of the contract
   */
  address public deployer;

  /**
   * @notice Mapping to save proposal structures
   */
  mapping(uint256 => Proposal) public proposals;

  /**
   * @notice Mapping saving if an admin has votes 
   */
  mapping(address => mapping(uint144 => uint144)) public hasVoted;

  //! --------------------------------------------------------------------------- EVENTS ---------------------------------------------------------------------------
  /**
   * @notice Event for when a proposal is created
   */
  event ProposalCreated(address indexed authority_, uint256 id_);

  /**
   * @notice Event for when an admin has voted
   */
  event ProposalVoted(address indexed authority_, uint256 id_);

  /**
   * @notice Event for when an admin has cancelled a proposal
   */
  event ProposalCancelled(address indexed authority_, uint256 id_);

  /**
   * @notice Event for when an proposal is executed
   */
  event ProposalExecuted(uint256 id_);

  /**
   * @notice Event for when the contract is changed
   */
  event ContractChanged(address indexed newContract_);

  //! --------------------------------------------------------------------------- MODIFIERS ---------------------------------------------------------------------------

  /**
   * @notice Requires the msg.sender to be admin
   */
  modifier onlyAdmin {
    require(rolesContract.isAdmin(msg.sender), 'V202');
    _;
  }

  /**
   * @notice Builder 
   */
  constructor () {
    deployer = msg.sender;
  }

  //! --------------------------------------------------------------------------- CREATE PROPOSAL ---------------------------------------------------------------------------

  /**
   * @notice Function to create proposals
   * @param action_ The data to execute
   * @param quorum_ The quorum needed
   * @dev only admins can create a proposal
   */
  function createProposal(bytes memory action_, uint8 quorum_, address to_) public onlyAdmin {
    require((quorum_ == 0) || (quorum_ == 1), 'V201');
    rolesContract.getAdminCount() <= 3 ? 
      proposals[nextId] = Proposal(0, 1, 0, 0, to_, action_) : 
      proposals[nextId] = Proposal(0, quorum_, 0, 0, to_, action_);
    emit ProposalCreated(msg.sender, nextId);
    nextId++;
  }


  //! --------------------------------------------------------------------------- VOTING  ---------------------------------------------------------------------------