contract DarkMatterCollectibleFarm is TokenWrapper, Ownable {
	ERC1155Tradable public dmc;
  IDarkMatterOriginsStory public story;

	mapping(address => uint256) public lastUpdateTime;
	mapping(address => uint256) public points;
	mapping(uint256 => uint256) public cards;

	event CardAdded(uint256 card, uint256 points);
	event Staked(address indexed user, uint256 amount);
	event Withdrawn(address indexed user, uint256 amount);
	event Redeemed(address indexed user, uint256 amount);

	modifier updateReward(address account) {
		if (account != address(0)) {
			points[account] = earned(account);
			lastUpdateTime[account] = block.timestamp;
		}
		_;
	}

	constructor(ERC1155Tradable _dmcAddress, IERC20 _erc20Address) public TokenWrapper(_erc20Address) {
		dmc = _dmcAddress;
	}

  function setStory(IDarkMatterOriginsStory _story) public onlyOwner {
    story = _story;
  }

	function addCard(uint256 cardId, uint256 amount) public onlyOwner {
		cards[cardId] = amount;
		emit CardAdded(cardId, amount);
	}

	function earned(address account) public view returns (uint256) {
		uint256 blockTime = block.timestamp;
		return
			points[account].add(
				blockTime.sub(lastUpdateTime[account]).mul(1e18).div(86400).mul(
					(balanceOf(account)).div(1e18)
				)
			);
	}

	// stake visibility is public as overriding TokenWrapper's stake() function
	function stake(uint256 amount) override public updateReward(msg.sender) {
		super.stake(amount);
		emit Staked(msg.sender, amount);
	}

	function withdraw(uint256 amount) override public updateReward(msg.sender) {
		require(amount > 0, "Cannot withdraw 0");

		super.withdraw(amount);
		emit Withdrawn(msg.sender, amount);
	}

	function exit() external {
		withdraw(balanceOf(msg.sender));
	}

	function redeem(uint256 card) public updateReward(msg.sender) {
		require(cards[card] != 0, "Card not found");
		require(points[msg.sender] >= cards[card], "Not enough points to redeem for card");
		require(dmc.totalSupply(card) < dmc.maxSupply(card), "Max cards minted");
        if (card < 8) {
          story.unlockCard(msg.sender, card);
        }

		points[msg.sender] = points[msg.sender].sub(cards[card]);
		dmc.mint(msg.sender, card, 1, "");
		emit Redeemed(msg.sender, cards[card]);
	}
}
function stake(uint256 amount) override public updateReward(msg.sender) {
	super.stake(amount);
	emit Staked(msg.sender, amount);
}

// stake visibility is public as overriding TokenWrapper's stake() function