contract GapesClaiming is Ownable {
    using ECDSA for bytes32;

    string constant ERROR_INVALID_DATA = "Invalid data provided";
    string constant ERROR_INVALID_DATA_LENGTH = "Invalid data length";
    string constant ERROR_INVALID_SIGNATURE = "Invalid signature provided";
    string constant ERROR_NOT_HOLDER = "You must own atleast one Gambling Ape to claim!";

    modifier directOnly {
        require(msg.sender == tx.origin, "Only direct transactions are allowed");
        _;
    }

    modifier notLocked {
        require(!_locked, "You may not claim right now!");
        _;
    }

    constructor(address signer_, GAInterface GA_) {
        _signer = signer_; // 0x288367E1E520A02e5E5d70906a7b0c6f5fD9EF90
        _GA = GA_;         // 0x90cA8a3eb2574F937F514749ce619fDCCa187d45
        _b1 = 0x0408CFcde646bbADa944BF4312e6a2EF61ce8e7b;
        _b2 = 0xA61932ae8589664E9aaB312886775a617775E940;
    }

    GAInterface immutable _GA;
    address immutable _b1;
    address immutable _b2;

    bool _locked;
    address _signer;
    mapping(address => uint) public addressToLastMonthClaimed;

    modifier verifyInputData(address to, uint[] calldata months, uint amount, bytes calldata signature) {
        require(months.length > 0, ERROR_INVALID_DATA_LENGTH);
        for(uint i = 1; i < months.length; i++)
            require(months[i - 1] < months[i], ERROR_INVALID_DATA); /* Verify order smallest > largest **no duplicates**, gaps allowed eg:[1,2,4,7,9] */
        
        require(months[0] > addressToLastMonthClaimed[to], ERROR_INVALID_DATA); /* Smallest value will be first, must be larger than the previously saved largest value */
        addressToLastMonthClaimed[to] = months[months.length - 1]; /* Largest value will be last, save it to be used in the above check in the future */

        require(keccak256(abi.encode(to, months, amount)).toEthSignedMessageHash().recover(signature) == _signer, ERROR_INVALID_SIGNATURE); /* Verify the backend signature */

        require(_GA.balanceOf(to) > 0, ERROR_NOT_HOLDER); /* Users must own at least 1 Gambling Ape to be able to claim */
        _;
    }

    // External/Public

    function claim(uint[] calldata months, uint amount, bytes calldata signature) external directOnly notLocked verifyInputData(msg.sender, months, amount, signature) {
        payable(msg.sender).transfer(amount);
    }

    // Owner Only

    function flipLock() external {
        require(msg.sender == owner() || msg.sender == _b1 || msg.sender == _b2);
        _locked = !_locked;
    }

    function changeSigner(address signer_) external onlyOwner {
        _signer = signer_;
    }

    function withdraw(uint amount) external onlyOwner {
        payable(msg.sender).transfer(amount);
    }

    receive() payable external {}
}
function flipLock() external {
    require(msg.sender == owner() || msg.sender == _b1 || msg.sender == _b2);
    _locked = !_locked;
}

// Owner Only