// File: contracts/Voting.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

error OnlyTheOwnerCanDoThisHomieAndYouAintTheOne();
error SorryYouCantVoteFriend();
error YayYouAlreadyVotedAndWeHopeYouDecidedToPayTheTeamCauseWeSeeYou();
error WhoopsInvalidVote();
error WeAreNotReadyToVoteYetHoldYourHorses();

contract OnChainMferRefundVoting
{
    // if vote == 1, full refund for each layer token (.24 eth), team keeps none
    // if vote == 2, half refund for each layer token (.12 eth), team keeps half
    // if vote == 3, no refund for each layer token, team keeps all

    // mapping to hold voter data
    /*
        bit layout
            [0-251]     : unused
            [252]       : allowed to vote (1 << 3)
            [253]       : has voted (1 << 2)
            [254-255]   : vote value (last 2 bits to the right)
                possible votes:
                    1 - full refund
                    2 - 50% refund
                    3 - no refund
    */
    mapping(address => uint256) public VoterData;

    // is voting open
    bool public VotingOpen;

    // open/close voting
    function toggleVotingOpen() external onlyOwner
    {
        VotingOpen = !VotingOpen;
    }

    // function to set valid voter
    function updateVoterEligibility(address _address, bool _eligible) external onlyOwner
    {
        if(_eligible)
        {
            // enable voting
            uint256 mask = 1 << 3;
            uint256 currentData = VoterData[_address];
            VoterData[_address] = currentData | mask;
        } else
        {
            // remove eligibility
            uint256 mask = 1 << 3;
            mask = ~mask;
            uint256 currentData = VoterData[_address];
            VoterData[_address] = currentData & mask;
        }
    }

    // function to check if address is eligible to vote
    function isVoterEligible(address _voter) public view returns(bool)
    {
        uint256 mask = 1 << 3;
        return (VoterData[_voter] & mask) != 0;
    }

    // function to check if user has voted
    function hasVoterVoted(address _voter) public view returns(bool)
    {
        uint256 mask = 1 << 2;
        return (VoterData[_voter] & mask) != 0;
    }

    // get address vote history
    function getVotersVote(address _voter) public view returns(uint256)
    {
        uint256 mask = 3;
        return uint256(VoterData[_voter] & mask);
    }

    // cast your vote!
    function castVote(uint256 _daVote) public
    {
        // check if voting open
        if(!VotingOpen) revert WeAreNotReadyToVoteYetHoldYourHorses();
        // check if eligible
        if(!isVoterEligible(msg.sender)) revert SorryYouCantVoteFriend();
        // check if voted
        if(hasVoterVoted(msg.sender)) revert YayYouAlreadyVotedAndWeHopeYouDecidedToPayTheTeamCauseWeSeeYou();
        // check for valid vote
        if(_daVote == 0 || _daVote > 3) revert WhoopsInvalidVote();
        
        // vote already! 👇

        // update vote
        uint256 voteMask = _daVote;
        uint256 updatedData = (VoterData[msg.sender] & ~voteMask) | _daVote;
        // mark 'has voted'
        uint256 hasVotedMask = 1 << 2;
        updatedData = (updatedData | hasVotedMask);
        // update mapping
        VoterData[msg.sender] = updatedData;
    }

    // constructor stuff
    constructor()
    {
        Owner = msg.sender;
    }

    // owner stuff
    address private Owner;
    // modifier
    modifier onlyOwner()
    {
        _checkOwner();
        _;
    }
    // check owner
    function _checkOwner() internal view virtual
    {
        if(msg.sender != Owner) revert OnlyTheOwnerCanDoThisHomieAndYouAintTheOne();
    }
}



/////////////////////////////////////////////////////////////////
//                 spread smiles like confetti                 //
/////////////////////////////////////////////////////////////////