// SPDX-License-Identifier: MIT
// ENVELOP(NIFTSY) protocol V1 for NFT. WNFTKeeper
pragma solidity 0.8.13;

import "Ownable.sol";
import "ERC721Holder.sol";
//import "ERC1155Holder.sol";
import "ECDSA.sol";
import "IERC721Mintable.sol";
import "IERC1155Mintable.sol";

/**
 * @title WNFT Token Keeper
 * @dev Use for hold wnft (like staking) 
 */
contract WNFTKeeper is  ERC721Holder, Ownable {
    using ECDSA for bytes32;

    struct NFT {
        address contractAddress;
        uint256 tokenId;
    } 
 
    // Map from chainId to last spawned nft
    mapping(uint256 => NFT) public lastSpawned;  
    

    // Map from hash to frozen WNFT 
    mapping(bytes32 => NFT) public frozenItems; 

    // Oracle signers status
    mapping(address => bool) public oracleSigners;

    // for MVP
    event NewFreeze(
        uint256 indexed forChain, 
        address indexed spawnerContract, 
        uint256 indexed spawnedTokenId
    );

    //event Debug(bytes32 indexed hashed, NFT nft);
    
    function freeze(
        NFT memory _inData, 
        uint256 _forSpawnInChain, 
        bytes32 _secretHashed  // must pass keccak256(abi.encode(uint256 Secret)), NOT secret
    ) 
        public 
        returns (NFT memory spawned) 
    {

        require(lastSpawned[_forSpawnInChain].contractAddress != address(0), "Only enabled target");

        lastSpawned[_forSpawnInChain].tokenId ++;
        frozenItems[
            keccak256(abi.encode(
                _secretHashed,
                lastSpawned[_forSpawnInChain].contractAddress,
                lastSpawned[_forSpawnInChain].tokenId)
            )
        ] = _inData;


        IERC721Mintable(_inData.contractAddress).transferFrom(
            msg.sender, 
            address(this), 
            _inData.tokenId
        );
        
        spawned = NFT(
            lastSpawned[_forSpawnInChain].contractAddress,
            lastSpawned[_forSpawnInChain].tokenId
        );

        emit NewFreeze(
            _forSpawnInChain, 
            lastSpawned[_forSpawnInChain].contractAddress,
            lastSpawned[_forSpawnInChain].tokenId
        );
    }

    function unFreeze(
        uint256 secret, 
        address spawnedContract, 
        uint256 spawnedtokenId,
        //bytes32 _msgForSign, 
        bytes calldata _signature

    ) public 
    {
        

        bytes32 msgMustWasSigned = keccak256(abi.encode(
                msg.sender,
                spawnedContract,
                spawnedtokenId
        )).toEthSignedMessageHash(); 
        

        // Check signature  author
        require(oracleSigners[msgMustWasSigned.recover(_signature)], "Unexpected signer");

        NFT memory frozzenItem = frozenItems[
            keccak256(abi.encode(
                getHashed(secret),
                spawnedContract,
                spawnedtokenId
            ))
        ];

        IERC721Mintable(frozzenItem.contractAddress).transferFrom(
            address(this),
            msg.sender, 
            frozzenItem.tokenId
        );
        
        delete frozenItems[
            keccak256(abi.encode(
                getHashed(secret),
                spawnedContract,
                spawnedtokenId
            ))
        ];
    }

    function checkWNFTByProof(
        bytes32 proof, 
        address spawnedContract, 
        uint256 spawnedtokenId
    ) external view returns (NFT memory wnft) 
    {
        wnft = frozenItems[
            keccak256(abi.encode(
                proof,
                spawnedContract,
                spawnedtokenId

            ))
        ];
    }
    ///////////////////////////////////////////////////////////////////////////

    function setSignerStatus(address _signer, bool _status) external onlyOwner {
        oracleSigners[_signer] = _status;
    }

    function setSpawnerContract(uint256 network, NFT calldata nft) external onlyOwner {
        lastSpawned[network] = nft;
    }

    function getHashed(uint256 secret) public pure returns(bytes32) {
        return keccak256(abi.encodePacked(secret));
    }
   
}
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"forChain","type":"uint256"},{"indexed":true,"internalType":"address","name":"spawnerContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"spawnedTokenId","type":"uint256"}],"name":"NewFreeze","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"bytes32","name":"proof","type":"bytes32"},{"internalType":"address","name":"spawnedContract","type":"address"},{"internalType":"uint256","name":"spawnedtokenId","type":"uint256"}],"name":"checkWNFTByProof","outputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct WNFTKeeper.NFT","name":"wnft","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct WNFTKeeper.NFT","name":"_inData","type":"tuple"},{"internalType":"uint256","name":"_forSpawnInChain","type":"uint256"},{"internalType":"bytes32","name":"_secretHashed","type":"bytes32"}],"name":"freeze","outputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct WNFTKeeper.NFT","name":"spawned","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"frozenItems","outputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"secret","type":"uint256"}],"name":"getHashed","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastSpawned","outputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"oracleSigners","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setSignerStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"network","type":"uint256"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct WNFTKeeper.NFT","name":"nft","type":"tuple"}],"name":"setSpawnerContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"secret","type":"uint256"},{"internalType":"address","name":"spawnedContract","type":"address"},{"internalType":"uint256","name":"spawnedtokenId","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"unFreeze","outputs":[],"stateMutability":"nonpayable","type":"function"}]