contract ExpandedToken is ERC721, Owned
{
	event SetName(string _name, string _symbol);

	uint256 public totalSupply;
	uint256 public idCurToken = 0;
	
	string public name = "";
	string public symbol = "";
	
	function setName(string memory _name, string memory _symbol) public onlyOwner 
	{
		name = _name;
		symbol = _symbol;
		emit SetName(name,symbol);
	}

	
	/*
	packed 256 bits :
	[ idType ][ idLocalToken ][ time ]
	[   96   ][     128      ][  32  ]	
	*/
	
	mapping(uint256 => uint256) tokensG2L;						// globalId => packed
	mapping(uint256 => mapping (uint256 => uint256)) tokensL2G; // typeId => localTokenId => globalId
		
	function getGlobalId(uint typeId, uint localTokenId) public view returns ( uint tokenId )		
	{
		tokenId = tokensL2G[typeId][localTokenId];
	}
	
	mapping (uint256 => address) private tokenIndexToOwner;
	mapping (address => uint256) public  ownershipTokenCount; 
	mapping (uint256 => address) private tokenIndexToApproved;
	
	function implementsERC721() public pure returns (bool)
	{
		return true;
	}

	function balanceOf(address _owner) public view returns (uint256 count) 
	{
		return ownershipTokenCount[_owner];
	}
	
	function ownerOf(uint256 _tokenId) public view returns (address owner)
	{
		if(_tokenId > totalSupply || _tokenId==0) owner = address(0);
		else {
			owner = tokenIndexToOwner[_tokenId];
			if (owner == address(0)) owner = address(this);
		}
	}
	
	function _approve(uint256 _tokenId, address _approved) internal 
	{
		tokenIndexToApproved[_tokenId] = _approved;
	}
	
	function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool)
	{
		return tokenIndexToApproved[_tokenId] == _claimant;
	}
	
	function approve( address _to, uint256 _tokenId ) public
	{
		require(_owns(msg.sender, _tokenId));
		_approve(_tokenId, _to);
		emit Approval(msg.sender, _to, _tokenId);
	}
	
	function transferFrom( address _from, address _to, uint256 _tokenId ) public
	{
		require(_to != address(0));
		require(_to != address(this));
		require(_approvedFor(msg.sender, _tokenId));
		require(_owns(_from, _tokenId));
		_transfer(_from, _to, _tokenId);
	}
	
	function _owns(address _claimant, uint256 _tokenId) internal view returns (bool)
	{	
		if (_tokenId > totalSupply || _tokenId==0) return false;
		if (tokenIndexToOwner[_tokenId] == _claimant) return true;
		if (tokenIndexToOwner[_tokenId] == address(0) && _claimant == address(this) ) return true;
		return false;
	}

	function _transfer(address _from, address _to, uint256 _tokenId) internal 
	{
		require( ownershipTokenCount[_from] > 0 );
		
		ownershipTokenCount[_to]++;
		ownershipTokenCount[_from]--;
		
		tokenIndexToOwner[_tokenId] = _to;
		
		delete tokenIndexToApproved[_tokenId];
		delete tokenAuction[_tokenId];

		emit Transfer(_from, _to, _tokenId);
	}
	
	function transfer(address _to, uint256 _tokenId) public
	{
		require(_to != address(0));
		require(_to != address(this));
		require(_owns(msg.sender, _tokenId));
		_transfer(msg.sender, _to, _tokenId);
	}

	struct Auction
	{
        address seller;
        uint startingPrice;
        uint endingPrice;
        uint32 startedAt;
		uint32 period;
		uint32 blocks;
    }

	mapping (uint256 => Auction) tokenAuction;
	
	event CreateAuction(uint256 tokenId, uint32 startedAt, uint256 startingPrice, uint256 endingPrice, uint32 period, uint32 blocks);
	event CancelAuction(uint tokenId);
	event CompleteAuction(uint tokenId);
	
	uint256 public feePercent;
	address public constant feeAddress = 0x15b8a6F624C9666f7EE7D35BC093305eBfb8C8C2;
	
	function createAuction( uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint32 period, uint32 blocks ) public
    {
        require(_owns(msg.sender, tokenId));
		require(tokenAuction[tokenId].startedAt==0);
		require(period >= 1 minutes && period <= 30 days);
		require(blocks>=1 && blocks <=10);
		require(startingPrice > 0);
		require(endingPrice > 0);
		
        Auction memory auction = Auction(
            msg.sender,
            startingPrice,
            endingPrice,
            timenow(),
			period,
			blocks
        );
		
		tokenAuction[tokenId] = auction;
		
        emit CreateAuction(tokenId, timenow(), startingPrice, endingPrice, period, blocks);
    }

	function cancelAuction(uint256 tokenId) public
	{
        require(_owns(msg.sender, tokenId));
		require(tokenAuction[tokenId].startedAt!=0);

		delete tokenAuction[tokenId];

        emit CancelAuction(tokenId);
    }

	function getAuction(uint256 tokenId) public view returns
    (
        address seller,
        uint256 startingPrice,
        uint256 endingPrice,
        uint32 startedAt,
		uint32 period,
		uint32 blocks,
		uint curPrice
    ){
        Auction storage auction = tokenAuction[tokenId];
        if(tokenAuction[tokenId].startedAt!=0)
		{
			seller = auction.seller;
			startingPrice = auction.startingPrice;
			endingPrice = auction.endingPrice;
			startedAt = auction.startedAt;
			period = auction.period;
			blocks = auction.blocks;
			curPrice = getCurrentPrice(tokenId);
		}
    }

	function getCurrentPrice(uint256 tokenId) public view returns (uint)
    {
		Auction storage auction = tokenAuction[tokenId];

		if(tokenAuction[tokenId].startedAt==0) return 0;
		if(timenow()>=auction.startedAt + auction.period) return auction.endingPrice;
		
		int changePriceOfStage = (int(auction.endingPrice) - int(auction.startingPrice)) / auction.blocks;
		uint32 periodOfStage = auction.period / auction.blocks;
		uint32 curStage = ( timenow() - auction.startedAt ) / periodOfStage;
		uint price = uint(int(auction.startingPrice) + changePriceOfStage * curStage);
				
		return price;
	}
	
	function bid(uint256 tokenId) public payable
	{
		Auction storage auction = tokenAuction[tokenId];
		
		require(auction.startedAt!=0);
		
		uint256 price = getCurrentPrice(tokenId);
        require(msg.value == price);
		
		address seller = auction.seller;
		require(msg.sender != seller);

		_transfer(seller, msg.sender, tokenId);
		
        uint256 curFee = price * feePercent / 100;
        uint256 sendValue = price - curFee;

        seller.transfer(sendValue);
		feeAddress.transfer(curFee);

		emit CompleteAuction(tokenId);
	}
	
}
function getGlobalId(uint typeId, uint localTokenId) public view returns ( uint tokenId )		
{
	tokenId = tokensL2G[typeId][localTokenId];
}

// typeId => localTokenId => globalId