package util.sqlparse.visitor.mongo;

import bean.DataBase;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonDouble;
import org.bson.BsonObjectId;
import org.bson.BsonString;

public class Translator implements MongoVisitor<Printer> {
   private ParseResult result;
   private DataBase dataBase;
   private Printer printer;

   public Translator(DataBase dataBase, ParseResult result) {
      this.dataBase = dataBase;
      this.result = result;
      this.printer = new Printer();
      this.printer.db();
   }

   public String getShellApi() {
      return this.printer.toString();
   }

   public Printer visitFind(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode find = ctx.get(Identifier.Find.code);
      String collection = find.value().toString();
      this.printer.write(collection).dot();
      BsonObjectNode filter = (BsonObjectNode)ctx.get(Identifier.Filter.code);
      BsonObjectNode projection = (BsonObjectNode)ctx.get(Identifier.Projection.code);
      BsonObjectNode options = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      options.setValue(document);
      BsonObjectNode let = (BsonObjectNode)ctx.get(Identifier.Let.code);
      if (let != null) {
         options.put("let", (MongoNode)let);
      }

      if (options.children().size() > 0) {
         this.printer.call(Identifier.Find.code, filter, projection, options);
      } else if (projection != null) {
         this.printer.call(Identifier.Find.code, filter, projection);
      } else if (this.printer.mapCount(filter.children()) > 0) {
         this.printer.call(Identifier.Find.code, filter);
      } else {
         this.printer.call(Identifier.Find.code);
      }

      BsonObjectNode sort = (BsonObjectNode)ctx.get(Identifier.Sort.code);
      this.call(Identifier.Sort, sort);
      MongoNode allowDiskUse = ctx.get(Identifier.AllowDiskUse.code);
      if (allowDiskUse != null) {
         this.printer.dot().call(Identifier.AllowDiskUse.code);
      }

      BsonObjectNode count = (BsonObjectNode)ctx.get(Identifier.Count.code);
      if (count != null) {
         this.printer.dot().call(Identifier.Count.code);
      }

      BsonBasicNode skip = (BsonBasicNode)ctx.get(Identifier.Skip.code);
      this.call(Identifier.Skip, skip);
      BsonBasicNode limit = (BsonBasicNode)ctx.get(Identifier.Limit.code);
      this.call(Identifier.Limit, limit);
      BsonBasicNode batchSize = (BsonBasicNode)ctx.get(Identifier.BatchSize.code);
      this.call(Identifier.BatchSize, batchSize);
      BsonBasicNode comment = (BsonBasicNode)ctx.get(Identifier.Comment.code);
      this.call(Identifier.Comment, comment);
      BsonBasicNode maxTimeMS = (BsonBasicNode)ctx.get(Identifier.MaxTimeMS.code);
      this.call(Identifier.MaxTimeMS, maxTimeMS);
      BsonObjectNode readConcern = (BsonObjectNode)ctx.get(Identifier.ReadConcern.code);
      if (readConcern != null) {
         this.printer.dot().call(Identifier.ReadConcern.code, readConcern.getValue().get(Identifier.Level.code));
      }

      BsonBasicNode ReturnKey = (BsonBasicNode)ctx.get(Identifier.ReturnKey.code);
      this.call(Identifier.ReturnKey, ReturnKey);
      BsonObjectNode hint = (BsonObjectNode)ctx.get(Identifier.Hint.code);
      this.call(Identifier.Hint, hint);
      BsonObjectNode min = (BsonObjectNode)ctx.get(Identifier.MinCommand.code);
      this.call(Identifier.MinCommand, min);
      BsonObjectNode max = (BsonObjectNode)ctx.get(Identifier.MaxCommand.code);
      this.call(Identifier.MaxCommand, max);
      BsonBasicNode showRecordId = (BsonBasicNode)ctx.get(Identifier.ShowRecordId.code);
      if (showRecordId != null) {
         this.printer.dot().call(Identifier.ShowRecordId.code);
      }

      BsonBasicNode allowPartialResults = (BsonBasicNode)ctx.get(Identifier.AllowPartialResults.code);
      this.call(Identifier.AllowPartialResults, allowPartialResults);
      BsonObjectNode collation = (BsonObjectNode)ctx.get(Identifier.Collation.code);
      this.call(Identifier.Collation, collation);
      BsonBasicNode noCursorTimeout = (BsonBasicNode)ctx.get(Identifier.NoCursorTimeout.code);
      if (noCursorTimeout != null) {
         this.printer.dot().call(Identifier.NoCursorTimeout.code);
      }

      BsonObjectNode readPref = (BsonObjectNode)ctx.get(Identifier.ReadPref.code);
      this.visitReadPref(readPref);
      BsonBasicNode tailable = (BsonBasicNode)ctx.get(Identifier.Tailable.code);
      this.visitTailable(ctx, tailable);
      this.result.api = this.printer.toString();
      return this.printer;
   }

   private void call(Identifier identifier, MongoNode node) {
      if (node != null) {
         this.printer.dot().call(identifier.code, node);
      }

   }

   private void visitTailable(BsonObjectNode ctx, BsonBasicNode tailable) {
      if (tailable != null) {
         BsonBasicNode awaitData = (BsonBasicNode)ctx.get(Identifier.AwaitData.code);
         if (awaitData != null) {
            BsonObjectNode objectNode = new BsonObjectNode();
            BsonDocument doc = new BsonDocument();
            objectNode.setValue(doc);
            doc.append(Identifier.AwaitData.code, awaitData.getValue());
            this.printer.dot().call(Identifier.Tailable.code, doc);
         }
      }

   }

   private void visitReadPref(BsonObjectNode readPref) {
      if (readPref != null) {
         List<MongoNode> params = new ArrayList();
         MongoNode mode = readPref.get(Identifier.Mode.code);
         params.add(mode);
         MongoNode tags = readPref.get(Identifier.Tags.code);
         MongoNode hedge = readPref.get(Identifier.Hedge.code);
         if (tags != null) {
            params.add(tags);
            if (hedge != null) {
               params.add(hedge);
            }
         }

         MongoNode[] nodesTemp = this.list2Array(params);
         this.printer.dot().call(Identifier.readPref.code, nodesTemp);
      }

   }

   private MongoNode[] list2Array(List<MongoNode> params) {
      MongoNode[] nodes2 = new MongoNode[params.size()];

      for(int i = 0; i < params.size(); ++i) {
         nodes2[i] = (MongoNode)params.get(i);
      }

      return nodes2;
   }

   public Printer visitInsert(MongoNode ctx) {
      BsonArrayNode array = (BsonArrayNode)ctx;
      BsonObjectNode insert = (BsonObjectNode)array.get(array.size() - 1);
      MongoNode collectionCtx = insert.get(Identifier.Insert.code);
      String collection = collectionCtx.value().toString();
      this.printer.write(collection).dot();
      MongoNode[] nodes = new MongoNode[array.children.size() - 1];

      for(int i = 0; i < array.children.size() - 1; ++i) {
         try {
            nodes[i] = array.get(i).clone();
         } catch (CloneNotSupportedException e) {
            e.printStackTrace();
         }
      }

      for(int i = 0; i < nodes.length; ++i) {
         BsonBasicNode id = (BsonBasicNode)((BsonObjectNode)nodes[i]).get(Identifier.ID.code);
         if (id != null && id.getValue() instanceof BsonObjectId) {
            ((BsonObjectNode)nodes[i]).remove(Identifier.ID.code);
         }
      }

      BsonObjectNode options = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      options.setValue(document);
      BsonBasicNode ordered = (BsonBasicNode)insert.get(Identifier.Ordered.code);
      if (ordered != null) {
         options.put("ordered", (MongoNode)ordered);
      }

      BsonObjectNode writeConcern = (BsonObjectNode)insert.get(Identifier.WriteConcern.code);
      if (writeConcern != null) {
         options.put("writeConcern", (MongoNode)writeConcern);
      }

      if (ordered == null && writeConcern == null) {
         this.printer.callInsert(Identifier.Insert.code, (MongoNode)null, nodes);
      } else {
         this.printer.callInsert(Identifier.Insert.code, options, nodes);
      }

      return this.printer;
   }

   public Printer visitUpdate(MongoNode ctx) {
      BsonArrayNode array = (BsonArrayNode)ctx;
      BsonObjectNode dataCtx = (BsonObjectNode)array.get(0);
      BsonObjectNode updateCtx = (BsonObjectNode)array.get(array.size() - 1);
      MongoNode collectionCtx = updateCtx.get(Identifier.Update.code);
      String collection = collectionCtx.value().toString();
      this.printer.write(collection).dot();
      BsonObjectNode q = (BsonObjectNode)dataCtx.get(Identifier.Q.code);
      BsonObjectNode u = (BsonObjectNode)dataCtx.get(Identifier.U.code);
      List<MongoNode> params = new ArrayList();
      params.add(q);
      params.add(u);
      BsonObjectNode node = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      node.setValue(document);
      BsonBasicNode upsert = (BsonBasicNode)dataCtx.get(Identifier.Upsert.code);
      if (upsert != null) {
         node.put(Identifier.Upsert.code, (MongoNode)upsert);
      }

      BsonBasicNode multi = (BsonBasicNode)dataCtx.get(Identifier.Multi.code);
      if (multi != null) {
         node.put(Identifier.Multi.code, (MongoNode)multi);
      }

      BsonObjectNode writeConcern = (BsonObjectNode)updateCtx.get(Identifier.WriteConcern.code);
      if (writeConcern != null) {
         node.put(Identifier.WriteConcern.code, (MongoNode)writeConcern);
      }

      BsonObjectNode collation = (BsonObjectNode)updateCtx.get(Identifier.Collation.code);
      if (collation != null) {
         node.put(Identifier.Collation.code, (MongoNode)collation);
      }

      BsonArrayNode arrayFilters = (BsonArrayNode)dataCtx.get(Identifier.ArrayFilters.code);
      if (arrayFilters != null) {
         node.put(Identifier.ArrayFilters.code, (MongoNode)arrayFilters);
      }

      BsonObjectNode hint = (BsonObjectNode)dataCtx.get(Identifier.Hint.code);
      if (hint != null) {
         node.put(Identifier.Hint.code, (MongoNode)hint);
      }

      BsonObjectNode let = (BsonObjectNode)updateCtx.get(Identifier.Let.code);
      if (let != null) {
         node.put(Identifier.Let.code, (MongoNode)let);
      }

      if (node.children().size() > 0) {
         params.add(node);
      }

      MongoNode[] nodes2 = new MongoNode[params.size()];

      for(int i = 0; i < params.size(); ++i) {
         nodes2[i] = (MongoNode)params.get(i);
      }

      this.printer.call(Identifier.Update.code, nodes2);
      return this.printer;
   }

   public Printer visitDelete(MongoNode ctx) {
      BsonArrayNode array = (BsonArrayNode)ctx;
      BsonObjectNode collectionCtx = (BsonObjectNode)array.get(array.size() - 1);
      String collection = collectionCtx.get(Identifier.Delete.code).value().toString();
      this.printer.write(collection).dot();
      MongoNode[] nodes = new MongoNode[array.children.size() - 1];

      for(int i = 0; i < array.children.size() - 1; ++i) {
         nodes[i] = array.get(i);
      }

      List<MongoNode> params = new ArrayList();
      BsonObjectNode q = (BsonObjectNode)((BsonObjectNode)nodes[0]).get(Identifier.Q.code);
      if (q != null) {
         params.add(q);
      }

      BsonObjectNode node = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      node.setValue(document);
      BsonBasicNode limit = (BsonBasicNode)((BsonObjectNode)nodes[0]).get(Identifier.Limit.code);
      if (limit != null) {
         if (((BsonDouble)limit.getValue()).getValue() == (double)1.0F) {
            document.append(Identifier.JustOne.code, new BsonBoolean(true));
         } else {
            document.append(Identifier.JustOne.code, new BsonBoolean(false));
         }
      }

      BsonObjectNode collation = (BsonObjectNode)((BsonObjectNode)nodes[0]).get(Identifier.Collation.code);
      if (collation != null) {
         node.put(Identifier.Collation.code, (MongoNode)collation);
      }

      BsonObjectNode writeConcern = (BsonObjectNode)collectionCtx.get(Identifier.WriteConcern.code);
      if (writeConcern != null) {
         node.put(Identifier.WriteConcern.code, (MongoNode)writeConcern);
      }

      BsonObjectNode hint = (BsonObjectNode)((BsonObjectNode)nodes[0]).get(Identifier.Hint.code);
      if (hint != null) {
         node.put(Identifier.Hint.code, (MongoNode)hint);
      }

      BsonObjectNode let = (BsonObjectNode)collectionCtx.get(Identifier.Let.code);
      if (let != null) {
         node.put(Identifier.Let.code, (MongoNode)let);
      }

      params.add(node);
      MongoNode[] nodes2 = new MongoNode[params.size()];

      for(int i = 0; i < params.size(); ++i) {
         nodes2[i] = (MongoNode)params.get(i);
      }

      this.printer.call(Identifier.Remove.code, nodes2);
      return this.printer;
   }

   public Printer visitCommand(MongoNode ctx) {
      return this.printer;
   }

   public Printer visitFindAndModify(MongoNode ctx) {
      BsonObjectNode objCtx = (BsonObjectNode)ctx;
      MongoNode mongoNode = objCtx.get(Identifier.FindAndModify.code);
      String collection = mongoNode.value().toString();
      this.result.addCollection(collection);
      this.printer.write(collection).dot();
      MongoNode query = objCtx.get(Identifier.Query.code);
      MongoNode remove = objCtx.get(Identifier.Remove.code);
      MongoNode fields = objCtx.get(Identifier.Fields.code);
      MongoNode let = objCtx.get(Identifier.Let.code);
      MongoNode bypassDocumentValidation = objCtx.get(Identifier.BypassDocumentValidation.code);
      if (remove != null && remove.data().asBoolean().getValue()) {
         this.printer.write(Identifier.FindOneAndDelete.code).lparen();
         this.printer.write(query);
         this.printer.comma();
         Map<String, MongoNode> options = this.options(objCtx, Identifier.ReturnDocument, Identifier.Projection, Identifier.Sort, Identifier.MaxTimeMS, Identifier.Collation);
         if (fields != null) {
            options.put(Identifier.Projection.code, fields);
         }

         this.printer.map(options);
         this.printer.rparen();
      } else {
         MongoNode update = objCtx.get(Identifier.Update.code);
         if (update.toString().contains("$")) {
            this.printer.write(Identifier.FindOneAndUpdate.code);
         } else {
            this.printer.write(Identifier.FindOneAndReplace.code);
         }

         this.printer.lparen();
         this.printer.write(query);
         this.printer.comma();
         if (update != null) {
            this.printer.write(update);
            this.printer.comma();
         }

         Map<String, MongoNode> options = this.options(objCtx, Identifier.Sort, Identifier.MaxTimeMS, Identifier.Upsert, Identifier.ReturnDocument, Identifier.ReturnNewDocument, Identifier.Collation, Identifier.ArrayFilters);
         if (fields != null) {
            options.put(Identifier.Projection.code, fields);
         }

         this.printer.map(options);
         this.printer.rparen();
      }

      return this.printer;
   }

   private Map<String, MongoNode> options(BsonObjectNode ctx, Identifier... ids) {
      Map<String, MongoNode> options = new LinkedHashMap();

      for(Identifier id : ids) {
         options.put(id.code, ctx.get(id.code));
      }

      return options;
   }

   private Map<String, MongoNode> options(Map<String, MongoNode> options, BsonObjectNode ctx, Identifier... ids) {
      for(Identifier id : ids) {
         options.put(id.code, ctx.get(id.code));
      }

      return options;
   }

   public Printer visitCount(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode count = ctx.get(Identifier.Count.code);
      String collection = count.value().toString();
      this.printer.write(collection).dot();
      BsonObjectNode query = (BsonObjectNode)ctx.get(Identifier.Query.code);
      Map<String, MongoNode> options = this.options(ctx, Identifier.Limit, Identifier.Skip, Identifier.Hint, Identifier.MaxTimeMS, Identifier.ReadConcern, Identifier.Collation, Identifier.Hint, Identifier.Comment, Identifier.WriteConcern, Identifier.Let);
      this.printer.write(Identifier.Count.code);
      this.printer.lparen();
      if (query != null) {
         this.printer.write((MongoNode)query);
      } else if (query == null && this.printer.mapCount(options) > 0) {
         this.printer.lbparen();
         this.printer.rbparen();
      }

      if (this.printer.mapCount(options) > 0) {
         this.printer.comma();
         this.printer.map(options);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitAggregate(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode aggregate = ctx.get(Identifier.Aggregate.code);
      String collection = aggregate.value().toString();
      this.printer.write(collection).dot();
      this.printer.write(Identifier.Aggregate.code);
      this.printer.lparen();
      MongoNode pipeline = ctx.get(Identifier.Pipeline.code);
      this.printer.write(pipeline);
      this.printer.comma();
      Map<String, MongoNode> options = this.options(ctx, Identifier.Explain, Identifier.AllowDiskUse, Identifier.Cursor, Identifier.MaxTimeMS, Identifier.BypassDocumentValidation, Identifier.ReadConcern, Identifier.Collation, Identifier.Hint, Identifier.Comment, Identifier.WriteConcern, Identifier.Let);
      this.printer.map(options);
      this.printer.rparen();
      return this.printer;
   }

   public Printer visitDistinct(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode distinct = ctx.get(Identifier.Distinct.code);
      String collection = distinct.value().toString();
      this.printer.write(collection).dot();
      MongoNode key = ctx.get(Identifier.Key.code);
      BsonObjectNode query = (BsonObjectNode)ctx.get(Identifier.Query.code);
      Map<String, MongoNode> options = this.options(ctx, Identifier.Collation);
      this.printer.write(Identifier.Distinct.code);
      this.printer.lparen();
      this.printer.write(key);
      if (query != null && query.children().size() > 0 || this.printer.mapCount(options) > 0) {
         this.printer.comma();
         this.printer.write((MongoNode)query);
      }

      if (this.printer.mapCount(options) > 0) {
         this.printer.comma();
         this.printer.map(options);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitDrop(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode drop = ctx.get(Identifier.Drop.code);
      String collection = drop.value().toString();
      this.printer.write(collection).dot().write(Identifier.Drop.code);
      Map<String, MongoNode> options = this.options(ctx, Identifier.WriteConcern);
      this.printer.lparen();
      if (this.printer.mapCount(options) > 0) {
         this.printer.map(options);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitCreate(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode create = ctx.get(Identifier.Create.code);
      String collection = create.value().toString();
      collection = this.printer.string(collection);
      Map<String, MongoNode> options = this.options(ctx, Identifier.Capped, Identifier.Timeseries, Identifier.ExpireAfterSeconds, Identifier.ClusteredIndex, Identifier.ChangeStreamPreAndPostImages, Identifier.MaxCommand, Identifier.StorageEngine, Identifier.MaxCommand, Identifier.Validator, Identifier.ValidationLevel, Identifier.ValidationAction, Identifier.IndexOptionDefaults, Identifier.ViewOn, Identifier.Pipeline, Identifier.Collation, Identifier.WriteConcern);
      this.printer.write(Identifier.CreateCollection.code);
      this.printer.lparen();
      this.printer.write(collection);
      if (this.printer.mapCount(options) > 0) {
         this.printer.comma();
         this.printer.map(options);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitCreateIndexes(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode createIndexes = ctx.get(Identifier.CreateIndexes.code);
      String collection = createIndexes.value().toString();
      this.printer.write(collection).dot();
      BsonArrayNode indexes = (BsonArrayNode)ctx.get(Identifier.Indexes.code);
      MongoNode commitQuorum = ctx.get(Identifier.CommitQuorum.code);
      if (indexes != null && indexes.size() > 1) {
         this.printer.write(Identifier.CreateIndexes.code);
         this.printer.lparen();
         BsonObjectNode[] keyPatterns = new BsonObjectNode[indexes.size()];

         for(int i = 0; i < indexes.size(); ++i) {
            keyPatterns[i] = (BsonObjectNode)((BsonObjectNode)indexes.get(i)).get(Identifier.Key.code);
         }

         this.printer.array(keyPatterns);
         Map<String, MongoNode> options = this.options((BsonObjectNode)indexes.get(0), Identifier.Unique, Identifier.PartialFilterExpression, Identifier.Sparse, Identifier.ExpireAfterSeconds, Identifier.Hidden, Identifier.Collation, Identifier.StorageEngine);
         if (options != null && this.printer.mapCount(options) > 0) {
            this.printer.comma();
            this.printer.map(options);
         }

         if (commitQuorum != null) {
            this.printer.comma();
            this.printer.write(commitQuorum);
         }

         this.printer.rparen();
      } else {
         this.printer.write(Identifier.CreateIndex.code);
         this.printer.lparen();
         this.printer.write((MongoNode) ((BsonObjectNode)indexes.get(0)).get(Identifier.Key.code));
         Map<String, MongoNode> options = this.options((BsonObjectNode)indexes.get(0), Identifier.Unique, Identifier.Name, Identifier.PartialFilterExpression, Identifier.Sparse, Identifier.ExpireAfterSeconds, Identifier.Hidden, Identifier.Collation, Identifier.StorageEngine);
         if (options != null && this.printer.mapCount(options) > 0) {
            this.printer.comma();
            this.printer.map(options);
         }

         if (commitQuorum != null) {
            this.printer.comma();
            this.printer.write(commitQuorum);
         }

         this.printer.rparen();
      }

      return this.printer;
   }

   public Printer visitDropIndexes(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode dropIndexes = ctx.get(Identifier.DropIndexes.code);
      String collection = dropIndexes.value().toString();
      this.printer.write(collection).dot();
      MongoNode index = ctx.get(Identifier.Index.code);
      if (index instanceof BsonArrayNode) {
         this.printer.write(Identifier.DropIndexes.code);
         this.printer.lparen();
         this.printer.write(index);
      } else if (index instanceof BsonBasicNode && index.data().asString().getValue().equals("*")) {
         this.printer.write(Identifier.DropIndexes.code);
         this.printer.lparen();
      } else {
         this.printer.write(Identifier.DropIndex.code);
         this.printer.lparen();
         this.printer.write(index);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitReIndex(MongoNode ctx) {
      BsonObjectNode ctxTemp = (BsonObjectNode)ctx;
      MongoNode drop = ctxTemp.get(Identifier.ReIndex.code);
      String collection = drop.value().toString();
      this.printer.write(collection).dot().write(Identifier.ReIndex.code).allParen();
      return this.printer;
   }

   public Printer visitRenameCollection(MongoNode ctx) {
      BsonObjectNode ctxTemp = (BsonObjectNode)ctx;
      MongoNode renameCollection = ctxTemp.get(Identifier.RenameCollection.code);
      String collection = renameCollection.value().toString();
      collection = collection.split("\\.")[1];
      MongoNode rename = ctxTemp.get(Identifier.To.code);
      String newName = rename.value().toString();
      newName = newName.split("\\.")[1];
      newName = this.printer.string(newName);
      MongoNode dropTarget = ctxTemp.get(Identifier.DropTarget.code);
      this.printer.write(collection).dot().write(Identifier.RenameCollection.code).lparen();
      this.printer.write(newName);
      if (dropTarget != null && dropTarget.data().asBoolean().getValue()) {
         this.printer.comma();
         this.printer.write(dropTarget);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitMapreduce(MongoNode ctxTemp) {
      BsonObjectNode ctx = (BsonObjectNode)ctxTemp;
      MongoNode mapreduceCollection = ctx.get(Identifier.Mapreduce.code);
      String collection = mapreduceCollection.value().toString();
      this.printer.write(collection).dot().write(Identifier.MapReduce.code).lparen();
      MongoNode map = ctx.get(Identifier.Map.code);
      MongoNode reduce = ctx.get(Identifier.Reduce.code);
      Map<String, MongoNode> options = this.options(ctx, Identifier.OutPlain, Identifier.Query, Identifier.Sort, Identifier.Limit, Identifier.Finalize, Identifier.Scope, Identifier.JsMode, Identifier.Verbose, Identifier.BypassDocumentValidation, Identifier.Collation);
      this.printer.write(map);
      this.printer.comma();
      this.printer.write(reduce);
      if (this.printer.mapCount(options) > 0) {
         this.printer.comma();
         this.printer.map(options);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitCreateUser(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode writeConcern = ctx.get(Identifier.WriteConcern.code);
      Map<String, MongoNode> options = new LinkedHashMap();
      options.put(Identifier.User.code, ctx.get(Identifier.CreateUser.code));
      this.options(options, ctx, Identifier.Pwd, Identifier.CustomData, Identifier.Roles, Identifier.AuthenticationRestrictions, Identifier.Mechanisms, Identifier.PasswordDigestor);
      this.printer.write(Identifier.CreateUser.code);
      this.printer.lparen();
      this.printer.map(options);
      this.printer.comma();
      this.printer.write(writeConcern);
      this.printer.rparen();
      return this.printer;
   }

   public Printer visitSaslSupportedMechs(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      BsonBasicNode userName = (BsonBasicNode)ctx.get(Identifier.SaslSupportedMechs.code);
      String userNameStr = (String)userName.value();
      userNameStr = userNameStr.split("\\.")[1];
      this.printer.write(Identifier.Auth.code);
      this.printer.lparen();
      this.printer.write(this.printer.string(userNameStr));
      this.printer.comma();
      this.printer.write("passwordPrompt()");
      this.printer.rparen();
      return this.printer;
   }

   public Printer visitUpdateUser(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      BsonBasicNode userName = (BsonBasicNode)ctx.get(Identifier.UpdateUser.code);
      Map<String, MongoNode> options = this.options(ctx, Identifier.CustomData, Identifier.Roles, Identifier.AuthenticationRestrictions, Identifier.Mechanisms, Identifier.PasswordDigestor);
      MongoNode writeConcern = ctx.get(Identifier.WriteConcern.code);
      MongoNode pwd = ctx.get(Identifier.Pwd.code);
      if (this.printer.mapCount(options) > 0) {
         this.printer.write(Identifier.UpdateUser.code);
         this.printer.lparen();
         this.printer.write((MongoNode)userName);
         this.printer.comma();
         BsonString password = new BsonString("passwordPrompt()");
         BsonBasicNode passwordPrompt = new BsonBasicNode();
         passwordPrompt.setValue(password);
         options.put(Identifier.Pwd.code, passwordPrompt);
         this.printer.map(options);
         if (writeConcern != null) {
            this.printer.comma();
            this.printer.write(writeConcern);
         }

         this.printer.rparen();
      } else {
         this.printer.write(Identifier.ChangeUserPassword.code);
         this.printer.lparen();
         this.printer.write((MongoNode)userName);
         this.printer.comma();
         this.printer.write("passwordPrompt()");
         this.printer.rparen();
      }

      return this.printer;
   }

   public Printer visitUsersInfo(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode userName = ctx.get(Identifier.UsersInfo.code);
      Map<String, MongoNode> options = new LinkedHashMap();
      this.options(options, ctx, Identifier.ShowCredentials, Identifier.ShowCustomData, Identifier.ShowPrivileges, Identifier.ShowAuthenticationRestrictions, Identifier.Filter);
      this.printer.write(Identifier.GetUser.code);
      this.printer.lparen();
      this.printer.write(userName);
      if (this.printer.mapCount(options) > 0) {
         this.printer.comma();
         this.printer.map(options);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitGrantRolesToUser(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode grantRolesToUser = ctx.get(Identifier.GrantRolesToUser.code);
      MongoNode Roles = ctx.get(Identifier.Roles.code);
      MongoNode writeConcern = ctx.get(Identifier.WriteConcern.code);
      this.printer.write(Identifier.GrantRolesToUser.code);
      this.printer.lparen();
      this.printer.write(grantRolesToUser);
      this.printer.comma();
      this.printer.write(Roles);
      if (writeConcern != null) {
         this.printer.comma();
         this.printer.write(writeConcern);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitRevokeRolesFromUser(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode grantRolesToUser = ctx.get(Identifier.RevokeRolesFromUser.code);
      MongoNode Roles = ctx.get(Identifier.Roles.code);
      MongoNode writeConcern = ctx.get(Identifier.WriteConcern.code);
      this.printer.write(Identifier.RevokeRolesFromUser.code);
      this.printer.lparen();
      this.printer.write(grantRolesToUser);
      this.printer.comma();
      this.printer.write(Roles);
      if (writeConcern != null) {
         this.printer.comma();
         this.printer.write(writeConcern);
      }

      this.printer.rparen();
      return this.printer;
   }

   public Printer visitDropUser(MongoNode context) {
      BsonObjectNode ctx = (BsonObjectNode)context;
      MongoNode dropUser = ctx.get(Identifier.DropUser.code);
      this.printer.write(Identifier.RemoveUser.code);
      this.printer.lparen();
      this.printer.write(dropUser);
      this.printer.rparen();
      return this.printer;
   }

   public Printer visitCreateCollection(MongoNode ctx) {
      return null;
   }
}
