package util.sqlparse.visitor.mongo;

import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriterSettings;

public abstract class BsonNode implements MongoNode {
   protected String name;
   protected MongoNode parent;

   public static MongoNode create(String name, BsonValue value) {
      MongoNode node = null;
      if (value.isDocument()) {
         node = new BsonObjectNode(name, value.asDocument());
      } else if (value.isArray()) {
         node = new BsonArrayNode(name, value.asArray());
      } else if (value.isJavaScript()) {
         node = new BsonJsNode(name, value.asJavaScript());
      } else if (value.isJavaScriptWithScope()) {
         node = new BsonJsScopeNode(name, value.asJavaScriptWithScope());
      } else {
         node = new BsonBasicNode(name, value);
      }

      return node;
   }

   public static MongoNode create(BsonValue value) {
      return create(null, value);
   }

   public BsonNode() {
   }

   public BsonNode(String name) {
      this.name = name;
   }

   public String getName() {
      return this.name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public MongoNode getParent() {
      return this.parent;
   }

   public void setParent(MongoNode parent) {
      this.parent = parent;
   }

   public boolean isKeyword() {
      if (this.name == null) {
         return false;
      } else if (this.name.startsWith(Identifier.LSID.code)) {
         return true;
      } else {
         return this.name.startsWith(Identifier.DB.code) ? true : this.name.startsWith("$");
      }
   }

   public String toString() {
      BsonValue value = this.data();
      if (value.isDocument()) {
         return this.toString(value.asDocument());
      } else if (value.isArray()) {
         StringBuilder sb = new StringBuilder();

         for(BsonValue bsonValue : value.asArray()) {
            if (bsonValue.isDocument()) {
               String json = this.toString(bsonValue.asDocument());
               sb.append(json).append("\r\n\r\n");
            }
         }

         if (sb.length() > 0) {
            sb.delete(sb.length() - 4, sb.length());
         }

         return sb.toString();
      } else {
         return value.toString();
      }
   }

   private String toString(BsonDocument value) {
      JsonWriterSettings settings = new JsonWriterSettings(JsonMode.RELAXED);
      return value.toJson(settings);
   }

   public abstract BsonNodeType type();

   public abstract MongoNode clone() throws CloneNotSupportedException;

   public static enum BsonNodeType {
      Basic,
      Key,
      Object,
      Array,
      Javascript,
      JavaScriptWithNode;
   }
}
