// Contract: TruchetBlocks
// Target function: function tokenURI(uint _truchet_id) public view override returns (string memory)
(from, to, tokenId);
		require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
	}
	function _exists(uint256 tokenId) internal view virtual returns (bool) {
		return _owners[tokenId] != address(0);
	}
	function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
		require(_exists(tokenId), "ERC721: operator query for nonexistent token");
		address owner = ERC721.ownerOf(tokenId);
		return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
	}
	function _safeMint(address to, uint256 tokenId) internal virtual {
		_safeMint(to, tokenId, "");
	}
	function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
		_mint(to, tokenId);
		require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
	}
	function _mint(address to, uint256 tokenId) internal virtual {
		require(to != address(0), "ERC721: mint to the zero address");
		require(!_exists(tokenId), "ERC721: token already minted");
		_beforeTokenTransfer(address(0), to, tokenId);
		_balances[to] += 1;
		_owners[tokenId] = to;
		emit Transfer(address(0), to, tokenId);
	}
	function _burn(uint256 tokenId) internal virtual {
		address owner = ERC721.ownerOf(tokenId);
		_beforeTokenTransfer(owner, address(0), tokenId);
		_approve(address(0), tokenId);
		_balances[owner] -= 1;
		delete _owners[tokenId];
		emit Transfer(owner, address(0), tokenId);
	}
	function _transfer(address from, address to, uint256 tokenId) internal virtual {
		require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
		require(to != address(0), "ERC721: transfer to the zero address");
		_beforeTokenTransfer(from, to, tokenId);
		_approve(address(0), tokenId);
		_balances[from] -= 1;
		_balances[to] += 1;
		_owners[tokenId] = to;
		emit Transfer(from, to, tokenId);
	}
	function _approve(address to, uint256 tokenId) internal virtual {
		_tokenApprovals[tokenId] = to;
		emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
	}
	function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool)
	{
		if (to.isContract()) {
			try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
				return retval == IERC721Receiver(to).onERC721Received.selector;
			} catch (bytes memory reason) {
				if (reason.length == 0) {
					revert("ERC721: transfer to non ERC721Receiver implementer");
				} else {
					// solhint-disable-next-line no-inline-assembly
					assembly {
						revert(add(32, reason), mload(reason))
					}
				}
			}
		} else {
			return true;
		}
	}
	function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}

abstract contract Ownable is Context {
	address private _owner;
	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
	constructor () {
		address msgSender = _msgSender();
		_owner = msgSender;
		emit OwnershipTransferred(address(0), msgSender);
	}
	function owner() public view virtual returns (address) {
		return _owner;
	}
	modifier onlyOwner() {
		require(owner() == _msgSender(), "Ownable: caller is not the owner");
		_;
	}
	function renounceOwnership() public virtual onlyOwner {
		emit OwnershipTransferred(_owner, address(0));
		_owner = address(0);
	}
	function transferOwnership(address newOwner) public virtual onlyOwner {
		require(newOwner != address(0), "Ownable: new owner is the zero address");
		emit OwnershipTransferred(_owner, newOwner);
		_owner = newOwner;
	}
}

abstract contract ReentrancyGuard {
	uint256 private constant _NOT_ENTERED = 1;
	uint256 private constant _ENTERED = 2;
	uint256 private _status;
	constructor() {
		_status = _NOT_ENTERED;
	}
	modifier nonReentrant() {
		require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
		_status = _ENTERED;
		_;
		_status = _NOT_ENTERED;
	}
}

library Strings {
	bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
	function toString(uint256 value) internal pure returns (string memory) {
		if (value == 0) return "0";
		uint256 temp = value;
		uint256 digits;
		while (temp != 0) {
			digits++;
			temp /= 10;
		}
		bytes memory buffer = new bytes(digits);
		while (value != 0) {
			digits -= 1;
			buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
			value /= 10;
		}
		return string(buffer);
	}
	function toAsciiString(address x) internal pure returns (string memory) {
		bytes memory s = new bytes(40);
		for (uint i = 0; i < 20; i++) {
			bytes1 b = bytes1(uint8(uint(uint160(x)) / (2**(8*(19 - i)))));
			bytes1 hi = bytes1(uint8(b) / 16);
			bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
			s[2*i] = char(hi);
			s[2*i+1] = char(lo);
		}
		return string(s);
	}
	function char(bytes1 b) internal pure returns (bytes1 c) {
		if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
		else return bytes1(uint8(b) + 0x57);
	}
}

library Base64 {
	bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	function encode(bytes memory data) internal pure returns (string memory) {
		uint256 len = data.length;
		if (len == 0) return "";
		uint256 encodedLen = 4 * ((len + 2) / 3);
		bytes memory result = new bytes(encodedLen + 32);
		bytes memory table = TABLE;
		assembly {
			let tablePtr := add(table, 1)
			let resultPtr := add(result, 32)

			for {
				let i := 0
			} lt(i, len) {
			} {
				i := add(i, 3)
				let input := and(mload(add(data, i)), 0xffffff)
				let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
				out := shl(8, out)
				out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
				out := shl(8, out)
				out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
				out := shl(8, out)
				out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
				out := shl(224, out)
				mstore(resultPtr, out)
				resultPtr := add(resultPtr, 4)
			}
			switch mod(len, 3)
			case 1 {
				mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
			}
			case 2 {
				mstore(sub(resultPtr, 1), shl(248, 0x3d))
			}
			mstore(result, encodedLen)
		}
		return string(result);
	}
}

library DynamicBuffer {
	function allocate(uint256 capacity) internal pure returns (bytes memory buffer) {
		assembly {
			let container := mload(0x40)
			{
				let size := add(capacity, 0x60)
				let newNextFree := add(container, size)
				mstore(0x40, newNextFree)
			}
			{
				let length := add(capacity, 0x40)
				mstore(container, length)
			}
			buffer := add(container, 0x20)
			mstore(buffer, 0)
		}
		return buffer;
	}
	function appendUnchecked(bytes memory buffer, bytes memory data) internal pure {
		assembly {
			let length := mload(data)
			for {
				data := add(data, 0x20)
				let dataEnd := add(data, length)
				let copyTo := add(buffer, add(mload(buffer), 0x20))
			} lt(data, dataEnd) {
				data := add(data, 0x20)
				copyTo := add(copyTo, 0x20)
			} {
				mstore(copyTo, mload(data))
			}
			mstore(buffer, add(mload(buffer), length))
		}
	}
	function appendSafe(bytes memory buffer, bytes memory data) internal pure {
		uint256 capacity;
		uint256 length;
		assembly {
			capacity := sub(mload(sub(buffer, 0x20)), 0x40)
			length := mload(buffer)
		}
		require(length + data.length <= capacity, "DynamicBuffer: Appending out of bounds.");
		appendUnchecked(buffer, data);
	}
}

library Address {
	function isContract(address account) internal view returns (bool) {
		uint256 size;
		// solhint-disable-next-line no-inline-assembly
		assembly { size := extcodesize(account) }
		return size > 0;
	}
	function sendValue(address payable recipient, uint256 amount) internal {
		require(address(this).balance >= amount, "Address: insufficient balance");
		// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
		(bool success, ) = recipient.call{ value: amount }("");
		require(success, "Address: unable to send value, recipient may have reverted");
	}
	function functionCall(address target, bytes memory data) internal returns (bytes memory) {
	  return functionCall(target, data, "Address: low-level call failed");
	}
	function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
		return functionCallWithValue(target, data, 0, errorMessage);
	}
	function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
		return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
	}
	function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
		require(address(this).balance >= value, "Address: insufficient balance for call");
		require(isContract(target), "Address: call to non-contract");
		// solhint-disable-next-line avoid-low-level-calls
		(bool success, bytes memory returndata) = target.call{ value: value }(data);
		return _verifyCallResult(success, returndata, errorMessage);
	}
	function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
		return functionStaticCall(target, data, "Address: low-level static call failed");
	}
	function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
		require(isContract(target), "Address: static call to non-contract");
		// solhint-disable-next-line avoid-low-level-calls
		(bool success, bytes memory returndata) = target.staticcall(data);
		return _verifyCallResult(success, returndata, errorMessage);
	}
	function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
		return functionDelegateCall(target, data, "Address: low-level delegate call failed");
	}
	function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
		require(isContract(target), "Address: delegate call to non-contract");
		// solhint-disable-next-line avoid-low-level-calls
		(bool success, bytes memory returndata) = target.delegatecall(data);
		return _verifyCallResult(success, returndata, errorMessage);
	}
	function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
		if (success) {
			return returndata;
		} else {
			if (returndata.length > 0) {
				// solhint-disable-next-line no-inline-assembly
				assembly {
					let returndata_size := mload(returndata)
					revert(add(32, returndata), returndata_size)
				}
			} else {
				revert(errorMessage);
			}
		}
	}
}

contract TruchetBlocks is ERC721, ReentrancyGuard, Ownable
{
	using Strings for int;
	using Strings for uint;
	using Strings for bytes32;
	using DynamicBuffer for bytes;

	uint num_tiles = 16;
	int half = 32;
	int full = 64;

	bool public sale_active = false;
	uint public sale_price = 0.00512 ether;
	uint public max_supply = 512;
	uint public truchets_minted = 0;

	string[] patterns;
	string[] svg_path_strings;

	mapping(uint => string) public truchet_seeds;


	/**
	 * @dev Set up all the variables for the Traits in the collection.
	 */
	constructor() ERC721("TruchetBlocks", "TRUBL")
	{
		patterns = ["Lines", "Waves", "Leaves", "Circles", "Triangles", "Y-Fronts", "Divides", "Frowns"];
		svg_path_strings = [
			'M24 0h16L0 40V24m24 40h16l24-24V24L24 64',
			'M24 64h16a24 24 0 0 1 24-24V24a40 40 0 0 0-40 40m0-64A24 24 0 0 1 0 24v16A40 40 0 0 0 40 0H24',
			'M0 0a64 64 0 0 1 64 64A64 64 0 0 1 0 0',
			'M0 0a64 64 0 0 1 64 64H0',
			'm0 0 64 64H0',
			'M24 0a8 8 0 0 0 16 0M0 24v16a24 24 0 0 1 24 24h16a24 24 0 0 1 24-24V24H0',
			'M24 0a8 8 0 0 0 16 0M24 64a8 8 0 0 1 16 0M0 24h64v16H0V24',
			'M24 0a8 8 0 0 0 16 0M0 24a8 8 0 0 1 0 16m24 24h16a24 24 0 0 1 24-24V24a40 40 0 0 0-40 40'
		];
	}


	/**
	 * @dev Calculate the total amount minted so far.
	 */
	function totalSupply() public view returns (uint)
	{
		return truchets_minted;
	}


	/**
	 * @dev Mint a Truchet and add the Seed to the mapping.
	 */
	function mint() public payable nonReentrant
	{
		require(totalSupply() < max_supply, "All the Truchet Blocks have already been minted.");
		require(sale_active, "Truchet Blocks are not on sale at the moment.");
		require(msg.value >= sale_price, "Insufficient funds to mint a Truchet Block.");

		uint new_truchet_id = totalSupply() + 1;

		string memory seed = generateSeed(msg.sender);

		_safeMint(msg.sender, new_truchet_id);

		truchet_seeds[new_truchet_id] = seed;
		truchets_minted += 1;
	}


	/**
	 * @dev Generate the random seed for the `_truchet_id`.
	 *
	 * @return uint256
	 */
	function generateSeed(address _sender) internal view returns (string memory)
	{
		uint balance = this.balanceOf(_sender);
		address address_string;

		if (balance > 0)
		{
			address_string = address(uint160(uint(keccak256(abi.encodePacked(_sender, balance.toString())))));
		}
		else
		{
			address_string = _sender;
		}

		return Strings.toAsciiString(address_string);
	}


	/**
	 * @dev Get the seed for the `_truchet_id`.
	 *
	 * @return uint256
	 */
	function getSeed(uint _truchet_id) public view returns (string memory)
	{
		return truchet_seeds[_truchet_id];
	}