// Contract: Etherwriter
// Target function: function mint(string memory firstLine, string memory secondLine, string memory thirdLine, string memory fourthLine) public payable
WZArBEcmS4zSPKc+KnjqD5Nr4x5vdrPzLsFN64B9wQwUUV9PrplsPHj2+OlbQ3NsfCHnnWZhaBamFUnBQE8yMV6Aa03IRMYgQCxJlzvzdOASh04QdEeMGDH6FqN53OC02DrDTaPw0zNErLe1XmuNqYtj4+cuDbN2PJvhAW4+390lRekS0e70aK4sCh58rZVKIYW1pmbGlxuoKMQo/emf+abGoR/Rrf8UYdcamLVD1RL9mvfADFUI8EfhJP2X6zuKo6QMS4VLviY84NnKBiL0H1y1rIrCLgseIFRU5Wz51XMVNcjNfe4z1G6eEt8tTQl1VsvAKoGs1RikgZRn5hpg6WQx0eFEcgZCUdDK52jR6nyOWj+VjaUU9s8rplzYiHoeg8qFVLCRmuyggBaVSqjQaOidhn2CCOA6R7RZDkal1AvshxYMAVTfXon5U+6Nl0gkKkuMHG0lwc5YWOLWxZ8lUSxZSbIuja24rTkj89JUhwDc4bxENgahxMSjuSQ4HNESZ8TukmhrXC5JjCz54sQ1ZQSWWXMuWjBu1JglGpSKih5HadQZ7gya4Zq1nMV8isNVzsoTFmJqgmfctXPMsq3GqlpgFxrRVulSc5H8IKWstBlfanlZQZ1U04hpOoqJYY4p6jtz12bPnhcrZZFlB8mS9Q1Sn+2caRnbgrPkwPoFIe1tFb/HaEfHg9PLBZhp6l6EtwKuOrxn9VtTiR0PY8F9gbRho5aXmF+AZiosA40Ul4+DuiZsaW1byTvWbmlF4zSZrD8E9xs+tMPJ2h1sZVPvUTt8/XGtcsLwXTHj2xjWDjbJoqPBDuF6wQFvfpTh/eamMob7ZMm5SX1TdwRqysbzoUY5e8el9AAVA/Ffd5XF8Sk88mJ/qudL0gi00LxcG3a0O2dVMBIgQPD/6aQICYuIiokroSIgIiGjoKKhAzAwgSAsbBxcPHwCQjCEiJiElIycgpKKGkpDS0fPwMjEzMLKxs7B6bgWllZtUtp16NSlW49effoNGJSWkZVzwiNOsp1y2hlnnXNentNZnhlPJpOtyujUxbmxhv0NZrDSVFa63NRRutQ0VLrY9LeFpsB80w1zjYHZxr/NNHSUTjfcMNUQmKwPTCQD43sCY3v+Nlp3tXSk7m/DdYEh/xJu3bHSQq11VJXpS9TLmtl1Ulsr5fg1UlVt2ypJtZWS2paR7GBXiGtbOYyyy0S5Ni2qthQudhBc/BRik4jdloCN7LiwtsWEpeyogOdHBCpcbYif7wf5NsCvl/DzS9k+XpuXzWF7uNW62bjvYiusWCeHYx2cau2cPGvjcH0r6xIWqJlthqwJqtYIxfsM4HPnGbYenL4OtKiabUVwtkVg5heEmUygWsg0sgX9Gc6l+MCbhpfX4rIdbI4Ocl4FnVrNokvYEO1NA1J3sJkaQFBFN1PjVYwxl/YPjQJ4qenpnFrXsDa3pkHNrbVVO1YXTYtXeS5qc7uEl3IHj/Pr/G3+A5dLhuK5yLFwWm6Q0pRFbmzPxilsS3aJdEyKS+el69LbkgR7gn7nve0HEHt1Slq4ynKKzewfGH3BqISVMgeLs+vsbSap6DczRXpAL/tZafcDOUh3+6GB0z494+sZC0gm60vnM0budPrkTaIX7dkvvIAdi27/5UzFLQGutGvGrEGCzwXrWhRnopqUmvAwkGIOHxqiLkRCDgtQH9qpXtfB5I3tNuIehdPCjt4M) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC, U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}';

	event ChangedText(address indexed Owner, uint256 indexed tokenId, string firstLine, string secondLine, string thirdLine, string fourthLine);

	modifier onlyAdmin {
		require(_msgSender() == _smartContractOwner, _strNotAuthorized);
		_;
	}

	modifier onlyOwner(uint256 tokenId) {
		require(_msgSender() == ownerOf(tokenId), _strNotAuthorized);
		_;
	}

	constructor() ERC721() {
		_smartContractOwner = msg.sender;
	}

	// Aux internal functions

	function _createTextBlock(uint256 tokenId, uint256 level) private view returns(string memory) {
		string memory text = tokenData[tokenId].texta;
		if(level == 1) {
			text = tokenData[tokenId].textb;
		}
		if(level == 2) {
			text = tokenData[tokenId].textc;
		}
		else if(level == 3) {
			text = tokenData[tokenId].textd;
		}

		return string(
			abi.encodePacked(
				'<text visibility="hidden" x="100" y="', Strings.toString(level), '90" font-family="Xe" font-size="40" fill="', tokenData[tokenId].color, '">',
				text
			)
		);
	}

	function _determineColor(uint256 _tokenId) private pure returns(string memory) {
		string memory _color = '#888888';
		if(_tokenId == 0) {
			// 1, token 0, electric pink, 0.01%, non-transferrable.
			_color = '#F9016A';
		}
		else if(_tokenId > 0 && _tokenId < 11) {
			// 10, 1 to 10, phosphor cerulean, 0.1%.
			_color = '#01B3F9';
		}
		else if(_tokenId > 10 && _tokenId < 111) {
			//100, 11 to 110, phosphor red, 1%.
			_color = '#FF1900';
		}
		else if(_tokenId > 110 && _tokenId < 1111) {
			//1000, 111 to 1110, phosphor green, 10%.
			_color = '#19FF00';
		}
		// 8889, 1111 to 9999, light gray, 88.89%.

		return _color;
	}

	function modifyTokenData(uint256 tokenId, string memory a, string memory b, string memory c, string memory d) private {
		require(Strings.stringLength(a) < 41, _strTextTooLong);
		require(Strings.stringLength(b) < 41, _strTextTooLong);
		require(Strings.stringLength(c) < 41, _strTextTooLong);
		require(Strings.stringLength(d) < 41, _strTextTooLong);

		tokenData[tokenId].texta = a;
		tokenData[tokenId].textb = b;
		tokenData[tokenId].textc = c;
		tokenData[tokenId].textd = d;
	}

	// Main public functions

	/// @dev Use this function to change the token's text.
	///	 Note: Only token's owner can change this.
	/// @param tokenId token ID.
	/// @param firstLine First line of text. Must be 40 characters or less.
	/// @param secondLine Second line of text. Must be 40 characters or less.
	/// @param thirdLine Third line of text. Must be 40 characters or less.
	/// @param fourthLine Fourth line of text. Must be 40 characters or less.
	function changeTokenText(uint256 tokenId, string memory firstLine, string memory secondLine, string memory thirdLine, string memory fourthLine) onlyOwner(tokenId) public {
		require(tokenId < _mintedTokens, _strOutOfBounds);
		modifyTokenData(tokenId, firstLine, secondLine, thirdLine, fourthLine);
		emit ChangedText(_msgSender(), tokenId, firstLine, secondLine, thirdLine, fourthLine);
	}

	/// @dev Changes the website URI.
	///	 Note: Only contract's owner can change this.
	/// @param _newWebsite New URI.
	function changeWebURI(string memory _newWebsite) onlyAdmin public {
		_strWebsite = _newWebsite;
	}

	/// @dev Returns the URI to the contract's JSON.
	///	 Note: can be a URL or a base64-encoded JSON.
	function contractURI() public view returns(string memory) {
		string memory _retValue = string(
			abi.encodePacked(
				'data:application/json;base64,',
				Base64.encode(abi.encodePacked(_strContractJSON))
			)
		);

		return _retValue;
	}

	/// @dev Creates a dynamic base-64 representation of a SMIL SVG containing the token's text.
	/// @param tokenId Token ID.
	function generateAnimation(uint256 tokenId) public view returns(string memory) {
		require(tokenId < _mintedTokens, _strOutOfBounds);
		bytes memory svg = abi.encodePacked(
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1010 460" width="100%" height="100%" preserveAspectRatio="xMinYMin meet" style="background-color:#0A0A0A;"><style xmlns="http://www.w3.org/2000/svg" type="text/css">',
			_font,
			'</style>',
			_createTextBlock(tokenId, 0),
			'<set begin="1" to="visible" attributeType="auto" attributeName="visibility"></set></text>',
			_createTextBlock(tokenId, 1),
			'<set begin="mv2.end" to="visible" attributeType="auto" attributeName="visibility"></set><set begin="mv8.end" to="hidden" attributeType="auto" attributeName="visibility"></set></text>',
			_createTextBlock(tokenId, 2),
			'<set begin="mv4.end" to="visible" attributeType="auto" attributeName="visibility"></set><set begin="mv8.end" to="hidden" attributeType="auto" attributeName="visibility"></set></text>',
			_createTextBlock(tokenId, 3),
			'<set begin="mv6.end" to="visible" attributeType="auto" attributeName="visibility"></set><set begin="mv8.end" to="hidden" attributeType="auto" attributeName="visibility"></set></text><g><rect x="100" y="40" width="30" height="60" fill="', tokenData[tokenId].color, '"><animate id="bl1" begin="1; mv8.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl2" begin="mv1.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl3" begin="mv2.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl4" begin="mv3.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl5" begin="mv4.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl6" begin="mv5.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl7" begin="mv6.end" to="#0A0A0A" dur="0.5s" repeatCount="2" attributeType="auto" attributeName="fill"></animate><animate id="bl8" begin="mv7.end" to="#0A0A0A" dur="0.5s" repeatCount="10" attributeType="auto" attributeName="fill"></animate><set begin="mv2.begin" to="140" fill="freeze" attributeType="auto" attributeName="y"></set><set begin="mv4.begin" to="240" fill="freeze" attributeType="auto" attributeName="y"></set><set begin="mv6.begin" to="340" fill="freeze" attributeType="auto" attributeName="y"></set><set begin="mv8.begin" to="40" fill="freeze" attributeType="auto" attributeName="y"></set></rect><rect x="125" y="40" width="800" height="60" fill="#0A0A0A"><set begin="mv2.begin" to="140" fill="freeze" attributeType="auto" attributeName="y"></set><set begin="mv4.begin" to="240" fill="freeze" attributeType="auto" attributeName="y"></set><set begin="mv6.begin" to="340" fill="freeze" attributeType="auto" attributeName="y"></set><set begin="mv8.begin" to="40" fill="freeze" attributeType="auto" attributeName="y"></set></rect><animateTransform id="mv1" begin="bl1.end" attributeName="transform" type="translate" from="0,0" to="20,0" dur="0.05s" accumulate="sum" calcMode="discrete" repeatCount="',
			Strings.toString(Strings.stringLength(tokenData[tokenId].texta)),
			'" fill="freeze"></animateTransform><animateTransform id="mv2" begin="bl2.end" attributeName="transform" type="translate" from="456,0" to="0,0" dur="0.05s" fill="freeze"></animateTransform><animateTransform id="mv3" begin="bl3.end" attributeName="transform" type="translate" from="0,0" to="20,0" dur="0.05s" accumulate="sum" calcMode="discrete" repeatCount="',
			Strings.toString(Strings.stringLength(tokenData[tokenId].textb)),
			'" fill="freeze"></animateTransform><animateTransform id="mv4" begin="bl4.end" attributeName="transform" type="translate" from="456,0" to="0,0" dur="0.05s" fill="freeze"></animateTransform><animateTransform id="mv5" begin="bl5.end" attributeName="transform" type="translate" from="0,0" to="20,0" dur="0.05s" accumulate="sum" calcMode="discrete" repeatCount="',
			Strings.toString(Strings.stringLength(tokenData[tokenId].textc)),
			'" fill="freeze"></animateTransform><animateTransform id="mv6" begin="bl6.end" attributeName="transform" type="translate" from="456,0" to="0,0" dur="0.05s" fill="freeze"></animateTransform><animateTransform id="mv7" begin="bl7.end" attributeName="transform" type="translate" from="0,0" to="20,0" dur="0.05s" accumulate="sum" calcMode="discrete" repeatCount="',
			Strings.toString(Strings.stringLength(tokenData[tokenId].textd)),
			'" fill="freeze"></animateTransform><animateTransform id="mv8" begin="bl8.end" attributeName="transform" type="translate" from="456,0" to="0,0" dur="0.05s" fill="freeze"></animateTransform></g></svg>'
		);

		return string(
			abi.encodePacked(
				'data:image/svg+xml;base64,',
				Base64.encode(svg)
			)
		);
	}