contract CSNYC is ERC721, ERC721EnumerableSimple, ERC721Metadata("Crypto stamp Special NYC Edition", "CSNYC") {

    string public uribase;

    address public createControl;

    address public tokenAssignmentControl;

    bool public mintingFinished = false;

    constructor(address _createControl, address _tokenAssignmentControl)
    public
    {
        createControl = _createControl;
        tokenAssignmentControl = _tokenAssignmentControl;
        uribase = "https://crypto.post.at/CSNYC/meta/";
    }

    modifier onlyCreateControl()
    {
        require(msg.sender == createControl, "createControl key required for this function.");
        _;
    }

    modifier onlyTokenAssignmentControl() {
        require(msg.sender == tokenAssignmentControl, "tokenAssignmentControl key required for this function.");
        _;
    }

    modifier requireMinting() {
        require(mintingFinished == false, "This call only works when minting is not finished.");
        _;
    }

    // Issue a new crypto stamp asset, giving it to a specific owner address.
    // As appending the ID into a URI in Solidity is complicated, generate both
    // externally and hand them over to the asset here.
    function create(uint256 _tokenId, address _owner)
    public
    onlyCreateControl
    requireMinting
    {
        // Make sure we do not get any holes in Ids so we can do more optimizations.
        require(_tokenId == 0 || _exists(_tokenId.sub(1)), "Previous token ID has to exist.");
        // _mint already ends up checking if owner != 0 and that tokenId doesn't exist yet.
        _mint(_owner, _tokenId);
    }

    // Batch-issue multiple crypto stamp with adjacent IDs.
    function createMulti(uint256 _tokenIdStart, address _owner, uint256 count)
    public
    onlyCreateControl
    requireMinting
    {
        // Make sure we do not get any holes in Ids so we can do more optimizations.
        require(_tokenIdStart == 0 || _exists(_tokenIdStart.sub(1)), "Previous token ID has to exist.");
        for (uint256 i = 0; i < count; i++) {
            // Make sure this is in sync with what create() does.
            _mint(_owner, _tokenIdStart + i);
        }
    }

    // Finish the creation/minting process.
    function finishMinting()
    public
    onlyCreateControl
    {
        mintingFinished = true;
    }

    // Set new base for the token URI.
    function newUriBase(string memory _newUriBase)
    public
    onlyCreateControl
    {
        uribase = _newUriBase;
    }

    // Override ERC721Metadata to create the URI from the base and ID.
    function tokenURI(uint256 _tokenId)
    external view
    returns (string memory)
    {
        require(_exists(_tokenId), "Token ID does not exist.");
        return string(abi.encodePacked(uribase, uint2str(_tokenId)));
    }

    // Returns whether the specified token exists
    function exists(uint256 tokenId) public view returns (bool) {
        return _exists(tokenId);
    }

    // Helper function from Oraclize
    // https://github.com/oraclize/ethereum-api/blob/master/oraclizeAPI_0.5.sol
    function uint2str(uint256 inp)
    internal pure
    returns (string memory)
    {
        if (inp == 0) return "0";
        uint i = inp;
        uint j = i;
        uint length;
        while (j != 0){
            length++;
            j /= 10;
        }
        bytes memory bstr = new bytes(length);
        uint k = length - 1;
        while (i != 0){
            bstr[k--] = byte(uint8(48 + i % 10));
            i /= 10;
        }
        return string(bstr);
    }

    /*** Enable reverse ENS registration ***/

    // Call this with the address of the reverse registrar for the respecitve network and the ENS name to register.
    // The reverse registrar can be found as the owner of 'addr.reverse' in the ENS system.
    // For Mainnet, the address needed is 0x9062c0a6dbd6108336bcbe4593a3d1ce05512069
    function registerReverseENS(address _reverseRegistrarAddress, string calldata _name)
    external
    onlyTokenAssignmentControl
    {
        require(_reverseRegistrarAddress != address(0), "need a valid reverse registrar");
        ENSReverseRegistrarI(_reverseRegistrarAddress).setName(_name);
    }

    /*** Make sure currency doesn't get stranded in this contract ***/

    // If this contract gets a balance in some ERC20 contract after it's finished, then we can rescue it.
    function rescueToken(IERC20 _foreignToken, address _to)
    external
    onlyTokenAssignmentControl
    {
        _foreignToken.transfer(_to, _foreignToken.balanceOf(address(this)));
    }

    // Make sure this contract cannot receive ETH.
    function()
    external payable
    {
        revert("The contract cannot receive ETH payments.");
    }
}
/* The inheritance is very much the same as OpenZeppelin's ERC721Full,
 * but using a simplified (and cheaper) version of ERC721Enumerable */
function finishMinting()
public
onlyCreateControl
{
    mintingFinished = true;
}

// Finish the creation/minting process.