// File: Context.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
	function _msgSender() internal view virtual returns (address) {
		return msg.sender;
	}

	function _msgData() internal view virtual returns (bytes calldata) {
		return msg.data;
	}
}

// File: IForest.sol



// SPDX-License-Identifier: MIT LICENSE

pragma solidity ^0.8.0;

interface IForest {
	struct Stake {
		uint16 tokenId;
		uint80 value;
		address owner;
	}

	function addManyToForestAndPack(address account, uint16[] calldata tokenIds) external;

	function randomWolfOwner(uint256 seed) external view returns (address);

	function getForestStakeByID(uint256 id) external view returns (Stake memory);

	function getPackByAlphaAndID(uint256 alpha, uint256 tokenId) external view returns (Stake memory);
}

// File: IWoolf.sol



// SPDX-License-Identifier: MIT LICENSE

pragma solidity ^0.8.0;

interface IWoolf {
	// struct to store each token's traits
	struct ApeWolf {
		bool isApe;
		uint8 skin;
		uint8 eyes;
		uint8 mouth;
		uint8 clothing;
		uint8 headwear;
		uint8 alphaIndex;
	}
	
	function tokenURI(uint256 tokenId) external view returns (string memory);

	function balanceOf(address owner) external view returns (uint256);

	function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);

	function getPaidTokens() external view returns (uint256);

	function getTokenTraits(uint256 tokenId) external view returns (ApeWolf memory);

	function totalSupply() external view returns (uint256);
}

// File: Ownable.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
	address private _owner;

	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

	/**
	 * @dev Initializes the contract setting the deployer as the initial owner.
	 */
	constructor() {
		_setOwner(_msgSender());
	}

	/**
	 * @dev Returns the address of the current owner.
	 */
	function owner() public view virtual returns (address) {
		return _owner;
	}

	/**
	 * @dev Throws if called by any account other than the owner.
	 */
	modifier onlyOwner() {
		require(owner() == _msgSender(), "Ownable: caller is not the owner");
		_;
	}

	/**
	 * @dev Leaves the contract without owner. It will not be possible to call
	 * `onlyOwner` functions anymore. Can only be called by the current owner.
	 *
	 * NOTE: Renouncing ownership will leave the contract without an owner,
	 * thereby removing any functionality that is only available to the owner.
	 */
	function renounceOwnership() public virtual onlyOwner {
		_setOwner(address(0));
	}

	/**
	 * @dev Transfers ownership of the contract to a new account (`newOwner`).
	 * Can only be called by the current owner.
	 */
	function transferOwnership(address newOwner) public virtual onlyOwner {
		require(newOwner != address(0), "Ownable: new owner is the zero address");
		_setOwner(newOwner);
	}

	function _setOwner(address newOwner) private {
		address oldOwner = _owner;
		_owner = newOwner;
		emit OwnershipTransferred(oldOwner, newOwner);
	}
}

// File: Utils.sol



// SPDX-License-Identifier: MIT LICENSE

pragma solidity ^0.8.0;

import "./IWoolf.sol";
import "./IForest.sol";
import "./Ownable.sol";

contract Utils is Ownable {
	IWoolf public woolf;
	IForest public forest;

	struct WolfGameInstance {
		IWoolf.ApeWolf apeWolf;
		string tokenURI;
	}

	struct Wolfs {
		uint256[] alpha0;
		uint256[] alpha1;
		uint256[] alpha2;
		uint256[] alpha3;
	}

	constructor(address _woolf, address _forest) {
		woolf = IWoolf(_woolf);
		forest = IForest(_forest);
	}

	function getMFList(uint256 startID, uint256 endID) public view returns (string[] memory) {
		string[] memory list = new string[](endID - startID);
		uint16 index = 0;
		for (uint256 i = startID; i < endID; i++) {
			list[index] = woolf.tokenURI(i);
			index++;
		}
		return list;
	}

	function getStatus(uint16 startIndex, uint16 endIndex) public view returns (uint16, uint16) {
		// uint256 count = woolf.totalSupply();
		uint16 wolfCount;
		uint16 apeCount;
		for (uint256 i = startIndex; i <= endIndex; i++) {
			if (woolf.getTokenTraits(i).isApe) {
				apeCount++;
			} else {
				wolfCount++;
			}
		}
		return (apeCount, wolfCount);
	}

	function getApeIDsByOwner(
		address owner,
		uint16 startIndex,
		uint16 endIndex
	) public view returns (uint256[] memory) {
		uint16 apeIndex = 0;
		uint256[] memory apeIDs = new uint256[](endIndex - startIndex);
		for (uint256 i = startIndex; i < endIndex; i++) {
			uint256 tokenId = woolf.tokenOfOwnerByIndex(owner, i);
			IWoolf.ApeWolf memory obj = woolf.getTokenTraits(tokenId);
			if (obj.isApe) {
				// apeIDs.length = 1;
				apeIDs[apeIndex] = tokenId;
				apeIndex++;
			}
		}
		return apeIDs;
	}

	function getWolfIDsByOwnerAndAlpha(
		address owner,
		uint16 startIndex,
		uint16 endIndex,
		uint16 alpha
	) public view returns (uint256[] memory) {
		uint16 wolfIndex = 0;
		uint256[] memory wolfIDs = new uint256[](endIndex - startIndex);
		for (uint256 i = startIndex; i < endIndex; i++) {
			uint256 tokenId = woolf.tokenOfOwnerByIndex(owner, i);
			IWoolf.ApeWolf memory obj = woolf.getTokenTraits(tokenId);
			if (!obj.isApe && alpha == obj.alphaIndex) {
				// apeIDs.length = 1;
				wolfIDs[wolfIndex] = tokenId;
				wolfIndex++;
			}
		}
		return wolfIDs;
	}

	function getStakeApeList(address owner, uint256[] memory IDs) public view returns (string[] memory) {
		string[] memory list = new string[](IDs.length);
		uint16 index = 0;
		for (uint256 i = 0; i < IDs.length; i++) {
			uint256 tokenId = IDs[i];
			if (forest.getForestStakeByID(tokenId).owner == owner) {
				list[index] = woolf.tokenURI(tokenId);
				index++;
			}
		}
		return list;
	}

	function getStakeWolfList(address owner, uint16 alpha ,uint256[] memory IDs) public view returns (string[] memory) {
		string[] memory list = new string[](IDs.length);
		uint16 index = 0;
		for (uint256 i = 0; i < IDs.length; i++) {
			uint256 tokenId = IDs[i];
			if (forest.getPackByAlphaAndID(alpha, tokenId).owner == owner) {
				list[index] = woolf.tokenURI(tokenId);
				index++;
			}
		}
		return list;
	}

	function getUnStakeListByOwner(
		address owner,
		uint16 startIndex,
		uint16 endIndex
	) public view returns (WolfGameInstance[] memory) {
		// uint256 count = woolf.balanceOf(owner);
		WolfGameInstance[] memory list = new WolfGameInstance[](endIndex - startIndex);
		uint16 index = 0;
		for (uint256 i = startIndex; i < endIndex; i++) {
			uint256 tokenId = woolf.tokenOfOwnerByIndex(owner, i);
			if (tokenId != 0) {
				list[index].apeWolf = woolf.getTokenTraits(tokenId);
				list[index].tokenURI = woolf.tokenURI(tokenId);
				index++;
			}
		}
		return list;
	}

	function setForest(address _forest) external onlyOwner {
		forest = IForest(_forest);
	}

	function setWoolf(address _woolf) external onlyOwner {
		woolf = IWoolf(_woolf);
	}
}