contract ERC721Premint is ERC721 {

    uint256 private _premintedCount;
    address private _premintOwner;
    uint256 private _mintedCount;
    mapping(uint256 => bool) private _burned;


    constructor(string memory name_, string memory symbol_, address premintOwner_) ERC721(name_, symbol_) {        
        _premintOwner = premintOwner_ == address(0) ? _msgSender() : premintOwner_;
    }

   
    // ------------------ IERC721 Overrides ---------------

    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return super.balanceOf(owner) 
                    + (_isPremintOwner(owner) ? _premintedCount : 0); 
    }

    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        if (isPremintedToken(tokenId)){  
            owner = _premintOwner;
        }
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    function _exists(uint256 tokenId) internal view virtual override returns (bool) {
        return _owners[tokenId] != address(0) || isPremintedToken(tokenId); 
    }

    function _mint(address to, uint256 tokenId) internal virtual override {
        super._mint(to, tokenId);

        _mintedCount += 1;
    }

    function _burn(uint256 tokenId) internal virtual override {
        require(!isPremintedToken(tokenId), "Premint: burning preminted token");
        super._burn(tokenId);

        _burned[tokenId] = true;
    }


    function _transfer(address from, address to, uint256 tokenId) internal override virtual {
        bool _isPremintedToken = isPremintedToken(tokenId);

        require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");
        require(!_isPremintedToken || !_isPremintOwner(to), "Premint: transfer preminted token to the Premint Owner");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        if (_isPremintOwner(from) && _isPremintedToken){
            _premintedCount -= 1;
            _mintedCount += 1;
        }
        else {
            _balances[from] -= 1;
        }

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }


    // ------------------ IERC721Premint and utils ---------------


    function _totalSupply() internal view returns (uint256) {
        return _mintedCount + _premintedCount;
    }

    function _isPremintOwner(address addr) internal view returns (bool) {
        return _premintOwner == addr;
    }

    function originBalanceOf(address owner) public view virtual returns (uint256) {
        return super.balanceOf(owner);
    }

    function originOwnerOf(uint256 tokenId) public view virtual returns (address) {
        return super.ownerOf(tokenId);
    }

    function premintOwner() public virtual view returns (address) {
        return _premintOwner;
    }

    function premintedCount() public virtual view returns (uint256) {
        return _premintedCount;
    }

    function isPremintedToken(uint256 tokenId) public view virtual returns (bool) {
        return 
            _owners[tokenId] == address(0) && 
            !_burned[tokenId] &&
            tokenId <= _totalSupply();
    }

    function mintedCount() public virtual view returns (uint256) {
        return _mintedCount; 
    }

    function _premint(uint256 count) internal virtual {
        require(count > 0, "Premint: premint count is zero");

        uint256 startTokenId = _totalSupply() + 1;
        _premintedCount += count;

        for(uint256 i = 0; i < count; i++){
            emit Transfer(address(0), _premintOwner, startTokenId + i);
        }
    }  

}
/**
 *  ERC721 with premint feautures. Premint owner can't be null address. 
 */
function _totalSupply() internal view returns (uint256) {
    return _mintedCount + _premintedCount;
}

// ------------------ IERC721Premint and utils ---------------