contract DSSRegistry is ERC721,AccessControl {


    bytes32 public constant ADMIN = keccak256("admin");

    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    mapping(bytes32 => bool) private _hashUsed;
    mapping(uint256 => string) private _tokenURL;
    mapping(uint256 => bool) private _canBeEditedByContractOwner;


    event Minted(address from,address indexed to,uint256 tokenId,
                 bytes32 indexed nameHash,string domain,string uri,string url);

    event UrlEdited(address indexed owner,uint256 indexed tokenId,
                    string uri,string actualURL,string previousURL);


    event PermissionChanged(uint256 indexed tokenId,bool indexed editable);


    modifier onlyAdmin {
        require(hasRole(ADMIN, msg.sender),
                "Sender is not admin");
        _;
    }

    constructor(string memory name, string memory symbol) ERC721(name, symbol) public {
        
        _setBaseURI("ipfs://ipfs/");
        _setupRole(DEFAULT_ADMIN_ROLE,msg.sender);
        _setupRole(ADMIN,msg.sender);

    }
    
    function mint(address to,string memory name,string memory tokenURI,string memory tokenURL)
     public
     onlyAdmin {

        bytes32 nameHash = keccak256(abi.encodePacked(name));
        require(_isNameHashAvailable(nameHash));
        _tokenIds.increment();
        uint256 newItemId = uint256(keccak256(abi.encodePacked(uint256(_tokenIds.current()), keccak256(abi.encodePacked("blockchains-domains")))));
        _mint(to, newItemId);
        _setTokenURI(newItemId,tokenURI);
        _tokenURL[newItemId] = tokenURL;
        _canBeEditedByContractOwner[newItemId] = true;
        emit Minted(msg.sender,to,newItemId,nameHash,name,tokenURI,tokenURL);
        emit PermissionChanged(newItemId,_canBeEditedByContractOwner[newItemId]);
    }

    function _isNameHashAvailable(bytes32 nameHash) private returns(bool) {
        
        require(!_hashUsed[nameHash],
                "Token name already used");
        _hashUsed[nameHash] = true;
        return(true);
    }



    function changeURL(uint256 tokenId,string memory url) 
     public  {
         address tokenOwner = ownerOf(tokenId);
         require(hasRole(ADMIN, msg.sender) || msg.sender == tokenOwner,
                 "Only admin of the contract or token can change url");
         if(tokenOwner != msg.sender){
             require(_canBeEditedByContractOwner[tokenId],
                     "Owner of that token does not allows to be edited");
         }
         string memory tokenUri = tokenURI(tokenId);
         string memory prevUrl = _tokenURL[tokenId];
         _tokenURL[tokenId] = url;
         emit UrlEdited(tokenOwner,tokenId,tokenUri,url,prevUrl);
    }

    function changePermissionURL(uint256 tokenId) public {
        require(ownerOf(tokenId) == msg.sender,
                "Only token's owner can allow or disallow contract's owner to edit tokenUrl");
        _canBeEditedByContractOwner[tokenId] = !_canBeEditedByContractOwner[tokenId];
        PermissionChanged(tokenId,_canBeEditedByContractOwner[tokenId]);
    }


    /**
     * @dev Transfers the ownership of a given token ID to another address.
     * Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     * Requires the msg.sender to be the owner, approved, or operator.
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        if(!_canBeEditedByContractOwner[tokenId]){
            changePermissionURL(tokenId);
        }
        _transfer(from, to, tokenId);
    }

    function tokenURL(uint256 tokenId) external view returns(string memory){
        return(_tokenURL[tokenId]);
    }

    function canBeEdited(uint256 tokenId) external view returns(bool){
        return(_canBeEditedByContractOwner[tokenId]);
    }


    


}
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
    //solhint-disable-next-line max-line-length
    require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
    if(!_canBeEditedByContractOwner[tokenId]){
        changePermissionURL(tokenId);
    }
    _transfer(from, to, tokenId);
}

/**
 * @dev Transfers the ownership of a given token ID to another address.
 * Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
 * Requires the msg.sender to be the owner, approved, or operator.
 * @param from current owner of the token
 * @param to address to receive the ownership of the given token ID
 * @param tokenId uint256 ID of the token to be transferred
 */