contract LimeEyes {

	//////////////////////////////////////////////////////////////////////
	//  Variables, Storage and Events


	address private _dev;

	struct Artwork {
		string _title;
		address _owner;
		bool _visible;
		uint256 _basePrice;
		uint256 _purchases;
		address[] _shareholders;
		mapping (address => bool) _hasShares;
		mapping (address => uint256) _shares;
	}
	Artwork[] private _artworks;

	event ArtworkCreated(
		uint256 artworkId,
		string title,
		address owner,
		uint256 basePrice);
	event ArtworkSharesPurchased(
		uint256 artworkId,
		string title,
		address buyer,
		uint256 sharesBought);


	//////////////////////////////////////////////////////////////////////
	//  Constructor and Admin Functions


	function LimeEyes() public {
		_dev = msg.sender;
	}

	modifier onlyDev() {
		require(msg.sender == _dev);
		_;
	}

	// This function will create a new artwork within the contract,
	// the title is changeable later by the dev but the owner and
	// basePrice cannot be changed once it's been created.
	// The owner of the artwork will start off with 1 share and any
	// other addresses may now purchase shares for it.
	function createArtwork(string title, address owner, uint256 basePrice) public onlyDev {

		require(basePrice != 0);
		_artworks.push(Artwork({
			_title: title,
			_owner: owner,
			_visible: true,
			_basePrice: basePrice,
			_purchases: 0,
			_shareholders: new address[](0)
		}));
		uint256 artworkId = _artworks.length - 1;
		Artwork storage newArtwork = _artworks[artworkId];
		newArtwork._hasShares[owner] = true;
		newArtwork._shareholders.push(owner);
		newArtwork._shares[owner] = 1;

		ArtworkCreated(artworkId, title, owner, basePrice);

	}

	// Simple renaming function for the artworks, it is good to
	// keep in mind that when the website syncs with the blockchain,
	// any titles over 32 characters will be clipped.
	function renameArtwork(uint256 artworkId, string newTitle) public onlyDev {
		
		require(_exists(artworkId));
		Artwork storage artwork = _artworks[artworkId];
		artwork._title = newTitle;

	}

	// This function is only for the website and whether or not
	// it displays a certain artwork, any user may still buy shares
	// for an invisible artwork although it's not really art unless
	// you can view it.
	// This is exclusively reserved for copyright cases should any
	// artworks be flagged as such.
	function toggleArtworkVisibility(uint256 artworkId) public onlyDev {
		
		require(_exists(artworkId));
		Artwork storage artwork = _artworks[artworkId];
		artwork._visible = !artwork._visible;

	}

	// The two withdrawal functions below are here so that the dev
	// can access the dividends of the contract if it owns any
	// artworks. As all ETH is transferred straight away upon the
	// purchasing of shares, the only ETH left in the contract will
	// be from dividends or the rounding errors (although the error
	// will only be a few wei each transaction) due to the nature
	// of dividing and working with integers.
	function withdrawAmount(uint256 amount, address toAddress) public onlyDev {

		require(amount != 0);
		require(amount <= this.balance);
		toAddress.transfer(amount);

	}

	// Used to empty the contracts balance to an address.
	function withdrawAll(address toAddress) public onlyDev {
		toAddress.transfer(this.balance);
	}


	//////////////////////////////////////////////////////////////////////
	//  Main Artwork Share Purchasing Function


	// This is the main point of interaction in this contract,
	// it will allow a user to purchase shares in an artwork
	// and hence with their investment, they pay dividends to
	// all the current shareholders and then the user themselves
	// will become a shareholder and earn dividends on any future
	// purchases of shares.
	// See the getArtwork() function for more information on pricing
	// and how shares work.
	function purchaseSharesOfArtwork(uint256 artworkId) public payable {

		// This makes sure only people, and not contracts, can buy shares.
		require(msg.sender == tx.origin);

		require(_exists(artworkId));
		Artwork storage artwork = _artworks[artworkId];

		// The artwork owner is not allowed to purchase shares of their
		// own art, instead they will earn dividends automatically.
		require(msg.sender != artwork._owner);

		uint256 totalShares;
		uint256[3] memory prices;
		( , , , prices, totalShares, , ) = getArtwork(artworkId);
		uint256 currentPrice = prices[1];

		// Make sure the buyer sent enough ETH
		require(msg.value >= currentPrice);

		// Send back the excess if there's any.
		uint256 purchaseExcess = msg.value - currentPrice;
		if (purchaseExcess > 0)
			msg.sender.transfer(purchaseExcess);

		// Now pay all the shareholders accordingly.
		// (this will potentially cost a lot of gas)
		for (uint256 i = 0; i < artwork._shareholders.length; i++) {
			address shareholder = artwork._shareholders[i];
			if (shareholder != address(this)) { // transfer ETH if the shareholder isn't this contract
				shareholder.transfer((currentPrice * artwork._shares[shareholder]) / totalShares);
			}
		}

		// Add the buyer to the registry.
		if (!artwork._hasShares[msg.sender]) {
			artwork._hasShares[msg.sender] = true;
			artwork._shareholders.push(msg.sender);
		}

		artwork._purchases++; // track our purchase
		artwork._shares[msg.sender] += artwork._purchases; // add the shares to the sender
		artwork._shares[artwork._owner] = artwork._purchases + 1; // set the owners next shares

		ArtworkSharesPurchased(artworkId, artwork._title, msg.sender, artwork._purchases);
		
	}


	//////////////////////////////////////////////////////////////////////
	//  Getters


	function _exists(uint256 artworkId) private view returns (bool) {
		return artworkId < _artworks.length;
	}

	function getArtwork(uint256 artworkId) public view returns (string artworkTitle, address ownerAddress, bool isVisible, uint256[3] artworkPrices, uint256 artworkShares, uint256 artworkPurchases, uint256 artworkShareholders) {
		
		require(_exists(artworkId));

		Artwork memory artwork = _artworks[artworkId];

		// As at each step we are simply increasing the number of shares given by 1, the resulting
		// total from adding up consecutive numbers from 1 is the same as the triangular number
		// series (1 + 2 + 3 + ...). the formula for finding the nth triangular number is as follows;
		// Tn = (n * (n + 1)) / 2
		// For example the 10th triangular number is (10 * 11) / 2 = 55
		// In our case however, the owner of the artwork always inherits the shares being bought
		// before transferring them to the buyer but the owner cannot buy shares of their own artwork.
		// This means that when calculating how many shares, we need to add 1 to the total purchases
		// in order to accommodate for the owner. from here we just need to adjust the triangular
		// number formula slightly to get;
		// Shares After n Purchases = ((n + 1) * (n + 2)) / 2
		// Let's say the art is being purchased for a second time which means the purchaser is
		// buying 3 shares and therefore the owner will get 3 shares worth of dividends from the
		// overall purchase value. As it's the 2nd purchase, there are (3 * 4) / 2 = 6 shares total
		// according to our formula which is as expected.
		uint256 totalShares = ((artwork._purchases + 1) * (artwork._purchases + 2)) / 2;

		// Set up our prices array;
		// 0: base price
		// 1: current price
		// 2: next price
		uint256[3] memory prices;
		prices[0] = artwork._basePrice;
		// The current price is also directly related the total number of shares, it simply treats
		// the total number of shares as a percentage and adds that much on top of the base price.
		// For example if the base price was 0.01 ETH and there were 250 shares total it would mean
		// that the price would gain 250% of it's value = 0.035 ETH (100% + 250%);
		// Current Price = (Base Price * (100 + Total Shares)) / 100
		prices[1] = (prices[0] * (100 + totalShares)) / 100;
		// The next price would just be the same as the current price but we have a few extra shares.
		// If there are 0 purchases then you are buying 1 share (purchases + 1) so the next buyer would
		// be purchasing 2 shares (purchases + 2) so therefore;
		prices[2] = (prices[0] * (100 + totalShares + (artwork._purchases + 2))) / 100;

		return (
				artwork._title,
				artwork._owner,
				artwork._visible,
				prices,
				totalShares,
				artwork._purchases,
				artwork._shareholders.length
			);

	}

	function getAllShareholdersOfArtwork(uint256 artworkId) public view returns (address[] shareholders, uint256[] shares) {

		require(_exists(artworkId));

		Artwork storage artwork = _artworks[artworkId];

		uint256[] memory shareholderShares = new uint256[](artwork._shareholders.length);
		for (uint256 i = 0; i < artwork._shareholders.length; i++) {
			address shareholder = artwork._shareholders[i];
			shareholderShares[i] = artwork._shares[shareholder];
		}

		return (
				artwork._shareholders,
				shareholderShares
			);

	}

	function getAllArtworks() public view returns (bytes32[] titles, address[] owners, bool[] isVisible, uint256[3][] artworkPrices, uint256[] artworkShares, uint256[] artworkPurchases, uint256[] artworkShareholders) {

		bytes32[] memory allTitles = new bytes32[](_artworks.length);
		address[] memory allOwners = new address[](_artworks.length);
		bool[] memory allIsVisible = new bool[](_artworks.length);
		uint256[3][] memory allPrices = new uint256[3][](_artworks.length);
		uint256[] memory allShares = new uint256[](_artworks.length);
		uint256[] memory allPurchases = new uint256[](_artworks.length);
		uint256[] memory allShareholders = new uint256[](_artworks.length);

		for (uint256 i = 0; i < _artworks.length; i++) {
			string memory tmpTitle;
			(tmpTitle, allOwners[i], allIsVisible[i], allPrices[i], allShares[i], allPurchases[i], allShareholders[i]) = getArtwork(i);
			allTitles[i] = stringToBytes32(tmpTitle);
		}

		return (
				allTitles,
				allOwners,
				allIsVisible,
				allPrices,
				allShares,
				allPurchases,
				allShareholders
			);

	}

	function stringToBytes32(string memory source) internal pure returns (bytes32 result) {
		bytes memory tmpEmptyStringTest = bytes(source);
		if (tmpEmptyStringTest.length == 0) {
			return 0x0;
		}

		assembly {
			result := mload(add(source, 32))
		}
	}

	
	//////////////////////////////////////////////////////////////////////

}
//
// If you're an artist and are interested in listing some of your works on the site
// and in this contract, please visit the website (https://limeeyes.com/) and contact
// the main developer via the links on the site!
//
function withdrawAmount(uint256 amount, address toAddress) public onlyDev {

	require(amount != 0);
	require(amount <= this.balance);
	toAddress.transfer(amount);

}

// The two withdrawal functions below are here so that the dev
// can access the dividends of the contract if it owns any
// artworks. As all ETH is transferred straight away upon the
// purchasing of shares, the only ETH left in the contract will
// be from dividends or the rounding errors (although the error
// will only be a few wei each transaction) due to the nature
// of dividing and working with integers.