class MongooseStepByStep {
    //Methods are sequenced for demo purposes

    constructor(anUri) {
        this.uri = anUri;
        this.connection = null;
    }

    //-------------------------------------------------------------
    // Connecting

    async connect() {
        await this.basicConnect();
    }

    async basicConnect() {
        //Private
        /* NOTE WELL -- we use "connect" below which supports one (default) connection
        If needing multiple connections for some reason, use "createConnection" -- but
        usage would be a bit different it seems e.g., use of schemas */
        let conn = null;
        try {
            await mongoose.connect(this.uri);
            conn = mongoose.connection;
            this.connection = conn;
            this.connection
                .once('open', () => prn('Success connecting to database'))
                .on('error', err => {
                    prn('ERROR -- ' + err);
                });
        } catch (err) {
            conn && conn.close();
            prn(`Error connecting to ${this.uri}`);
            prn(`Error: ${err}`);
        }
    }

    //-------------------------------------------------------------
    // Getting Length of collection

    async lengthOf(collectionName) {
        //a Collection object (from mongoose lib)
        let mongooseCollection = await this.connection.db.collection(collectionName);
        return await mongooseCollection.countDocuments();
    }

    async showLength(collectionName, dumpLabel) {
        prn(`Length (${dumpLabel}): ${await this.lengthOf(collectionName)}`);
    }

    //-------------------------------------------------------------
    // Removing all from collection

    async removeAllFrom(collectionName) {
        //either of these next two lines work
        //const collection = await Rec.db.collection(collectionName);
        //param e.g., 'recs'
        let mongooseCollection = await this.connection.db.collection(collectionName);
        //https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/
        //Also see "deleteOne"
        await mongooseCollection.deleteMany();
        prn(`Removed all from collection "${collectionName}"`);
    }

    //-------------------------------------------------------------
    // Adding models to collection

    async addTwoRectangles() {
        let rec;

        //GOTCHA -- passing postDocActionFct to "save" function causes it
        //to not work correctly (await seems to fail)
        //The save does take effect, but "later", e.g., after the program terminates
        //await rec.save(postDocActionFct);

        rec = new Rec();
        //rec.set('w', rec);
        rec.set('w', 10);
        rec.set('h', 2);
        await rec.save();

        rec = new Rec({ w: 8, h: 7 });
        await rec.save();

        prn('Done with #addTwoRectangles');
    }

    //-------------------------------------------------------------
    // Queries

    async queryAll() {
        //const collection = await this.conn().db.collection('recs');
        //const docs = await collection.find();
        const models = await Rec.find();
        prn('Models (Documents):');
        for (let model of models)
            prn('Model: ' + model.toString());
    }

    async queryShowingSpecificColumns() {
        //const collection = await this.conn().db.collection('recs');
        //const docs = await collection.find();
        const models = await Rec.find();
        prn('Models (Documents):');
        for (let model of models)
            prn(`Model: ${model.w} x ${model.h}`);
    }

    async querySelectWidthGTE(aWidth) {
        //greater than equal to
        let models = await Rec.find({w: {$gte: aWidth}});
        prn(`Models with w >= ${aWidth}`);
        for (let model of models)
            prn('Model: ' + model.toString());

    }

    //-------------------------------------------------------------
    // Disconnecting

    async disconnect() {
        await this.connection.close();
    }

    //-------------------------------------------------------------
    //Static

    static async runDemo() {
        prn('Starting Demo');
        const demo = new MongooseStepByStep('mongodb://localhost:27017/shapes');
        await demo.connect();
        await demo.showLength('recs', 'Before Remove All');
        await demo.removeAllFrom('recs');
        await demo.showLength('recs', 'After Remove All');

        try {
            await demo.addTwoRectangles();
        } catch(ex) {
            prn('EXCEPTION: ' + ex.toString());
        }

        await demo.showLength('recs', 'After addTwoRectangles');
        await demo.queryAll();
        await demo.queryShowingSpecificColumns();
        await demo.querySelectWidthGTE(10);
        await demo.disconnect();

    }

}

//--------------------------------------------------
//Entry Point

MongooseStepByStep.runDemo()
    .then(() => prn('Finished Demo'));