contract ManateezComic is ERC721, Ownable {
    using SafeMath for uint256;

    uint256 public COMIC_PRICE = 0.01 ether;
    uint256 public TEAM_TOKEN_COUNT = 0;
    uint256 public constant TEAM_TOKEN_MAX = 500;
    uint256 public constant MAX_COMICS = 5000;
    uint256 constant public BASE_RATE = 5 ether;
    uint256 constant public INITIAL_ISSUANCE = 100 ether;
    bool public isLive = false;

    // 12/31/2031 @11:59:59 EST
    uint256 constant public END = 1956545999;

    mapping(address => uint256) public rewards;
    mapping(address => uint256) public lastUpdate;
    mapping(address => uint256) public userBalance;

    AlgaeToken public algaetoken;
    IERC721Enumerable public mightymanateez;
    IERC721Enumerable public merchpass;

    function mintComics(uint numberOfTokens) public payable {
        require(isLive, "Can not mint Comics before the sale has started");
        require(totalSupply().add(numberOfTokens) <= MAX_COMICS, "Purchase would exceed max supply of Comics");
        require(COMIC_PRICE.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");


        updateRewardOnMint(msg.sender, numberOfTokens);
        userBalance[msg.sender] = userBalance[msg.sender].add(numberOfTokens);

        for (uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_COMICS) {
                _safeMint(msg.sender, mintIndex);
            }
        }
    }

    // function getReward(address _to) external {
    //     // require(canBurn[msg.sender]);
    //     uint256 reward = rewards[_to];
    //     if (reward > 0) {
    //         rewards[_to] = 0;
    //         _mint(_to, reward);
    //     }
    // }

    

    function getTotalClaimable(address _user) external view returns (uint256) {
        uint256 time = min(block.timestamp, END);
        uint256 pending = userBalance[_user].mul(BASE_RATE.mul((time.sub(lastUpdate[_user])))).div(86400);
        return rewards[_user] + pending;
    }

    function updateReward(address _from, address _to) private {
        uint256 time = min(block.timestamp, END);
        uint256 timerFrom = lastUpdate[_from];

        if (timerFrom > 0)
            rewards[_from] += userBalance[_from].mul(BASE_RATE.mul((time.sub(timerFrom)))).div(86400);
        if (timerFrom != END)
            lastUpdate[_from] = time;
        if (_to != address(0)) {
            uint256 timerTo = lastUpdate[_to];
            if (timerTo > 0)
                rewards[_to] += userBalance[_to].mul(BASE_RATE.mul((time.sub(timerTo)))).div(86400);
            if (timerTo != END)
                lastUpdate[_to] = time;
        }
    }

    function getReward() external {
        uint256 reward = rewards[msg.sender];
        uint256 time = min(block.timestamp, END);
        uint256 pending = userBalance[msg.sender].mul(BASE_RATE.mul((time.sub(lastUpdate[msg.sender])))).div(86400);
        
        if (reward > 0) {
            rewards[msg.sender] = rewards[msg.sender].add(pending);
            algaetoken.sendAlgae(msg.sender, rewards[msg.sender]);
            rewards[msg.sender] = 0;
        }
        
    }

    function transferFrom(address from, address to, uint256 tokenId) public override {
        updateReward(from, to);
        userBalance[from]--;
        userBalance[to]++;
        ERC721.transferFrom(from, to, tokenId);
    }

    function updateRewardOnMint(address _user, uint256 _amount) private {
        uint256 time = min(block.timestamp, END);
        uint256 timerUser = lastUpdate[_user];
        if (timerUser > 0)
            rewards[_user] = rewards[_user].add(userBalance[_user].mul(BASE_RATE.mul((time.sub(timerUser)))).div(86400)
            .add(_amount.mul(INITIAL_ISSUANCE)));
        else
            rewards[_user] = rewards[_user].add(_amount.mul(INITIAL_ISSUANCE));
        lastUpdate[_user] = time;
    }

    function reserveForTeam(uint _amount, address _to) external onlyOwner {
        require(TEAM_TOKEN_COUNT.add(_amount) <= TEAM_TOKEN_MAX, "Can not mint more than 500 tokens for the team");
        TEAM_TOKEN_COUNT = TEAM_TOKEN_COUNT.add(_amount);
        
        updateRewardOnMint(_to, _amount);
        userBalance[msg.sender] = userBalance[msg.sender].add(_amount);

        uint supply = totalSupply();
        uint i;
        for (i = 0; i < _amount; i++) {
            _safeMint(_to, supply + i);
        }
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
 
    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }

    function setAlgaeToken(address _algae) external onlyOwner {
        algaetoken = AlgaeToken(_algae);
    }

    function updateNFTPrice(uint256 _price) external onlyOwner {
        COMIC_PRICE = _price;
    }

    function goLive() external onlyOwner {
        isLive = true;
    }

    function stopLive() external onlyOwner {
        isLive = false;
    }
    
    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        msg.sender.transfer(balance);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public override {
        updateReward(from, to);
        userBalance[from]--;
        userBalance[to]++;
        ERC721.safeTransferFrom(from, to, tokenId, _data);
    }

    function sendAlgaeToManateezOwners() public onlyOwner{
        uint256 count = totalSupply();
        for(uint i = 0; i < count; i++){
            address owner = ownerOf(i);
            rewards[owner] = rewards[owner].add(500 ether);
        }
    }

    function sendAlgaeToMerchPassOwners() public onlyOwner{
        uint256 count = merchpass.totalSupply();

        for(uint i = 0; i < count; i++){
            address owner = merchpass.ownerOf(i);
            rewards[owner] = rewards[owner].add(100 ether);
        }
    }


    constructor(AlgaeToken _algae, IERC721Enumerable _mightyManateez, IERC721Enumerable _merchPass) ERC721("MightyManateez Comic Issue #1", "MMCOMIC") {
        mightymanateez = IERC721Enumerable(_mightyManateez);
        merchpass = IERC721Enumerable(_merchPass);
        algaetoken = AlgaeToken(_algae);
    }
}
/**
 * @title ManateezComic
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function getTotalClaimable(address _user) external view returns (uint256) {
    uint256 time = min(block.timestamp, END);
    uint256 pending = userBalance[_user].mul(BASE_RATE.mul((time.sub(lastUpdate[_user])))).div(86400);
    return rewards[_user] + pending;
}

// function getReward(address _to) external {
//     // require(canBurn[msg.sender]);
//     uint256 reward = rewards[_to];
//     if (reward > 0) {
//         rewards[_to] = 0;
//         _mint(_to, reward);
//     }
// }