package util.sqlparse.visitor.mongo;

import bean.DataBase;
import com.chenyang.druid.sql.dialect.mongo.parser.ApiTreeParser;
import com.chenyang.druid.sql.dialect.mongo.parser.Method;
import java.util.Map;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonDouble;
import org.bson.BsonInt32;
import org.bson.BsonInvalidOperationException;
import org.bson.BsonJavaScript;
import org.bson.BsonNull;
import org.bson.BsonObjectId;
import org.bson.BsonString;
import org.bson.BsonValue;

public class Encoder implements MongoVisitor<MongoNode> {
   public static final int FIRST_PARAM = 0;
   public static final int SECOND_PARAM = 1;
   public static final int THIRD_PARAM = 2;
   public static final String ADMIN = "admin";
   private ParseResult result;
   private DataBase dataBase;
   private String schema;
   private String api;
   private ApiTreeParser apiTreeParser;

   public Encoder(DataBase dataBase, String schema, String api) {
      this.dataBase = dataBase;
      this.schema = schema;
      this.api = api;
   }

   public MongoNode visitFind(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Find.code, new BsonString(apiTreeParser.getCollection()));
      this.setArg2DocOrElse(document, method, 0, Identifier.Filter);
      this.setArg2Doc(document, method, 1, Identifier.Projection);
      if (method != null && method.getArgs().size() > 2) {
         String options = (String)method.getArgs().get(2);
         BsonDocument var7 = BsonDocument.parse(options);
      }

      this.visitNext(document, method);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitFindOne(MongoNode ctx) {
      BsonObjectNode contexts = (BsonObjectNode)this.visitFind(ctx);
      contexts.put(Identifier.Limit.code, (BsonValue)(new BsonDouble((double)1.0F)));
      contexts.put(Identifier.SingleBatch.code, (BsonValue)(new BsonBoolean(true)));
      return contexts;
   }

   private void visitNext(BsonDocument document, Method method) {
      if (method.getNext() != null) {
         Method next = method.getNext();
         if (Identifier.Sort.code.equals(next.getName())) {
            this.setArg2Doc(document, next, 0, Identifier.Sort);
         } else if (Identifier.AllowDiskUse.code.equals(next.getName())) {
            this.setArgBoolean2Doc(document, Identifier.AllowDiskUse, true);
         } else if (Identifier.Count.code.equals(next.getName())) {
            this.mergeArg2Doc(document, next, 0);
            BsonValue value = document.get(Identifier.Find.code);
            document.remove(Identifier.Find.code);
            document.put(Identifier.Count.code, value);
         } else if (Identifier.Skip.code.equals(next.getName())) {
            this.setArgDouble2Doc(document, next, 0, Identifier.Skip);
         } else if (Identifier.Limit.code.equals(next.getName())) {
            this.setArgDouble2Doc(document, next, 0, Identifier.Limit);
            this.setArgBoolean2Doc(document, Identifier.SingleBatch, false);
         } else if (Identifier.BatchSize.code.equals(next.getName())) {
            this.setArgDouble2Doc(document, next, 0, Identifier.BatchSize);
         } else if (Identifier.Comment.code.equals(next.getName())) {
            this.setArgString2Doc(document, next, 0, Identifier.Comment);
         } else if (Identifier.MaxTimeMS.code.equals(next.getName())) {
            this.setArgDouble2Doc(document, next, 0, Identifier.MaxTimeMS);
         } else if (Identifier.ReadConcern.code.equals(next.getName())) {
            String arg = next.getArgs(0);
            BsonDocument doc = new BsonDocument();
            doc.append(Identifier.Level.code, new BsonString(arg));
            document.append(Identifier.ReadConcern.code, doc);
         } else if (Identifier.ReturnKey.code.equals(next.getName())) {
            this.setArgBoolean2Doc(document, Identifier.ReturnKey, true);
         } else if (Identifier.Hint.code.equals(next.getName())) {
            this.setArg2Doc(document, next, 0, Identifier.Hint);
         } else if (Identifier.MinCommand.code.equals(next.getName())) {
            this.setArg2Doc(document, next, 0, Identifier.MinCommand);
         } else if (Identifier.MaxCommand.code.equals(next.getName())) {
            this.setArg2Doc(document, next, 0, Identifier.MaxCommand);
         } else if (Identifier.ShowRecordId.code.equals(next.getName())) {
            this.setArgBoolean2Doc(document, Identifier.ShowRecordId, true);
         } else if (Identifier.AllowPartialResults.code.equals(next.getName())) {
            this.setArgBoolean2Doc(document, Identifier.AllowPartialResults, true);
         } else if (Identifier.Collation.code.equals(next.getName())) {
            this.setArg2Doc(document, next, 0, Identifier.Collation);
         } else if (Identifier.NoCursorTimeout.code.equals(next.getName())) {
            this.setArgBoolean2Doc(document, Identifier.NoCursorTimeout, true);
         } else if (Identifier.readPref.code.equals(next.getName())) {
            BsonDocument doc = new BsonDocument();
            this.setArgString2Doc(doc, next, 0, Identifier.Mode);
            this.setArgArray2Doc(doc, next, 1, Identifier.Tags);
            this.setArg2Doc(doc, next, 2, Identifier.Hedge);
            document.put(Identifier.ReadPref.code, doc);
         } else if (Identifier.Tailable.code.equals(next.getName())) {
            this.setArgBoolean2Doc(document, Identifier.Tailable, true);
            if (next.getArgs().size() > 0) {
               String arg = (String)method.getArgs().get(0);
               BsonDocument argDoc = BsonDocument.parse(arg);
               BsonValue awaitData = argDoc.get(Identifier.AwaitData);
               if (awaitData != null) {
                  if (awaitData.asBoolean().getValue()) {
                     this.setArgBoolean2Doc(document, Identifier.AwaitData, true);
                  } else {
                     this.setArgBoolean2Doc(document, Identifier.AwaitData, false);
                  }
               } else {
                  this.setArgBoolean2Doc(document, Identifier.AwaitData, false);
               }
            } else {
               this.setArgBoolean2Doc(document, Identifier.AwaitData, false);
            }
         }

         this.visitNext(document, next);
      }

   }

   public MongoNode visitInsert(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)ctx;
      BsonObjectNode node = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Insert.code, new BsonString(apiTreeParser.getCollection()));
      if (method != null && method.getArgs().size() > 0) {
         if (15 == (Integer)method.getArgsType().get(0)) {
            String items = method.getArgs(0);
            BsonArray arrayBson = BsonArray.parse(items);

            for(BsonValue value : arrayBson.getValues()) {
               if (value.asDocument().get(Identifier.ID.code) == null) {
                  value.asDocument().append(Identifier.ID.code, new BsonObjectId());
               }

               MongoNode temp = new BsonObjectNode(null, value.asDocument());
               contexts.add(temp);
            }
         } else {
            String item = method.getArgs(0);
            BsonDocument itemDoc = BsonDocument.parse(item);
            if (itemDoc.get(Identifier.ID.code) == null) {
               itemDoc.append(Identifier.ID.code, new BsonObjectId());
            }

            MongoNode temp = new BsonObjectNode(null, itemDoc);
            contexts.add(temp);
         }

         this.mergeArg2Doc(document, method, 1);
      }

      if (document.get(Identifier.Ordered.code) == null) {
         document.append(Identifier.Ordered.code, new BsonBoolean(true));
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      node.setValue(document);
      contexts.add(node);
      return contexts;
   }

   public MongoNode visitInsertOne(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)this.visitInsert(ctx);
      return contexts;
   }

   public MongoNode visitInsertMany(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)this.visitInsert(ctx);
      return contexts;
   }

   public MongoNode visitUpdate(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)ctx;
      BsonObjectNode node = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      BsonObjectNode temp = new BsonObjectNode();
      BsonDocument firstDoc = new BsonDocument();
      document.append(Identifier.Update.code, new BsonString(apiTreeParser.getCollection()));
      this.setArg2Doc(firstDoc, method, 0, Identifier.Q);

      try {
         this.setArg2Doc(firstDoc, method, 1, Identifier.U);
      } catch (BsonInvalidOperationException var10) {
         this.setArgArray2Doc(firstDoc, method, 1, Identifier.U);
      }

      this.mergeArg2Doc(firstDoc, method, 2, Identifier.Multi, Identifier.Upsert, Identifier.ArrayFilters, Identifier.Hint);
      if (firstDoc.get(Identifier.Upsert.code) == null) {
         firstDoc.append(Identifier.Upsert.code, new BsonBoolean(false));
      }

      temp.setValue(firstDoc);
      contexts.add(temp);
      this.mergeArg2Doc(document, method, 2, Identifier.WriteConcern, Identifier.Collation, Identifier.Let);
      if (document.get(Identifier.Ordered.code) == null) {
         document.append(Identifier.Ordered.code, new BsonBoolean(true));
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      node.setValue(document);
      contexts.add(node);
      return contexts;
   }

   public MongoNode visitUpdateOne(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)this.visitUpdate(ctx);
      BsonObjectNode var = (BsonObjectNode)contexts.get(0);
      var.put(Identifier.Multi.code, (BsonValue)(new BsonBoolean(false)));
      return ctx;
   }

   public MongoNode visitUpdateMany(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)this.visitUpdate(ctx);
      BsonObjectNode var = (BsonObjectNode)contexts.get(0);
      var.put(Identifier.Multi.code, (BsonValue)(new BsonBoolean(true)));
      return ctx;
   }

   public MongoNode visitSave(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)ctx;
      BsonObjectNode node = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      BsonObjectNode temp = new BsonObjectNode();
      BsonDocument firstDoc = new BsonDocument();
      document.append(Identifier.Update.code, new BsonString(apiTreeParser.getCollection()));
      if (method != null && method.getArgs().size() > 0) {
         String arg = (String)method.getArgs().get(0);
         BsonDocument argDoc = BsonDocument.parse(arg);
         BsonDocument q = new BsonDocument();
         if (argDoc.get(Identifier.ID.code) == null) {
            return this.visitInsert(ctx);
         }

         q.put(Identifier.ID.code, argDoc.get(Identifier.ID.code));
         firstDoc.append(Identifier.Upsert.code, new BsonBoolean(false));
         firstDoc.append(Identifier.Multi.code, new BsonBoolean(false));
         firstDoc.put(Identifier.Q.code, q);
      }

      this.setArg2Doc(firstDoc, method, 0, Identifier.U);
      temp.setValue(firstDoc);
      contexts.add(temp);
      this.mergeArg2Doc(document, method, 1, Identifier.WriteConcern);
      if (document.get(Identifier.Ordered.code) == null) {
         document.append(Identifier.Ordered.code, new BsonBoolean(true));
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      node.setValue(document);
      contexts.add(node);
      return ctx;
   }

   public MongoNode visitDelete(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)ctx;
      BsonObjectNode node = new BsonObjectNode();
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      BsonObjectNode temp = new BsonObjectNode();
      BsonDocument firstDoc = new BsonDocument();
      document.append(Identifier.Delete.code, new BsonString(apiTreeParser.getCollection()));
      this.setArg2Doc(firstDoc, method, 0, Identifier.Q);
      this.visitJustOne(method, firstDoc);
      this.mergeArg2Doc(firstDoc, method, 1, Identifier.Collation, Identifier.Hint);
      temp.setValue(firstDoc);
      contexts.add(temp);
      this.mergeArg2Doc(document, method, 1, Identifier.WriteConcern, Identifier.Let);
      if (document.get(Identifier.Ordered.code) == null) {
         document.append(Identifier.Ordered.code, new BsonBoolean(true));
      }

      if (document.get(Identifier.WriteConcern.code) == null) {
         BsonDocument writeConcern = new BsonDocument();
         this.mergeArg2Doc(writeConcern, method, 1, Identifier.W, Identifier.J, Identifier.Wtimeout);
         if (writeConcern != null && writeConcern.size() > 0) {
            document.append(Identifier.WriteConcern.code, writeConcern);
         }
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      node.setValue(document);
      contexts.add(node);
      return contexts;
   }

   public MongoNode visitDeleteOne(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)this.visitDelete(ctx);
      BsonObjectNode var = (BsonObjectNode)contexts.get(0);
      var.put(Identifier.Limit.code, (BsonValue)(new BsonDouble((double)1.0F)));
      return ctx;
   }

   public MongoNode visitDeleteMany(MongoNode ctx) {
      BsonArrayNode contexts = (BsonArrayNode)this.visitDelete(ctx);
      BsonObjectNode var = (BsonObjectNode)contexts.get(0);
      var.put(Identifier.Limit.code, (BsonValue)(new BsonDouble((double)0.0F)));
      return ctx;
   }

   private void visitJustOne(Method method, BsonDocument firstDoc) {
      if (method != null && method.getArgs().size() > 0) {
         String arg = (String)method.getArgs().get(0);
         BsonDocument argDoc = BsonDocument.parse(arg);
         BsonValue JustOne = argDoc.get(Identifier.JustOne);
         if (JustOne != null) {
            if (JustOne.asBoolean().getValue()) {
               firstDoc.put(Identifier.Limit.code, new BsonDouble((double)1.0F));
            } else {
               firstDoc.put(Identifier.Limit.code, new BsonDouble((double)0.0F));
            }
         } else {
            firstDoc.put(Identifier.Limit.code, new BsonDouble((double)0.0F));
         }
      }

   }

   public MongoNode visitCommand(MongoNode ctx) {
      return null;
   }

   public MongoNode visitFindAndModify(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.FindAndModify.code, new BsonString(apiTreeParser.getCollection()));
      this.mergeArg2Doc(document, method, 0);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode findOneAndReplace(MongoNode ctx) {
      MongoNode context = this.findOneAndUpdate(ctx);
      return context;
   }

   public MongoNode findOneAndDelete(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.FindAndModify.code, new BsonString(apiTreeParser.getCollection()));
      this.setArg2Doc(document, method, 0, Identifier.Query);
      this.mergeArg2Doc(document, method, 1);
      document.append(Identifier.Remove.code, new BsonBoolean(false));
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode findOneAndUpdate(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.FindAndModify.code, new BsonString(apiTreeParser.getCollection()));
      this.setArg2Doc(document, method, 0, Identifier.Query);
      this.setArg2Doc(document, method, 1, Identifier.Update);
      this.mergeArg2Doc(document, method, 2);
      document.append(Identifier.Upsert.code, new BsonBoolean(false));
      document.append(Identifier.New.code, new BsonBoolean(false));
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitCount(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Count.code, new BsonString(apiTreeParser.getCollection()));
      this.setArg2Doc(document, method, 0, Identifier.Query);
      this.mergeArg2Doc(document, method, 1);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitAggregate(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Aggregate.code, new BsonString(apiTreeParser.getCollection()));
      this.setArgArray2Doc(document, method, 0, Identifier.Pipeline);
      this.mergeArg2Doc(document, method, 1);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitDistinct(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Distinct.code, new BsonString(apiTreeParser.getCollection()));
      this.setArgString2Doc(document, method, 0, Identifier.Key);
      if (method.getArgs().size() > 1) {
         this.setArg2Doc(document, method, 1, Identifier.Query);
      } else {
         this.setArgNew2Doc(document, Identifier.Query);
      }

      this.mergeArg2Doc(document, method, 2);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitDrop(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      document.append(Identifier.Drop.code, new BsonString(apiTreeParser.getCollection()));
      this.mergeArg2Doc(document, apiTreeParser.getMethod(), 0);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitCountDocuments(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Aggregate.code, new BsonString(apiTreeParser.getCollection()));
      BsonArray array = new BsonArray();
      BsonDocument first = new BsonDocument();
      this.setArg2Doc(first, method, 0, Identifier.Match);
      array.add(first);
      if (method != null && method.getArgs().size() > 1) {
         String arg = (String)method.getArgs().get(1);
         BsonDocument argDoc = BsonDocument.parse(arg);

         for(String s : argDoc.keySet()) {
            BsonDocument n = new BsonDocument();
            if (!Identifier.Limit.code.equals(s) && !Identifier.Skip.code.equals(s)) {
               n.put(s, argDoc.get(s));
            } else {
               n.put("$" + s, argDoc.get(s));
            }

            array.add(n);
         }
      }

      BsonDocument third = new BsonDocument();
      BsonDocument n = new BsonDocument();
      n.put(Identifier.Sum.code, new BsonDouble((double)1.0F));
      BsonDocument group = new BsonDocument();
      group.put(Identifier.N.code, n);
      group.put(Identifier.ID.code, new BsonNull());
      third.put(Identifier.Group.code, group);
      array.add(third);
      document.put(Identifier.Pipeline.code, array);
      BsonDocument cursor = new BsonDocument();
      document.put(Identifier.Cursor.code, cursor);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitCreate(MongoNode ctx) {
      return null;
   }

   public MongoNode visitCreateIndexes(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.CreateIndexes.code, new BsonString(apiTreeParser.getCollection()));
      if (method != null && method.getArgs().size() > 0) {
         String arg = (String)method.getArgs().get(0);
         if ((Integer)method.getArgsType().get(0) == 13) {
            BsonDocument argDoc = BsonDocument.parse(arg);
            BsonDocument key = new BsonDocument();
            BsonArray array = new BsonArray();
            key.put(Identifier.Key.code, argDoc);
            array.add(key);
            document.put(Identifier.Indexes.code, array);
         } else {
            BsonArray array = new BsonArray();

            for(BsonValue argDoc : BsonArray.parse(arg)) {
               BsonDocument key = new BsonDocument();
               key.put(Identifier.Key.code, argDoc);
               array.add(key);
            }

            document.put(Identifier.Indexes.code, array);
         }
      }

      BsonArray array = (BsonArray)document.get(Identifier.Indexes.code);

      for(BsonValue value : array) {
         this.mergeArg2Doc((BsonDocument)value, method, 1);
      }

      this.generateIndexName(array);
      this.setArgString2Doc(document, method, 2, Identifier.CommitQuorum);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   private void generateIndexName(BsonArray array) {
      for(BsonValue value : array) {
         BsonDocument doc = (BsonDocument)value;
         if (doc.get(Identifier.Name.code) == null) {
            BsonDocument key = (BsonDocument)doc.get(Identifier.Key.code);
            if (key != null) {
               StringBuilder builder = new StringBuilder();

               for(String s : key.keySet()) {
                  builder.append(s).append("_1");
               }

               doc.put(Identifier.Name.code, new BsonString(builder.toString()));
            }
         }
      }

   }

   public MongoNode visitDropIndexes(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.DropIndexes.code, new BsonString(apiTreeParser.getCollection()));
      if (method != null && method.getArgs().size() > 0) {
         String arg = (String)method.getArgs().get(0);
         if ((Integer)method.getArgsType().get(0) == 13) {
            this.setArg2Doc(document, method, 0, Identifier.Index);
         } else if ((Integer)method.getArgsType().get(0) == 15) {
            this.setArgArray2Doc(document, method, 0, Identifier.Index);
         } else {
            this.setArgString2Doc(document, method, 0, Identifier.Index);
         }
      } else {
         document.append(Identifier.Index.code, new BsonString("*"));
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitReIndex(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      document.append(Identifier.ReIndex.code, new BsonString(apiTreeParser.getCollection()));
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitGetIndexes(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      document.append(Identifier.ListIndexes.code, new BsonString(apiTreeParser.getCollection()));
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitRenameCollection(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.RenameCollection.code, new BsonString(this.schema + "." + apiTreeParser.getCollection()));
      if (method != null && method.getArgs().size() > 0) {
         String arg = method.getArgs(0);
         document.append(Identifier.To.code, new BsonString(this.schema + "." + arg));
      }

      this.setArgBoolean2Doc(document, method, 1, Identifier.DropTarget);
      if (document.get(Identifier.DropTarget) == null) {
         this.setArgBoolean2Doc(document, Identifier.DropTarget, false);
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString("admin"));
      context.setValue(document);
      return context;
   }

   public MongoNode visitMapreduce(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      document.append(Identifier.Mapreduce.code, new BsonString(apiTreeParser.getCollection()));
      this.setArgJavascript2Doc(document, method, 0, Identifier.Map);
      this.setArgJavascript2Doc(document, method, 1, Identifier.Reduce);
      this.mergeArg2Doc(document, method, 2);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitCreateUser(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.mergeArg2Doc(document, method, 0);
      BsonValue user = document.get(Identifier.User.code);
      if (user != null) {
         document.remove(Identifier.User.code);
         document.put(Identifier.CreateUser.code, user);
      }

      this.setArg2Doc(document, method, 1, Identifier.WriteConcern);
      this.setArgBoolean2Doc(document, Identifier.DigestPassword, true);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitChangeUserPassword(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.UpdateUser);
      this.setArgString2Doc(document, method, 1, Identifier.Pwd);
      BsonDocument writeConcern = this.generateWirteconcern();
      document.append(Identifier.WriteConcern.code, writeConcern);
      if (document.get(Identifier.Pwd.code) != null) {
         this.setArgBoolean2Doc(document, Identifier.DigestPassword, true);
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   private BsonDocument generateWirteconcern() {
      BsonDocument writeConcern = new BsonDocument();
      writeConcern.put(Identifier.W.code, new BsonString("majority"));
      writeConcern.put(Identifier.Wtimeout.code, new BsonDouble((double)600000.0F));
      return writeConcern;
   }

   public MongoNode visitSaslSupportedMechs(MongoNode ctx) {
      return null;
   }

   public MongoNode visitUpdateUser(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.UpdateUser);
      this.mergeArg2Doc(document, method, 1);
      this.setArg2Doc(document, method, 2, Identifier.WriteConcern);
      if (document.get(Identifier.Pwd.code) != null) {
         this.setArgBoolean2Doc(document, Identifier.DigestPassword, true);
      }

      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitUsersInfo(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.UsersInfo);
      this.mergeArg2Doc(document, method, 1);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitGrantRolesToUser(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.GrantRolesToUser);
      this.setArgArray2Doc(document, method, 1, Identifier.Roles);
      this.setArg2Doc(document, method, 2, Identifier.WriteConcern);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitRunCommand(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = null;
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      if (method != null && method.getArgs().size() > 0) {
         String arg = (String)method.getArgs().get(0);
         document = BsonDocument.parse(arg);
         BsonValue lsid = document.get(Identifier.LSID.code);
         if (lsid == null) {
            document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
         }

         BsonValue db = document.get(Identifier.DB.code);
         if (db == null) {
            document.append(Identifier.DB.code, new BsonString(this.schema));
         }

         context.setValue(document);
      }

      return null;
   }

   public MongoNode visitRevokeRolesFromUser(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.RevokeRolesFromUser);
      this.setArgArray2Doc(document, method, 1, Identifier.Roles);
      this.setArg2Doc(document, method, 2, Identifier.WriteConcern);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitDropUser(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.DropUser);
      BsonDocument writeConcern = this.generateWirteconcern();
      document.append(Identifier.WriteConcern.code, writeConcern);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   public MongoNode visitCreateCollection(MongoNode ctx) {
      BsonObjectNode context = (BsonObjectNode)ctx;
      BsonDocument document = new BsonDocument();
      ApiTreeParser apiTreeParser = this.getApiTreeParser();
      Method method = apiTreeParser.getMethod();
      this.setArgString2Doc(document, method, 0, Identifier.Create);
      this.mergeArg2Doc(document, method, 1);
      document.append(Identifier.LSID.code, MongoTools.generateIdDoc());
      document.append(Identifier.DB.code, new BsonString(this.schema));
      context.setValue(document);
      return context;
   }

   private void mergeDocument(BsonDocument target, BsonDocument source) {
      for(Map.Entry<String, BsonValue> entry : source.entrySet()) {
         BsonValue value = (BsonValue)entry.getValue();
         String key = (String)entry.getKey();
         target.put(key, value);
      }

   }

   public String getApi() {
      return this.api;
   }

   public void setApi(String api) {
      this.api = api;
   }

   public ParseResult getResult() {
      return this.result;
   }

   public void setResult(ParseResult result) {
      this.result = result;
   }

   public DataBase getDataBase() {
      return this.dataBase;
   }

   public void setDataBase(DataBase dataBase) {
      this.dataBase = dataBase;
   }

   public String getSchema() {
      return this.schema;
   }

   public void setSchema(String schema) {
      this.schema = schema;
   }

   public ApiTreeParser getApiTreeParser() {
      return this.apiTreeParser;
   }

   public void setApiTreeParser(ApiTreeParser apiTreeParser) {
      this.apiTreeParser = apiTreeParser;
   }

   private void mergeArg2Doc(BsonDocument document, Method method, int paramNumber) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = method.getArgs(paramNumber);
         BsonDocument argDoc = BsonDocument.parse(arg);
         this.mergeDocument(document, argDoc);
      }

   }

   private void mergeArg2Doc(BsonDocument document, Method method, int paramNumber, Identifier... ids) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = method.getArgs(paramNumber);
         BsonDocument argDoc = BsonDocument.parse(arg);
         BsonDocument temp = new BsonDocument();

         for(Identifier id : ids) {
            BsonValue value = argDoc.get(id.code);
            if (value != null) {
               temp.put(id.code, argDoc.get(id.code));
            }
         }

         this.mergeDocument(document, temp);
      }

   }

   private void setArg2DocOrElse(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         this.setArg2Doc(document, method, paramNumber, identifier);
      } else {
         this.setArgNew2Doc(document, identifier);
      }

   }

   private void setArg2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = (String)method.getArgs().get(paramNumber);
         BsonDocument argDoc = BsonDocument.parse(arg);
         document.put(identifier.code, argDoc);
      }

   }

   private void setArgOfItem2Doc(BsonDocument document, Method method, int paramNumber, Identifier item, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = (String)method.getArgs().get(paramNumber);
         BsonDocument argDoc = BsonDocument.parse(arg);
         if (argDoc.get(item.code) != null) {
            document.put(identifier.code, argDoc.get(item.code));
         }
      }

   }

   private void setArgArray2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = (String)method.getArgs().get(paramNumber);
         BsonArray argDoc = BsonArray.parse(arg);
         document.put(identifier.code, argDoc);
      }

   }

   private void setArgString2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = method.getArgs(paramNumber);
         document.append(identifier.code, new BsonString(arg));
      }

   }

   private void setArgNumber2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = method.getArgs(paramNumber);
         document.append(identifier.code, new BsonInt32(Integer.valueOf(arg)));
      }

   }

   private void setArgJavascript2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = (String)method.getArgs().get(paramNumber);
         BsonJavaScript argDoc = new BsonJavaScript(arg);
         document.put(identifier.code, argDoc);
      }

   }

   private void setArgDouble2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = method.getArgs(paramNumber);
         document.append(identifier.code, new BsonDouble(Double.valueOf(arg)));
      }

   }

   private void setArgNew2Doc(BsonDocument document, Identifier identifier) {
      BsonDocument doc = new BsonDocument();
      document.put(identifier.code, doc);
   }

   private void setArgBoolean2Doc(BsonDocument document, Identifier identifier, boolean bool) {
      document.put(identifier.code, new BsonBoolean(bool));
   }

   private void setArgBoolean2Doc(BsonDocument document, Method method, int paramNumber, Identifier identifier) {
      if (method != null && method.getArgs().size() > paramNumber) {
         String arg = (String)method.getArgs().get(paramNumber);
         BsonBoolean argDoc = new BsonBoolean(Boolean.valueOf(arg));
         document.put(identifier.code, argDoc);
      }

   }
}
