contract GLICPIXXXVER001 is ERC721Enumerable, Ownable, IERC1155Receiver {
    
	string public _baseTokenURI;
	string public amazingCanvasCode;
	
	string public constant glicpixFolderIPFS = "QmYumvDNGyiN2MyPQcNBaU7hnesmwJitvqipyN2H3fF2tk";
	string public constant glicpixImagesBase64IPFS = "QmZz3VUi72zpEinivczPPUWVpaWYeSKoz8oKcAupw9PUUG";
		
	// TO MIGRATE FROM OPENSEA STOREFRONT	
    address public constant openseaStorefrontContract = 0x495f947276749Ce646f68AC8c248420045cb7b5e;

	mapping (uint => uint) public idToOpenseaId;

		// ROTATE YOUR GLICPIX IN CANVAS
	
	enum Rotation {
	DEG0,
	DEG90,
	DEG180,
	DEG270
    }
    
    // FLIP YOUR GLICPIX IN CANVAS
    
    enum Flip {
	NOFLIP,
	FLIPX,
	FLIPY,
	FLIPXY
    }
    
    // LOCATION SWAP OFFERS mapping
    
    mapping (uint8 => mapping(uint8 => SwapOffer) ) public SwapOffers;
    
    // PROGRAMMABLE ART PARAMETERS
    
    mapping (uint8 => Rotation) public RotationList;
    mapping (uint8 => Flip) public FlipList;
    mapping (uint8 => uint8) public idToLocationOnCanvas;
    
    // REQUESTER IS HOLD IN CASE SOMEONE TRANSFERS THEIR GLICPIX AND FORGETS TO DEACTIVATE THEIR SWAP OFFER
    
    struct SwapOffer {
    	address requester;
    	bool isActive;
    }

    struct swapOfferShowcase {
    	address requester; 
    	uint8 id;
    }
    
    struct LayerStatus {
        Flip flip;
        Rotation rotation;
        uint8 location;
    }

    // COOLIO EVENTS
    
    event RotationChange(uint8 _id, Rotation _rotation);
    event FlipChange(uint8 _id, Flip _flip);
    event CanvasOrderChange(uint8 tokenA, uint8 aNewLoc, uint8 tokenB, uint8 bNewLoc);
    
    event SwapOfferMade(uint8 tokenA, uint8 tokenB);
    event SwapOfferCanceled(uint8 tokenA, uint8 tokenB);
    event SwapOfferAccepted(uint8 tokenA, uint8 tokenB);

    constructor() ERC721("GLICPIXXXVER001 - PROGRAMMABLE CANVAS", "GLICPIXXXVER001")  {
        
        for (uint8 i = 1; i < 37; i++) {
            idToLocationOnCanvas[i] = i;
        } 
        
        _safeMint(owner(), 0);
        
        idToOpenseaId[1] =	89502295366922500116793596804463892406027145793870087151951406456662956965889;
        idToOpenseaId[2] =	89502295366922500116793596804463892406027145793870087151951406457762468593665;
        idToOpenseaId[3] =	89502295366922500116793596804463892406027145793870087151951406458861980221441;
        idToOpenseaId[4] =	89502295366922500116793596804463892406027145793870087151951406461061003476993;
        idToOpenseaId[5] =	89502295366922500116793596804463892406027145793870087151951406459961491849217;
        idToOpenseaId[6] =	89502295366922500116793596804463892406027145793870087151951406462160515104769;
        idToOpenseaId[7] =	89502295366922500116793596804463892406027145793870087151951406463260026732545;
        idToOpenseaId[8] =	89502295366922500116793596804463892406027145793870087151951406464359538360321;
        idToOpenseaId[9] =	89502295366922500116793596804463892406027145793870087151951406465459049988097;
        idToOpenseaId[10] =	89502295366922500116793596804463892406027145793870087151951406466558561615873;
        idToOpenseaId[11] =	89502295366922500116793596804463892406027145793870087151951406467658073243649;
        idToOpenseaId[12] =	89502295366922500116793596804463892406027145793870087151951406468757584871425;
        idToOpenseaId[13] =	89502295366922500116793596804463892406027145793870087151951406469857096499201;
        idToOpenseaId[14] =	89502295366922500116793596804463892406027145793870087151951406470956608126977;
        idToOpenseaId[15] =	89502295366922500116793596804463892406027145793870087151951406472056119754753;
        idToOpenseaId[16] =	89502295366922500116793596804463892406027145793870087151951406473155631382529;
        idToOpenseaId[17] =	89502295366922500116793596804463892406027145793870087151951406474255143010305;
        idToOpenseaId[18] =	89502295366922500116793596804463892406027145793870087151951406475354654638081;
        idToOpenseaId[19] =	89502295366922500116793596804463892406027145793870087151951406476454166265857;
        idToOpenseaId[20] =	89502295366922500116793596804463892406027145793870087151951406477553677893633;
        idToOpenseaId[21] =	89502295366922500116793596804463892406027145793870087151951406495145863938049;
        idToOpenseaId[22] =	89502295366922500116793596804463892406027145793870087151951406478653189521409;
        idToOpenseaId[23] =	89502295366922500116793596804463892406027145793870087151951406479752701149185;
        idToOpenseaId[24] =	89502295366922500116793596804463892406027145793870087151951406480852212776961;
        idToOpenseaId[25] =	89502295366922500116793596804463892406027145793870087151951406481951724404737;
        idToOpenseaId[26] =	89502295366922500116793596804463892406027145793870087151951406483051236032513;
        idToOpenseaId[27] =	89502295366922500116793596804463892406027145793870087151951406484150747660289;
        idToOpenseaId[28] =	89502295366922500116793596804463892406027145793870087151951406485250259288065;
        idToOpenseaId[29] =	89502295366922500116793596804463892406027145793870087151951406486349770915841;
        idToOpenseaId[30] =	89502295366922500116793596804463892406027145793870087151951406487449282543617;
        idToOpenseaId[31] =	89502295366922500116793596804463892406027145793870087151951406488548794171393;
        idToOpenseaId[32] =	89502295366922500116793596804463892406027145793870087151951406489648305799169;
        idToOpenseaId[33] =	89502295366922500116793596804463892406027145793870087151951406490747817426945;
        idToOpenseaId[34] =	89502295366922500116793596804463892406027145793870087151951406491847329054721;
        idToOpenseaId[35] =	89502295366922500116793596804463892406027145793870087151951406492946840682497;
        idToOpenseaId[36] =	89502295366922500116793596804463892406027145793870087151951406494046352310273;
        
    }
   
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }
    
    function migrateGLICPIXV1(uint _id) public {
        require(_id > 0 && _id < 37, "WRONG GLICPIX NUMBER");
        require( openseaBullshit(openseaStorefrontContract).balanceOf(msg.sender, idToOpenseaId[_id]) == 1, "YOU DON'T OWN THIS GLICPIX");
        openseaBullshit(openseaStorefrontContract).safeTransferFrom(msg.sender, address(this), idToOpenseaId[_id], 1, "");
        _safeMint(msg.sender, _id);
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }
    
    function setBaseURI(string memory baseURI) public onlyOwner {
        _baseTokenURI = baseURI;
    }
 
    function modifyCode(string memory _code) public onlyOwner {    
       amazingCanvasCode = _code;   
    }   
    
    function tokensOfOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }
    
    
    // PROGRAMMABLE ART, RESULT RENDERS ON #0
    
    
    function showIdToCanvasLocationList() external view returns (uint8[36] memory) {
        
        uint8[36] memory tempcanvasOrder;
        
        for (uint8 i = 1; i < 37; i++) {
            tempcanvasOrder[i - 1] = idToLocationOnCanvas[i];
        }
        
        return tempcanvasOrder;
    }
    
    function showRotationList() external view returns (Rotation[36] memory) {
        
        Rotation[36] memory tempRotationList;
        
        for (uint8 i = 1; i < 37; i++) {
            tempRotationList[i - 1] = RotationList[i];
        }
        
        return tempRotationList;
    }
    
    function showFlipList() external view returns (Flip[36] memory) {
        
        Flip[36] memory tempFlipList;
        
        for (uint8 i = 1; i < 37; i++) {
            tempFlipList[i - 1] = FlipList[i];
        }
        
        return tempFlipList;
    }
    
    function showLayerStatus(uint8 _id) external view returns (LayerStatus memory) {
        
        require(_id > 0 && _id <37 , "PLS ENTER ID BETWEEN 1-36");
        LayerStatus memory layerstatus;
        layerstatus.flip = FlipList[_id];
        layerstatus.rotation = RotationList[_id];
        layerstatus.location = idToLocationOnCanvas[_id];
     
        return(
            layerstatus
        );
        
    }

    function showLayerStatusAll() external view returns(LayerStatus[36] memory) {
        LayerStatus[36] memory tempLayers;      
        for (uint8 i = 1; i < 37; i++) {
            LayerStatus memory layerstatus;
            layerstatus.flip = FlipList[i];
            layerstatus.rotation = RotationList[i];
            layerstatus.location = idToLocationOnCanvas[i];

            tempLayers[i-1] = layerstatus;
        }
        return tempLayers;
    }

    function showOffersOnAToken(uint8 _tokenId) external view returns(swapOfferShowcase[36] memory) {
        require (_tokenId > 0 && _tokenId < 37);
        swapOfferShowcase[36] memory tempOffers;      
        for (uint8 i = 1; i < 37; i++) {
            swapOfferShowcase memory swapoffer;
            SwapOffer memory offer = SwapOffers[_tokenId][i];

            if (offer.isActive && offer.requester == ownerOf(i)) {
                swapoffer.id = i;
                swapoffer.requester = offer.requester;
            } else {
                swapoffer.id = 0;
                swapoffer.requester = address(0);
            }
            
            tempOffers[i - 1] = swapoffer;
            
        }
        return tempOffers;
    }
    
    // YOU CAN OFFER TO SWAP LOCATION OF YOUR GLICPIX WITH ANOTHER
    
    function offerLocationSwap (uint8 yourGPX, uint8 theirGPX) public {
    	require (yourGPX > 0 && yourGPX < 37 && theirGPX > 0 && theirGPX < 37, "PLS ENTER IDS BETWEEN 1-36");
    	require (ownerOf(yourGPX) == msg.sender,"YOU ARE NOT THE OWNER OF THIS GLICPIX GODDAMNIT!");

    	SwapOffers[theirGPX][yourGPX].requester = msg.sender;
    	SwapOffers[theirGPX][yourGPX].isActive = true;
    	
    	emit SwapOfferMade(yourGPX, theirGPX);
    }
    
    
    function cancelOfferLocationSwap (uint8 yourGPX, uint8 theirGPX) public {
    	require (ownerOf(yourGPX) == msg.sender, "U R NOT THE OWNER OF THIS GLICPIX GODDAMNIT!");
    	SwapOffer storage offer = SwapOffers[theirGPX][yourGPX];
        require (offer.requester == msg.sender && offer.isActive);
        offer.isActive = false;
        
        emit SwapOfferCanceled(yourGPX, theirGPX);
    }
    
    
    function acceptLocationSwap (uint8 yourGPX, uint8 theirGPX) public {
    	require (ownerOf(yourGPX) == msg.sender, "U R NOT THE OWNER OF THIS GLICPIX GODDAMNIT!");
    	
    	SwapOffer storage offer = SwapOffers[yourGPX][theirGPX];
    	
    	require(offer.isActive && offer.requester == ownerOf(theirGPX));
        offer.isActive = false;
        
	    uint8 locA = idToLocationOnCanvas[yourGPX];
	    uint8 locB = idToLocationOnCanvas[theirGPX];
	    idToLocationOnCanvas[yourGPX] = locB;
	    idToLocationOnCanvas[theirGPX] = locA;
	    
        emit CanvasOrderChange(yourGPX, locB, theirGPX, locA);
        emit SwapOfferAccepted(yourGPX, theirGPX);
    	
    }
    
    // IF YOU HOLD TWO GLICPIXV1, YOU CAN SWAP THEIR PLACE ANYTIME YOU WANT
    function iOwnTwoGLICPIXYO (uint8 tokenA, uint8 tokenB) public {
    	require (tokenA > 0 && tokenA < 37 && tokenB > 0 && tokenB < 37);    	
    	require (ownerOf(tokenA) == msg.sender && ownerOf(tokenB) == msg.sender);

	    uint8 locA = idToLocationOnCanvas[tokenA];
	    uint8 locB = idToLocationOnCanvas[tokenB];
	    idToLocationOnCanvas[tokenA] = locB;
	    idToLocationOnCanvas[tokenB] = locA;
    	
        emit CanvasOrderChange(tokenA, locB, tokenB, locA);
    }
    
    function rotateGLICPIXinCanvas (uint8 _id, Rotation _rotation) public {
        require (_id > 0 && _id < 37);
     	require(ownerOf(_id) == msg.sender);
    	RotationList[_id] = _rotation;
    	emit RotationChange(_id, _rotation);
    }
    
    function flipGLICPIXinCanvas (uint8 _id, Flip _flip) public {
        require (_id > 0 && _id < 37);
     	require(ownerOf(_id) == msg.sender);
    	FlipList[_id] = _flip;
    	emit FlipChange(_id, _flip);
    }
    
}
// made by Berk - berkozdemir.com - @berkozdemir
// help with programmable art / technical stuff by - @memorycollect0r
// this contract migrates GLICPIXXXVER001 NFTs from OPENSEA STOREFRONT, and also works as a programmable art piece. All 36 tokens have parameters which holders can modify the change the state of programmable canvas.
function offerLocationSwap (uint8 yourGPX, uint8 theirGPX) public {
	require (yourGPX > 0 && yourGPX < 37 && theirGPX > 0 && theirGPX < 37, "PLS ENTER IDS BETWEEN 1-36");
	require (ownerOf(yourGPX) == msg.sender,"YOU ARE NOT THE OWNER OF THIS GLICPIX GODDAMNIT!");

	SwapOffers[theirGPX][yourGPX].requester = msg.sender;
	SwapOffers[theirGPX][yourGPX].isActive = true;
    	
	emit SwapOfferMade(yourGPX, theirGPX);
}

// YOU CAN OFFER TO SWAP LOCATION OF YOUR GLICPIX WITH ANOTHER