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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import "./BeachHutMembershipI.sol";

//     __    __  ______  __      __  ______  __  __      
//    /\ "-./  \/\  __ \/\ \    /\ \/\  == \/\ \/\ \     
//    \ \ \-./\ \ \  __ \ \ \___\ \ \ \  __<\ \ \_\ \    
//     \ \_\ \ \_\ \_\ \_\ \_____\ \_\ \_____\ \_____\   
//      \/_/  \/_/\/_/\/_/\/_____/\/_/\/_____/\/_____/   
//     ______  ______  __  __   __                       
//    /\  ___\/\  __ \/\ \/\ "-.\ \                      
//    \ \ \___\ \ \/\ \ \ \ \ \-.  \                     
//     \ \_____\ \_____\ \_\ \_\\"\_\                    
//      \/_____/\/_____/\/_/\/_/ \/_/                    
                                       
contract MalibuCoin is ERC20, ERC20Permit, Ownable, ReentrancyGuard {

    uint256 public constant maxSupply = 1000000 * 10 ** 18; //(* 10 ** 18)
    uint32 public rewardsPerRound = 10;
    uint32 public genesisRewardsPerRound = 10;
    uint256 public epochLength;
    uint32 public genesisTokenId = 1;

    BeachHutMembershipI public membership;

    constructor() ERC20("Malibu Coin", "KMC") ERC20Permit("Malibu Coin") {
        epochLength = 86400; // seconds
        _mint(msg.sender, 100000 * 10 ** 18);
    }

    function claimMalibuCoins() 
        public 
        nonReentrant 
    {
        require(maxSupply > totalSupply(), "All tokens issued");
        _mint(msg.sender, getUnclaimedMalibuCoins(msg.sender));
        membership.setLastRewarded(msg.sender);

        if (membership.getRetroActiveRewards(msg.sender) > 0) {
            membership.resetRetroActiveRewards(msg.sender);
        }
    }

    //=============================================================================
    // Coin Calculation Functions
    //=============================================================================

    function getUnclaimedMalibuCoins(address account) public view returns (uint256 calculatedAmount) {
        uint256 membershipTokenId = 0;
        uint256 membershipTokenCount = 0;
        bool isOwner = false;
        bool isGenesisOwner = false;

        do {
            membershipTokenId++;
            if (membership.balanceOf(account, membershipTokenId) >= 1) {
                if (!isGenesisOwner) {
                    isGenesisOwner = membershipTokenId == genesisTokenId ? true : false;
                }
                isOwner = true;
                membershipTokenCount += membership.balanceOf(account, membershipTokenId);
            }
        } while (membership.exists(membershipTokenId));

        require(isOwner, "You need a membership token to claim");

        uint256 extras;
        uint256 epochsToReward;
        uint256 lastReward = membership.getLastRewarded(account);
        uint256 rewards = rewardsPerRound;

        epochsToReward = (block.timestamp - lastReward) / epochLength;

        if (isGenesisOwner) {
            rewards += genesisRewardsPerRound;
        } 

        if (membership.getRetroActiveRewards(msg.sender) > 0) {
            extras = membership.getRetroActiveRewards(msg.sender);
        }

        calculatedAmount += (((rewards * epochsToReward) * membershipTokenCount) + extras);
        calculatedAmount = mintMaxSupply((calculatedAmount * 10 ** 18));
    }
    
    function mintMaxSupply(uint256 amount) private view returns (uint256) { 
        if (totalSupply() + amount > maxSupply) {
            amount = (maxSupply - totalSupply());
        }

        return amount;
     }

    function remainingSupply() external view returns (uint256) {
        return maxSupply - totalSupply();
    }

    //=============================================================================
    // Admin Functions
    //=============================================================================

    function setRewardsPerRound(uint32 qty) public onlyOwner {
        rewardsPerRound = qty;
    }

    function setGenesisRewardsPerRound(uint32 qty) public onlyOwner {
        genesisRewardsPerRound = qty;
    }

    function setEpochLength(uint256 epoch) public onlyOwner {
        epochLength = epoch;
    }

    function setGenesisTokenId(uint32 id) public onlyOwner {
        genesisTokenId = id;
    }

    function setBeachHutMembership(address _contract) external onlyOwner {
        require(_contract != address(0), "Can not be address 0");
        membership = BeachHutMembershipI(_contract);
    }
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimMalibuCoins","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epochLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genesisRewardsPerRound","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genesisTokenId","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getUnclaimedMalibuCoins","outputs":[{"internalType":"uint256","name":"calculatedAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"membership","outputs":[{"internalType":"contract BeachHutMembershipI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"remainingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsPerRound","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"setBeachHutMembership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"setEpochLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"qty","type":"uint32"}],"name":"setGenesisRewardsPerRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"setGenesisTokenId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"qty","type":"uint32"}],"name":"setRewardsPerRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]