contract HourglassX {
	using SafeMath for uint256;
	using SafeMath for uint;
	using SafeMath for int256;

	modifier onlyOwner {
		require(msg.sender == owner);
		_;
	}

	modifier playerBookEnabled {
		require(address(playerBook) != NULL_ADDRESS, "named referrals not enabled");
		_;
	}

	// Make the thing
	constructor(address h, address p) public {
		// Set up ERC20 values
		name = "PoWH3D Extended";
		symbol = "P3X";
		decimals = 18;
		totalSupply = 0;

		// Add external contracts
		hourglass = Hourglass(h);
		playerBook = TeamJustPlayerBook(p);

		// Set referral requirement to be the same as P3D by default.
		referralRequirement = hourglass.stakingRequirement();

		// Yes I could deploy 2 contracts myself, but I'm lazy. :^)
		refHandler = new HourglassXReferralHandler(hourglass);

		// Internal stuffs
		ignoreTokenFallbackEnable = false;
		owner = msg.sender;
	}
	// HourglassX-specific data
	address owner;
	address newOwner;

	uint256 referralRequirement;
	uint256 internal profitPerShare = 0;
	uint256 public lastTotalBalance = 0;
	uint256 constant internal ROUNDING_MAGNITUDE = 2**64;
	address constant internal NULL_ADDRESS = 0x0000000000000000000000000000000000000000;

	// I would get this from hourglass, but these values are inaccessable to the public.
	uint8 constant internal HOURGLASS_FEE = 10;
	uint8 constant internal HOURGLASS_BONUS = 3;

	// External contracts
	Hourglass internal hourglass;
	HourglassXReferralHandler internal refHandler;
	TeamJustPlayerBook internal playerBook;

	// P3X Specific data
	mapping(address => int256) internal payouts;
	mapping(address => uint256) internal bonuses;
	mapping(address => address) public savedReferral;

	// Futureproofing stuffs
	mapping(address => mapping (address => bool)) internal ignoreTokenFallbackList;
	bool internal ignoreTokenFallbackEnable;

	// Gauntlets
	mapping(address => uint256) internal gauntletBalance;
	mapping(address => uint256) internal gauntletEnd;
	mapping(address => uint8) internal gauntletType; // 1 = Time, 2 = P3D Supply, 3 = External

	// Normal token data
	mapping(address => uint256) internal balances;
	mapping(address => mapping (address => uint256)) internal allowances;
	string public name;
	string public symbol;
	uint8 public decimals;
	uint256 public totalSupply;

	// --Events
	event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
	event Transfer(address indexed from, address indexed to, uint value);
	event Transfer(address indexed from, address indexed to, uint value, bytes indexed data);
	// Q: Why do you have 2 transfer events?
	// A: Because keccak256("Transfer(address,address,uint256)") != keccak256("Transfer(address,address,uint256,bytes)")
	//    and etherscan listens for the former.


	event onTokenPurchase(
		address indexed accountHolder,
		uint256 ethereumSpent,
		uint256 tokensCreated,
		// If P3D is given to the contract, that amount in P3X will be given to the next buyer since we have no idea who gave us the P3D.
		uint256 tokensGiven,
		address indexed referrer,
		uint8 indexed bitFlags // 1 = invalidMasternode, 2 = usedHourglassMasternode, 4 = reinvestment
	);
	event onTokenSell(
		address indexed accountHolder,
		uint256 tokensDestroyed,
		uint256 ethereumEarned
	);
	event onWithdraw(
		address indexed accountHolder,
		uint256 earningsWithdrawn,
		uint256 refBonusWithdrawn,
		bool indexed reinvestment
	);
	event onDonatedDividends(
		address indexed donator,
		uint256 ethereumDonated
	);
	event onGauntletAcquired(
		address indexed strongHands,
		uint256 stakeAmount,
		uint8 indexed gauntletType,
		uint256 end
	);
	event onExternalGauntletAcquired(
		address indexed strongHands,
		uint256 stakeAmount,
		address indexed extGauntlet
	);
	// --Events--

	// --Owner only functions
	function setNewOwner(address o) public onlyOwner {
		newOwner = o;
	}

	function acceptNewOwner() public {
		require(msg.sender == newOwner);
		owner = msg.sender;
	}

	// P3D allows re-branding, makes sense if P3X allows it too.
	function rebrand(string memory n, string memory s) public onlyOwner {
		name = n;
		symbol = s;
	}

	// P3X selling point: _lower staking requirement than P3D!!_
	function setReferralRequirement(uint256 r) public onlyOwner {
		referralRequirement = r;
	}

	// Enables the function defined below.
	function allowIgnoreTokenFallback() public onlyOwner {
		ignoreTokenFallbackEnable = true;
	}
	// --Owner only functions--

	// --Public write functions

	// Ethereum _might_ implement something where every address, including ones controlled by humans, is a smart contract.
	// Obviously transfering P3X to other people with no fee is one of its selling points.
	// A somewhat future-proofing fix is for the sender to specify that their recipiant is human if such a change ever takes place.
	// However, due to the popularity of ERC223, this might not be necessary.
	function ignoreTokenFallback(address to, bool ignore) public {
		require(ignoreTokenFallbackEnable, "This function is disabled");
		ignoreTokenFallbackList[msg.sender][to] = ignore;
	}

	// Transfer tokens to the specified address, call the specified function, and pass the specified data
	function transfer(address payable to, uint value, bytes memory data, string memory func) public returns(bool) {
		actualTransfer(msg.sender, to, value, data, func, true);
		return true;
	}

	// Transfer tokens to the specified address, call tokenFallback, and pass the specified data
	function transfer(address payable to, uint value, bytes memory data) public returns(bool) {
		actualTransfer(msg.sender, to, value, data, "", true);
		return true;
	}

	// Transfer tokens to the specified address, call tokenFallback if applicable
	function transfer(address payable to, uint value) public returns(bool) {
		actualTransfer(msg.sender, to, value, "", "", !ignoreTokenFallbackList[msg.sender][to]);
		return true;
	}

	// Allow someone else to spend your tokens
	function approve(address spender, uint value) public returns(bool) {
		require(updateUsableBalanceOf(msg.sender) >= value, "Insufficient balance to approve");
		allowances[msg.sender][spender] = value;
		emit Approval(msg.sender, spender, value);
		return true;
	}

	// Have that someone else spend your tokens
	function transferFrom(address payable from, address payable to, uint value) public returns(bool success) {
		uint256 allowance = allowances[from][msg.sender];
		require(allowance > 0, "Not approved");
		require(allowance >= value, "Over spending limit");
		allowances[from][msg.sender] = allowance.sub(value);
		actualTransfer(from, to, value, "", "", false);
		return true;
	}

	// The fallback function
	function() payable external{
		// Only accept free ETH from the hourglass and from our child slave.
		if (msg.sender != address(hourglass) && msg.sender != address(refHandler)) {
			// Now, sending ETH increases the balance _before_ the transaction has been fully processed.
			// We don't want to distribute the entire purchase order as dividends.
			if (msg.value > 0) {
				lastTotalBalance += msg.value;
				distributeDividends(0, NULL_ADDRESS);
				lastTotalBalance -= msg.value;
			}
			createTokens(msg.sender, msg.value, NULL_ADDRESS, false);
		}
	}

	// Worried about having weak hands? Put on an optional gauntlet.
	// Prevents you from selling or transfering a specified amount of tokens
	function acquireGauntlet(uint256 amount, uint8 gType, uint256 end) public{
		require(amount <= balances[msg.sender], "Insufficient balance");

		// We need to apply the data first in order to prevent re-entry attacks.
		// ExternalGauntletInterface.gauntletRequirement _is_ a function which can change the state, after all.
		uint256 oldGauntletType = gauntletType[msg.sender];
		uint256 oldGauntletBalance = gauntletBalance[msg.sender];
		uint256 oldGauntletEnd = gauntletEnd[msg.sender];

		gauntletType[msg.sender] = gType;
		gauntletEnd[msg.sender] = end;
		gauntletBalance[msg.sender] = amount;

		if (oldGauntletType == 0) {
			if (gType == 1) {
				require(end >= (block.timestamp + 97200), "Gauntlet time must be >= 4 weeks"); //97200 seconds = 3 weeks and 6 days.
				emit onGauntletAcquired(msg.sender, amount, gType, end);
			} else if (gType == 2) {
				uint256 P3DSupply = hourglass.totalSupply();
				require(end >= (P3DSupply + (P3DSupply / 5)), "Gauntlet must make a profit"); // P3D buyers are down 19% when they buy, so make gauntlet gainz a minimum of 20%.
				emit onGauntletAcquired(msg.sender, amount, gType, end);
			} else if (gType == 3) {
				require(end <= 0x00ffffffffffffffffffffffffffffffffffffffff, "Invalid address");
				require(ExternalGauntletInterface(address(end)).gauntletRequirement(msg.sender, 0, amount), "External gauntlet check failed");
				emit onExternalGauntletAcquired(msg.sender, amount, address(end));
			} else {
				revert("Invalid gauntlet type");
			}
		} else if (oldGauntletType == 3) {
			require(gType == 3, "New gauntlet must be same type");
			require(end == gauntletEnd[msg.sender], "Must be same external gauntlet");
			require(ExternalGauntletInterface(address(end)).gauntletRequirement(msg.sender, oldGauntletBalance, amount), "External gauntlet check failed");
			emit onExternalGauntletAcquired(msg.sender, amount, address(end));
		} else {
			require(gType == oldGauntletType, "New gauntlet must be same type");
			require(end > oldGauntletEnd, "Gauntlet must be an upgrade");
			require(amount >= oldGauntletBalance, "New gauntlet must hold more tokens");
			emit onGauntletAcquired(msg.sender, amount, gType, end);
		}
	}

	function acquireExternalGauntlet(uint256 amount, address extGauntlet) public{
		acquireGauntlet(amount, 3, uint256(extGauntlet));
	}

	// Throw your money at this thing with a referrer specified by their Ethereum address.
	// Returns the amount of tokens created.
	function buy(address referrerAddress) payable public returns(uint256) {
		// Now, sending ETH increases the balance _before_ the transaction has been fully processed.
		// We don't want to distribute the entire purchase order as dividends.
		if (msg.value > 0) {
			lastTotalBalance += msg.value;
			distributeDividends(0, NULL_ADDRESS);
			lastTotalBalance -= msg.value;
		}
		return createTokens(msg.sender, msg.value, referrerAddress, false);
	}

	// I'm only copy/pasting these functions due to the stack limit.
	// Throw your money at this thing with a referrer specified by their team JUST playerbook name.
	// Returns the amount of tokens created.
	function buy(string memory referrerName) payable public playerBookEnabled returns(uint256) {
		address referrerAddress = getAddressFromReferralName(referrerName);
		// As I said before, we don't want to distribute the entire purchase order as dividends.
		if (msg.value > 0) {
			lastTotalBalance += msg.value;
			distributeDividends(0, NULL_ADDRESS);
			lastTotalBalance -= msg.value;
		}
		return createTokens(msg.sender, msg.value, referrerAddress, false);
	}

	// Use all the ETH you earned hodling P3X to buy more P3X.
	// Returns the amount of tokens created.
	function reinvest() public returns(uint256) {
		address accountHolder = msg.sender;
		distributeDividends(0, NULL_ADDRESS); // Just in case P3D-only transactions happened.
		uint256 payout;
		uint256 bonusPayout;
		(payout, bonusPayout) = clearDividends(accountHolder);
		emit onWithdraw(accountHolder, payout, bonusPayout, true);
		return createTokens(accountHolder, payout + bonusPayout, NULL_ADDRESS, true);
	}

	// Use some of the ETH you earned hodling P3X to buy more P3X.
	// You can withdraw the rest or keept it in here allocated for you.
	// Returns the amount of tokens created.
	function reinvestPartial(uint256 ethToReinvest, bool withdrawAfter) public returns(uint256 tokensCreated) {
		address payable accountHolder = msg.sender;
		distributeDividends(0, NULL_ADDRESS); // Just in case P3D-only transactions happened.

		uint256 payout = dividendsOf(accountHolder, false);
		uint256 bonusPayout = bonuses[accountHolder];

		uint256 payoutReinvested = 0;
		uint256 bonusReinvested;

		require((payout + bonusPayout) >= ethToReinvest, "Insufficient balance for reinvestment");
		// We're going to take ETH out of the masternode bonus first, then the outstanding divs.
		if (ethToReinvest > bonusPayout){
			payoutReinvested = ethToReinvest - bonusPayout;
			bonusReinvested = bonusPayout;
			// Take ETH out from outstanding dividends.
			payouts[accountHolder] += int256(payoutReinvested * ROUNDING_MAGNITUDE);
		}else{
			bonusReinvested = ethToReinvest;
		}
		// Take ETH from the masternode bonus.
		bonuses[accountHolder] -= bonusReinvested;

		emit onWithdraw(accountHolder, payoutReinvested, bonusReinvested, true);
		// Do the buy thing!
		tokensCreated = createTokens(accountHolder, ethToReinvest, NULL_ADDRESS, true);

		if (withdrawAfter && dividendsOf(msg.sender, true) > 0) {
			withdrawDividends(msg.sender);
		}
		return tokensCreated;
	}

	// I'm just a man who loves "default variables"
	function reinvestPartial(uint256 ethToReinvest) public returns(uint256) {
		return reinvestPartial(ethToReinvest, true);
	}

	// There's literally no reason to call this function
	function sell(uint256 amount, bool withdrawAfter) public returns(uint256) {
		require(amount > 0, "You have to sell something");
		uint256 sellAmount = destroyTokens(msg.sender, amount);
		if (withdrawAfter && dividendsOf(msg.sender, true) > 0) {
			withdrawDividends(msg.sender);
		}
		return sellAmount;
	}

	// Again with the default variables!
	function sell(uint256 amount) public returns(uint256) {
		require(amount > 0, "You have to sell something");
		return destroyTokens(msg.sender, amount);
	}

	// Transfer the sender's masternode bonuses and their outstanding divs to their wallet.
	function withdraw() public{
		require(dividendsOf(msg.sender, true) > 0, "No dividends to withdraw");
		withdrawDividends(msg.sender);
	}

	// There's definitely no reason to call this function
	function exit() public{
		address payable accountHolder = msg.sender;
		uint256 balance = balances[accountHolder];
		if (balance > 0) {
			destroyTokens(accountHolder, balance);
		}
		if (dividendsOf(accountHolder, true) > 0) {
			withdrawDividends(accountHolder);
		}
	}

	// Since website won't be released on launch, provide something on etherscan which will allow users to easily set masternodes.
	function setReferrer(address ref) public{
		savedReferral[msg.sender] = ref;
	}

	// Same as above except using the team JUST player book
	function setReferrer(string memory refName) public{
		savedReferral[msg.sender] = getAddressFromReferralName(refName);
	}

	// Another P3X selling point: Get P3X-exclusive didvidends _combined with_ P3D dividends!
	function donateDividends() payable public{
		distributeDividends(0, NULL_ADDRESS);
		emit onDonatedDividends(msg.sender, msg.value);
	}

	// --Public write functions--

	// --Public read-only functions

	// Returns the P3D address.
	function baseHourglass() external view returns(address) {
		return address(hourglass);
	}

	// Returns the salve account address (was mostly used for debugging purposes)
	function refHandlerAddress() external view returns(address) {
		return address(refHandler);
	}

	// Get someone's address from their team JUST playerbook name
	function getAddressFromReferralName(string memory refName) public view returns (address){
		return playerBook.getPlayerAddr(playerBook.pIDxName_(stringToBytes32(refName)));
	}

	// Retruns an addresses gauntlet type.
	function gauntletTypeOf(address accountHolder) public view returns(uint stakeAmount, uint gType, uint end) {
		if (isGauntletExpired(accountHolder)) {
			return (0, 0, gauntletEnd[accountHolder]);
		} else {
			return (gauntletBalance[accountHolder], gauntletType[accountHolder], gauntletEnd[accountHolder]);
		}
	}

	// Same as above except for msg.sender
	function myGauntletType() public view returns(uint stakeAmount, uint gType, uint end) {
		return gauntletTypeOf(msg.sender);
	}

	// Returns an addresse's P3X balance minus what they have in their gauntlet.
	function usableBalanceOf(address accountHolder) public view returns(uint balance) {
		if (isGauntletExpired(accountHolder)) {
			return balances[accountHolder];
		} else {
			return balances[accountHolder].sub(gauntletBalance[accountHolder]);
		}
	}

	// Same as above except for msg.sender
	function myUsableBalance() public view returns(uint balance) {
		return usableBalanceOf(msg.sender);
	}

	// I mean, every ERC20 token has this function. I'm sure you know what it does.
	function balanceOf(address accountHolder) external view returns(uint balance) {
		return balances[accountHolder];
	}

	// Same as above except for msg.sender
	function myBalance() public view returns(uint256) {
		return balances[msg.sender];
	}

	// See if the specified sugardaddy allows the spender to spend their tokens
	function allowance(address sugardaddy, address spender) external view returns(uint remaining) {
		return allowances[sugardaddy][spender];
	}

	// Returns all the ETH that this contract has access to
	function totalBalance() public view returns(uint256) {
		return address(this).balance + hourglass.myDividends(true) + refHandler.totalBalance();
	}

	// Returns the ETH the specified address is owed.
	function dividendsOf(address customerAddress, bool includeReferralBonus) public view returns(uint256) {
		uint256 divs = uint256(int256(profitPerShare * balances[customerAddress]) - payouts[customerAddress]) / ROUNDING_MAGNITUDE;
		if (includeReferralBonus) {
			divs += bonuses[customerAddress];
		}
		return divs;
	}

	// Same as above except includes the masternode bonus
	function dividendsOf(address customerAddress) public view returns(uint256) {
		return dividendsOf(customerAddress, true);
	}

	// Alias of dividendsOf(msg.sender)
	function myDividends() public view returns(uint256) {
		return dividendsOf(msg.sender, true);
	}

	// Alias of dividendsOf(msg.sender, includeReferralBonus)
	function myDividends(bool includeReferralBonus) public view returns(uint256) {
		return dividendsOf(msg.sender, includeReferralBonus);
	}

	// Returns the masternode earnings of a specified account
	function refBonusOf(address customerAddress) external view returns(uint256) {
		return bonuses[customerAddress];
	}

	// Same as above xcept with msg.sender
	function myRefBonus() external view returns(uint256) {
		return bonuses[msg.sender];
	}

	// Backwards compatibility with the P3D interface
	function stakingRequirement() external view returns(uint256) {
		return referralRequirement;
	}

	// Backwards compatibility with the P3D interface
	function calculateTokensReceived(uint256 ethereumToSpend) public view returns(uint256) {
		return hourglass.calculateTokensReceived(ethereumToSpend);
	}

	// Backwards compatibility with the P3D interface
	function calculateEthereumReceived(uint256 tokensToSell) public view returns(uint256) {
		return hourglass.calculateEthereumReceived(tokensToSell);
	}
	// --Public read-only functions--

	// Internal functions

	// Returns true if the gauntlet has expired. Otherwise, false.
	function isGauntletExpired(address holder) internal view returns(bool) {
		if (gauntletType[holder] != 0) {
			if (gauntletType[holder] == 1) {
				return (block.timestamp >= gauntletEnd[holder]);
			} else if (gauntletType[holder] == 2) {
				return (hourglass.totalSupply() >= gauntletEnd[holder]);
			} else if (gauntletType[holder] == 3) {
				return ExternalGauntletInterface(gauntletEnd[holder]).gauntletRemovable(holder);
			}
		}
		return false;
	}

	// Same as usableBalanceOf, except the gauntlet is lifted when it's expired.
	function updateUsableBalanceOf(address holder) internal returns(uint256) {
		// isGauntletExpired is a _view_ function, with uses STATICCALL in solidity 0.5.0 or later.
		// Since STATICCALLs can't modifiy the state, re-entry attacks aren't possible here.
		if (isGauntletExpired(holder)) {
			if (gauntletType[holder] == 3){
				emit onExternalGauntletAcquired(holder, 0, NULL_ADDRESS);
			}else{
				emit onGauntletAcquired(holder, 0, 0, 0);
			}
			gauntletType[holder] = 0;
			gauntletBalance[holder] = 0;

			return balances[holder];
		}
		return balances[holder] - gauntletBalance[holder];
	}

	// This is the actual buy function
	function createTokens(address creator, uint256 eth, address referrer, bool reinvestment) internal returns(uint256) {
		// Let's not call the parent hourglass all the time.
		uint256 parentReferralRequirement = hourglass.stakingRequirement();
		// How much ETH will be given to the referrer if there is one.
		uint256 referralBonus = eth / HOURGLASS_FEE / HOURGLASS_BONUS;

		bool usedHourglassMasternode = false;
		bool invalidMasternode = false;
		if (referrer == NULL_ADDRESS) {
			referrer = savedReferral[creator];
		}

		// Solidity has limited amount of local variables, so the memory allocated to this one gets reused for other purposes later.
		//uint256 refHandlerBalance = hourglass.balanceOf(address(refHandler));
		uint256 tmp = hourglass.balanceOf(address(refHandler));

		// Let's once again pretend this actually prevents people from cheating.
		if (creator == referrer) {
			// Tell everyone that no referral purchase was made because cheating (unlike P3D)
			invalidMasternode = true;
		} else if (referrer == NULL_ADDRESS) {
			usedHourglassMasternode = true;
		// Make sure that the referrer has enough funds to _be_ a referrer, and make sure that we have our own P3D masternode to get that extra ETH
		} else if (balances[referrer] >= referralRequirement && (tmp >= parentReferralRequirement || hourglass.balanceOf(address(this)) >= parentReferralRequirement)) {
			// It's a valid P3X masternode, hooray! (do nothing)
		} else if (hourglass.balanceOf(referrer) >= parentReferralRequirement) {
			usedHourglassMasternode = true;
		} else {
			// Tell everyone that no referral purchase was made because not enough balance (again, unlike P3D)
			invalidMasternode = true;
		}

		// Thanks to Crypto McPump for helping me _not_ waste gas here.
		/*
		uint256 createdTokens = hourglass.calculateTokensReceived(eth); // See? Look how much gas I would have wasted.
		totalSupply += createdTokens;
		*/
		uint256 createdTokens = hourglass.totalSupply();

		// KNOWN BUG: If lord Justo increases the staking requirement to something above both of the contract's P3D
		// balance, then all masternodes won't work until there are enough buy orders to make the refHandler's P3D
		// balance above P3D's masternode requirement.

		// if the refHandler hass less P3D than P3D's masternode requirement, then it should buy the tokens.
		if (tmp < parentReferralRequirement) {
			if (reinvestment) {
				// We need to know if the refHandler has enough ETH to do the reinvestment on its own
				//uint256 refHandlerEthBalance = refHandler.totalBalance();
				tmp = refHandler.totalBalance();
				if (tmp < eth) {
					// If it doesn't, then we must transfer it the remaining ETH it needs.
					tmp = eth - tmp; // fundsToGive = eth - refHandlerEthBalance;
					if (address(this).balance < tmp) {
						// If this fails, something went horribly wrong because the client is attempting to reinvest more ethereum than we've got
						hourglass.withdraw();
					}
					address(refHandler).transfer(tmp);
				}
				tmp = hourglass.balanceOf(address(refHandler));

				// Reinvestments are always done using the null referrer
				refHandler.buyTokensFromBalance(NULL_ADDRESS, eth);
			} else {
				// these nested ? statements are only here because I can only have a limited amount of local variables.
				// Forward the ETH we were sent to the refHandler to place the buy order.
				refHandler.buyTokens.value(eth)(invalidMasternode ? NULL_ADDRESS : (usedHourglassMasternode ? referrer : address(this)));
			}
		} else {
			if (reinvestment) {
				// If we don't have enough ETH to do the reinvestment, withdraw.
				if (address(this).balance < eth && hourglass.myDividends(true) > 0) {
					hourglass.withdraw();
				}
				// If we _still_ don't have enough ETH to do the reinvestment, have the refHandler sends us some.
				if (address(this).balance < eth) {
					refHandler.sendETH(address(this), eth - address(this).balance);
				}
			}
			hourglass.buy.value(eth)(invalidMasternode ? NULL_ADDRESS : (usedHourglassMasternode ? referrer : address(refHandler)));
		}

		// Use the delta from before and after the buy order to get the amount of P3D created.
		createdTokens = hourglass.totalSupply() - createdTokens;
		totalSupply += createdTokens;

		// This is here for when someone transfers P3D to the contract directly. We have no way of knowing who it's from, so we'll just give it to the next person who happens to buy.
		uint256 bonusTokens = hourglass.myTokens() + tmp - totalSupply;

		// Here I now re-use that uint256 to create the bit flags.
		tmp = 0;
		if (invalidMasternode)			{ tmp |= 1; }
		if (usedHourglassMasternode)	{ tmp |= 2; }
		if (reinvestment)				{ tmp |= 4; }

		emit onTokenPurchase(creator, eth, createdTokens, bonusTokens, referrer, uint8(tmp));
		createdTokens += bonusTokens;
		// We can finally give the P3X to the buyer!
		balances[creator] += createdTokens;
		totalSupply += bonusTokens;

		//Updates services like etherscan which track token hodlings.
		emit Transfer(address(this), creator, createdTokens, "");
		emit Transfer(address(this), creator, createdTokens);

		// Unfortunatly, SafeMath cannot be used here, otherwise the stack gets too deep
		payouts[creator] += int256(profitPerShare * createdTokens); // You don't deserve the dividends before you owned the tokens.

		if (reinvestment) {
			// No dividend distribution underflows allowed.
			// Ethereum has been given away after a "reinvestment" purchase, so we have to keep track of that.
			lastTotalBalance = lastTotalBalance.sub(eth);
		}
		distributeDividends((usedHourglassMasternode || invalidMasternode) ? 0 : referralBonus, referrer);
		if (referrer != NULL_ADDRESS) {
			// Save the referrer for next time!
			savedReferral[creator] = referrer;
		}
		return createdTokens;
	}

	// This is marked as an internal function because selling could have been the result of transfering P3X to the contract via a transferFrom transaction.
	function destroyTokens(address weakHand, uint256 bags) internal returns(uint256) {
		require(updateUsableBalanceOf(weakHand) >= bags, "Insufficient balance");

		// Give the weak hand the last of their deserved payout.
		// Also updates lastTotalBalance
		distributeDividends(0, NULL_ADDRESS);
		uint256 tokenBalance = hourglass.myTokens();

		// We can't rely on ETH balance delta because we get cut of the sell fee ourselves.
		uint256 ethReceived = hourglass.calculateEthereumReceived(bags);
		lastTotalBalance += ethReceived;
		if (tokenBalance >= bags) {
			hourglass.sell(bags);
		} else {
			// If we don't have enough P3D to sell ourselves, get the slave to sell some, too.
			if (tokenBalance > 0) {
				hourglass.sell(tokenBalance);
			}
			refHandler.sellTokens(bags - tokenBalance);
		}

		// Put the ETH in outstanding dividends, and allow the weak hand access to the divs they've accumilated before they sold.
		int256 updatedPayouts = int256(profitPerShare * bags + (ethReceived * ROUNDING_MAGNITUDE));
		payouts[weakHand] = payouts[weakHand].sub(updatedPayouts);

		// We already checked the balance of the weakHanded person, so SafeMathing here is redundant.
		balances[weakHand] -= bags;
		totalSupply -= bags;

		emit onTokenSell(weakHand, bags, ethReceived);

		// Tell etherscan of this tragity.
		emit Transfer(weakHand, address(this), bags, "");
		emit Transfer(weakHand, address(this), bags);
		return ethReceived;
	}

	// sends ETH to the specified account, using all the ETH P3X has access to.
	function sendETH(address payable to, uint256 amount) internal {
		uint256 childTotalBalance = refHandler.totalBalance();
		uint256 thisBalance = address(this).balance;
		uint256 thisTotalBalance = thisBalance + hourglass.myDividends(true);
		if (childTotalBalance >= amount) {
			// the refHanlder has enough of its own ETH to send, so it should do that.
			refHandler.sendETH(to, amount);
		} else if (thisTotalBalance >= amount) {
			// We have enough ETH of our own to send.
			if (thisBalance < amount) {
				hourglass.withdraw();
			}
			to.transfer(amount);
		} else {
			// Neither we nor the refHandler has enough ETH to send individually, so both contracts have to send ETH.
			refHandler.sendETH(to, childTotalBalance);
			if (hourglass.myDividends(true) > 0) {
				hourglass.withdraw();
			}
			to.transfer(amount - childTotalBalance);
		}
		// keep the dividend tracker in check.
		lastTotalBalance = lastTotalBalance.sub(amount);
	}

	// Take the ETH we've got and distribute it among our token holders.
	function distributeDividends(uint256 bonus, address bonuser) internal{
		// Prevents "HELP I WAS THE LAST PERSON WHO SOLD AND I CAN'T WITHDRAW MY ETH WHAT DO????" (dividing by 0 results in a crash)
		if (totalSupply > 0) {
			uint256 tb = totalBalance();
			uint256 delta = tb - lastTotalBalance;
			if (delta > 0) {
				// We have more ETH than before, so we'll just distribute those dividends among our token holders.
				if (bonus != 0) {
					bonuses[bonuser] += bonus;
				}
				profitPerShare = profitPerShare.add(((delta - bonus) * ROUNDING_MAGNITUDE) / totalSupply);
				lastTotalBalance += delta;
			}
		}
	}

	// Clear out someone's dividends.
	function clearDividends(address accountHolder) internal returns(uint256, uint256) {
		uint256 payout = dividendsOf(accountHolder, false);
		uint256 bonusPayout = bonuses[accountHolder];

		payouts[accountHolder] += int256(payout * ROUNDING_MAGNITUDE);
		bonuses[accountHolder] = 0;

		// External apps can now get reliable masternode statistics
		return (payout, bonusPayout);
	}

	// Withdraw 100% of someone's dividends
	function withdrawDividends(address payable accountHolder) internal {
		distributeDividends(0, NULL_ADDRESS); // Just in case P3D-only transactions happened.
		uint256 payout;
		uint256 bonusPayout;
		(payout, bonusPayout) = clearDividends(accountHolder);
		emit onWithdraw(accountHolder, payout, bonusPayout, false);
		sendETH(accountHolder, payout + bonusPayout);
	}

	// The internal transfer function.
	function actualTransfer (address payable from, address payable to, uint value, bytes memory data, string memory func, bool careAboutHumanity) internal{
		require(updateUsableBalanceOf(from) >= value, "Insufficient balance");
		require(to != address(refHandler), "My slave doesn't get paid"); // I don't know why anyone would do this, but w/e
		require(to != address(hourglass), "P3D has no need for these"); // Prevent l33x h4x0rs from having P3X call arbitrary P3D functions.

		if (to == address(this)) {
			// Treat transfers to this contract as a sell and withdraw order.
			if (value == 0) {
				// Transfers of 0 still have to be emitted... for some reason.
				emit Transfer(from, to, value, data);
				emit Transfer(from, to, value);
			} else {
				destroyTokens(from, value);
			}
			withdrawDividends(from);
		} else {
			distributeDividends(0, NULL_ADDRESS); // Just in case P3D-only transactions happened.
			// I was going to add a value == 0 check here, but if you're sending 0 tokens to someone, you deserve to pay for wasted gas.

			// Throwing an exception undos all changes. Otherwise changing the balance now would be a shitshow
			balances[from] = balances[from].sub(value);
			balances[to] = balances[to].add(value);

			// Sender can have their dividends from when they owned the tokens
			payouts[from] -= int256(profitPerShare * value);
			// Receiver is not allowed to have dividends from before they owned the tokens.
			payouts[to] += int256(profitPerShare * value);

			if (careAboutHumanity && isContract(to)) {
				if (bytes(func).length == 0) {
					ERC223Handler receiver = ERC223Handler(to);
					receiver.tokenFallback(from, value, data);
				} else {
					bool success;
					bytes memory returnData;
					(success, returnData) = to.call.value(0)(abi.encodeWithSignature(func, from, value, data));
					assert(success);
				}
			}
			emit Transfer(from, to, value, data);
			emit Transfer(from, to, value);
		}
	}

	// The playerbook contract accepts a bytes32. We'll be converting for convenience sense.
	function bytesToBytes32(bytes memory data) internal pure returns(bytes32){
		uint256 result = 0;
		uint256 len = data.length;
		uint256 singleByte;
		for (uint256 i = 0; i<len; i+=1){
			singleByte = uint256(uint8(data[i])) << ( (31 - i) * 8);
			require(singleByte != 0, "bytes cannot contain a null byte");
			result |= singleByte;
		}
		return bytes32(result);
	}

	// haha casting types.
	function stringToBytes32(string memory data) internal pure returns(bytes32){
		return bytesToBytes32(bytes(data));
	}

	// If bytecode exists at _addr then the _addr is a contract.
	function isContract(address _addr) internal view returns(bool) {
		uint length;
		assembly {
			//retrieve the size of the code on target address, this needs assembly
			length := extcodesize(_addr)
		}
		return (length>0);
	}

	// Reject possible accidental sendin of higher-tech shitcoins. (with a fancy message)
	function tokenFallback(address from, uint value, bytes memory data) public pure{
		revert("I don't want your shitcoins!");
	}

	// Allow anyone else to take forcefully sent low-tech shitcoins. (I sure as hell don't want them)
	function takeShitcoin(address shitCoin) public{
		// Don't allow people to siphon funds from us
		require(shitCoin != address(hourglass), "P3D isn't a shitcoin");
		ERC20interface s = ERC20interface(shitCoin);
		s.transfer(msg.sender, s.balanceOf(address(this)));
	}
}
function refHandlerAddress() external view returns(address) {
	return address(refHandler);
}

// Returns the salve account address (was mostly used for debugging purposes)