// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Address.sol";

contract BSTNFT is IERC721, IERC721Metadata {
    using Strings for uint256;
    using Address for address;
    
    mapping(address => uint256) private _balances;
    mapping(uint256 => address) private _owners;
    mapping(uint256 => address) private _tokenApprovals;
    mapping(address => mapping(address => bool)) private _operatorApprovals;
    
    address private ownerNFT;
    constructor() {
        ownerNFT = msg.sender;
    }
    
    function mint(address to, uint256 tokenId) external {
        // require(ownerNFT == msg.sender, "only onwner");
        require(_owners[tokenId] == address(0), "ERC721: token already minted");

        _balances[to] += 1;
        _owners[tokenId] = to;
        emit Transfer(address(0), to, tokenId);
    }

    function burn(address owner, uint256 tokenId) public {
        require(msg.sender == owner || getApproved(tokenId) == msg.sender || isApprovedForAll(owner, msg.sender), "ERC721: safeTransferFrom caller is not owner nor approved for all");
        _balances[owner] -= 1;
        _owners[tokenId] = address(0);
        emit Transfer(owner, address(0), tokenId);
    }
    
    function name() external pure returns (string memory){return "Beerstamp NFT";}
    function symbol() external pure returns (string memory){return "BSTNFT";}
    function tokenURI(uint256 _tokenId) external pure returns (string memory){
        return string(abi.encodePacked("https://dsl.hsr.ch/nft/", _tokenId.toString()));
        
    }
    function supportsInterface(bytes4 interfaceId) external pure returns (bool){
        return interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
    
    function balanceOf(address _owner) external view returns (uint256) {
        return _balances[_owner];
    }
    function ownerOf(uint256 _tokenId) public view returns (address) {
        return _owners[_tokenId];
    }
    function getApproved(uint256 _tokenId) public view returns (address){
        return _tokenApprovals[_tokenId];
    }
    function isApprovedForAll(address _owner, address _operator) public view returns (bool){
        return _operatorApprovals[_owner][_operator];
    }
    
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public override {
        transferFrom(_from, _to, _tokenId);
        if (_to.isContract()) {
            bytes4 retVal = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
            require (retVal == IERC721Receiver.onERC721Received.selector, "ERC721: transfer to non ERC721Receiver implementer");
        }
    }
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external override {
        safeTransferFrom(_from, _to, _tokenId, "");
    }
    function transferFrom(address _from, address _to, uint256 _tokenId) public override{
        address owner = ownerOf(_tokenId);
        require(msg.sender == owner || getApproved(_tokenId) == msg.sender || isApprovedForAll(owner, msg.sender), "ERC721: safeTransferFrom caller is not owner nor approved for all");
        // Clear approvals from the previous owner
        _tokenApprovals[_tokenId] = address(0);
        emit Approval(owner, address(0), _tokenId);

        _balances[_from] -= 1;
        _balances[_to] += 1;
        _owners[_tokenId] = _to;

        emit Transfer(_from, _to, _tokenId);
    }
    
    function approve(address _to, uint256 _tokenId) external override{
        address owner = ownerOf(_tokenId);
        require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "ERC721: approve caller is not owner nor approved for all");
        _tokenApprovals[_tokenId] = _to;
        emit Approval(owner, _to, _tokenId);
    }

    function setApprovalForAll(address _operator, bool _approved) external{
        _operatorApprovals[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    function safeTransferAll(address _from, address _to,  uint256[] memory _tokenIds) external {
        for(uint i=0; i < _tokenIds.length; i++) {
            safeTransferFrom(_from, _to, _tokenIds[i], "");
        }
    }

    function burnAll(address _owner, uint256[] memory _tokenIds) external {
        for(uint i=0; i < _tokenIds.length; i++) {
            burn(_owner, _tokenIds[i]);
        }
    }

    
}
