content: /**
   * Copyright (C) 2010 EdgyTech LLC.
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
   * use this file except in compliance with the License. You may obtain a copy of
   * the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   * License for the specific language governing permissions and limitations under
   * the License.
   */
  package com.edgytech.umongo;
  
  import com.edgytech.swingfast.XmlComponentUnit;
  import com.mongodb.BasicDBObject;
  import com.mongodb.DB;
  import com.mongodb.DBCollection;
  import com.mongodb.DBObject;
  import com.mongodb.Mongo;
  import com.mongodb.MongoException;
  import com.mongodb.gridfs.GridFS;
  import com.mongodb.gridfs.GridFSDBFile;
  import com.mongodb.gridfs.GridFSInputFile;
  import java.io.File;
  import java.io.IOException;
  import javax.swing.JPanel;
  import org.bson.types.Code;
  import com.edgytech.umongo.DbPanel.Item;
  import com.edgytech.swingfast.*;
  import com.mongodb.*;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   *
   * @author antoine
   */
  public class DbPanel extends BasePanel implements EnumListener<Item> {
  
      enum Item {
  
          icon,
          name,
          queryOptions,
          writeConcern,
          readPreference,
          stats,
          getStats,
          refresh,
          dropDatabase,
          uploadFile,
          uploadFileDialog,
          uploadFilePath,
          uploadFileName,
          uploadContentType,
          uploadMetadata,
          downloadFile,
          downloadQuery,
          downloadFileName,
          downloadFilePath,
          listFiles,
          deleteFile,
          deleteQuery,
          deleteFileName,
          commandCmd,
          commandStr,
          commandJson,
          commandHelp,
          listCommands,
          eval,
          evalCode,
          evalNoLock,
          dbHash,
          readWriteOptions,
          authenticate,
          authUser,
          authPassword,
          manageUsers,
          userDialog,
          userList,
          addUser,
          removeUser,
          editUser,
          userChange,
          createCollection,
          createCollName,
          createCollCapped,
          createCollSize,
          createCollCount,
  //        createCollAutoIndex,
          createCollUsePowerOf2Sizes,
          enableSharding,
          movePrimary,
          mvpToShard,
          shardingInfo,
          profile,
          profileLevel,
          profileSlowMS,
          analyzeProfilingData,
          repair,
          addJSFunction,
          addJSFunctionName,
          addJSFunctionCode,
          findJSFunction,
          findJSFunctionName,
          summarizeData
      }
      GridFS gridFS;
  
      public DbPanel() {
          setEnumBinding(Item.values(), this);
      }
  
      public GridFS getGridFS() {
          if (gridFS == null) {
              gridFS = new GridFS(getDbNode().getDb());
          }
          return gridFS;
      }
  
      public DbNode getDbNode() {
          return (DbNode) getNode();
      }
  
      @Override
      protected void updateComponentCustom(JPanel old) {
          try {
              DB db = getDbNode().getDb();
              setStringFieldValue(Item.name, db.getName());
              setStringFieldValue(Item.queryOptions, MongoUtils.queryOptionsToString(db.getOptions()));
              ((DocField) getBoundUnit(Item.writeConcern)).setDoc(db.getWriteConcern().getCommand());
              ((DocField) getBoundUnit(Item.readPreference)).setDoc(db.getReadPreference().toDBObject());
              ((CmdField) getBoundUnit(Item.stats)).updateFromCmd(db);
          } catch (Exception e) {
              UMongo.instance.showError(this.getClass().getSimpleName() + " update", e);
          }
      }
  
      public void actionPerformed(Item enm, XmlComponentUnit unit, Object src) {
      }
  
      public void command(final ButtonBase button) {
          final DB db = getDbNode().getDb();
          final String scmd = getStringFieldValue(Item.commandStr);
          final DBObject cmd = !scmd.isEmpty() ? new BasicDBObject(scmd, 1)
                  : ((DocBuilderField) getBoundUnit(Item.commandJson)).getDBObject();
          boolean help = getBooleanFieldValue(Item.commandHelp);
          if (help) {
              cmd.put("help", true);
          }
  
          new DbJobCmd(db, cmd, null, button).addJob();
      }
  
      public void listCommands(ButtonBase button) {
          new DbJobCmd(getDbNode().getDb(), "listCommands").addJob();
      }
  
      public void eval(final ButtonBase button) {
          final DB db = getDbNode().getDb();
          final String sfunc = getStringFieldValue(Item.evalCode);
          final boolean noLock = getBooleanFieldValue(Item.evalNoLock);
          final BasicDBObject cmd = new BasicDBObject("$eval", sfunc);
          if (noLock) {
              cmd.put("nolock", true);
          }
          new DbJobCmd(db, cmd, null, button).addJob();
      }
  
      public void uploadFile(final ButtonBase button) {
          final DbNode dbNode = getDbNode();
          final DB db = dbNode.getDb();
  
          final String path = getStringFieldValue(Item.uploadFilePath);
          if (path.isEmpty()) {
              return;
          }
          final File src = new File(path);
          final String fileName = getStringFieldValue(Item.uploadFileName);
          final String contentType = getStringFieldValue(Item.uploadContentType);
          final DBObject metadata = ((DocBuilderField) getBoundUnit(Item.uploadMetadata)).getDBObject();
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  final GridFSInputFile file = getGridFS().createFile(src);
                  if (!fileName.isEmpty()) {
                      file.setFilename(fileName);
                  }
                  if (!contentType.isEmpty()) {
                      file.setContentType(contentType);
                  }
                  if (metadata != null) {
                      file.setMetaData(metadata);
                  }
                  file.save();
                  return file;
              }
  
              @Override
              public String getNS() {
                  return db.getName();
              }
  
              @Override
              public String getShortName() {
                  return "Upload File";
              }
  
              @Override
              public DBObject getRoot(Object result) {
                  return new BasicDBObject("path", path);
              }
  
              @Override
              public void wrapUp(Object res) {
                  super.wrapUp(res);
                  // may have new collections
                  dbNode.structureComponent();
              }
  
              @Override
              public ButtonBase getButton() {
                  return button;
              }
          }.addJob();
      }
  
      public void downloadFile(final ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBObject query = ((DocBuilderField) getBoundUnit(Item.downloadQuery)).getDBObject();
          final String fname = getStringFieldValue(Item.downloadFileName);
          final String dpath = getStringFieldValue(Item.downloadFilePath);
          if (dpath.isEmpty()) {
              return;
          }
          final File dfile = new File(dpath);
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  GridFSDBFile dbfile = null;
                  if (query != null) {
                      dbfile = getGridFS().findOne(query);
                  } else {
                      dbfile = getGridFS().findOne(fname);
                  }
                  if (dbfile == null) {
                      throw new MongoException("GridFS cannot find file " + fname);
                  }
                  dbfile.writeTo(dfile);
                  return dbfile;
              }
  
              @Override
              public String getNS() {
                  return db.getName();
              }
  
              @Override
              public String getShortName() {
                  return "Download File";
              }
  
              @Override
              public DBObject getRoot(Object result) {
                  BasicDBObject obj = new BasicDBObject("filename", fname);
                  obj.put("query", query);
                  obj.put("path", dpath);
                  return obj;
              }
  
              @Override
              public ButtonBase getButton() {
                  return button;
              }
          }.addJob();
      }
  
      public void listFiles(ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBCollection col = db.getCollection("fs.files");
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  return col.find();
              }
  
              @Override
              public String getNS() {
                  return col.getName();
              }
  
              @Override
              public String getShortName() {
                  return "List Files";
              }
          }.addJob();
      }
  
      public void deleteFile(final ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBObject query = ((DocBuilderField) getBoundUnit(Item.downloadQuery)).getDBObject();
          final String fname = getStringFieldValue(Item.downloadFileName);
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  if (query != null) {
                      getGridFS().remove(query);
                  } else {
                      getGridFS().remove(fname);
                  }
                  return true;
              }
  
              @Override
              public String getNS() {
                  return db.getName();
              }
  
              @Override
              public String getShortName() {
                  return "Delete File";
              }
  
              @Override
              public DBObject getRoot(Object result) {
                  BasicDBObject obj = new BasicDBObject("filename", fname);
                  obj.put("query", query);
                  return obj;
              }
  
              @Override
              public ButtonBase getButton() {
                  return button;
              }
          }.addJob();
      }
  
      public void dropDatabase(ButtonBase button) {
          final DbNode node = getDbNode();
          final DB db = getDbNode().getDb();
          BasicDBObject cmd = new BasicDBObject("dropDatabase", 1);
          new DbJobCmd(db, cmd, null, node.getMongoNode(), null).addJob();
      }
  
      public void readWriteOptions(ButtonBase button) {
          final DB db = getDbNode().getDb();
          OptionDialog od = UMongo.instance.getGlobalStore().getOptionDialog();
          od.update(db.getOptions(), db.getWriteConcern(), db.getReadPreference());
          if (!od.show()) {
              return;
          }
          db.setOptions(od.getQueryOptions());
          db.setWriteConcern(od.getWriteConcern());
          db.setReadPreference(od.getReadPreference());
          refresh();
      }
  
      public void authenticate(final ButtonBase button) {
          final DbNode dbNode = getDbNode();
          final DB db = dbNode.getDb();
          final String user = getStringFieldValue(Item.authUser);
          final String pass = getStringFieldValue(Item.authPassword);
          new DbJob() {
  
              @Override
              public Object doRun() {
                  db.authenticateCommand(user, pass.toCharArray());
                  return null;
              }
  
              @Override
              public String getNS() {
                  return db.getName();
              }
  
              @Override
              public String getShortName() {
                  return "Auth";
              }
  
              @Override
              public void wrapUp(Object res) {
                  super.wrapUp(res);
                  if (dbNode.getDb().getName().equals("admin")) {
                      // now we can list dbs, refresh whole mongo
                      dbNode.getMongoNode().structureComponent();
                  } else {
                      dbNode.structureComponent();
                  }
              }
  
              @Override
              public ButtonBase getButton() {
                  return button;
              }
          }.addJob();
      }
  
      void refreshUserList() {
          ListArea list = (ListArea) getBoundUnit(Item.userList);
          final DB db = getDbNode().getDb();
          DBCursor cur = db.getCollection("system.users").find().sort(new BasicDBObject("user", 1));
          ArrayList users = new ArrayList();
          while (cur.hasNext()) {
              BasicDBObject user = (BasicDBObject) cur.next();
              users.add(user.getString("user"));
          }
  
          list.items = (String[]) users.toArray(new String[users.size()]);
          list.structureComponent();
      }
  
      public void manageUsers(ButtonBase button) {
          FormDialog dialog = (FormDialog) ((MenuItem) getBoundUnit(Item.manageUsers)).getDialog();
          refreshUserList();
          dialog.show();
      }
  
      public void addUser(ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBCollection col = db.getCollection("system.users");
  
          UserDialog ud = (UserDialog) getBoundUnit(Item.userDialog);
          ud.resetForNew();
          if (!ud.show()) {
              return;
          }
  
          final BasicDBObject newUser = ud.getUser(null);
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  return col.insert(newUser);
              }
  
              @Override
              public String getNS() {
                  return "system.users";
              }
  
              @Override
              public String getShortName() {
                  return "Add User";
              }
  
              @Override
              public void wrapUp(Object res) {
                  super.wrapUp(res);
                  refreshUserList();
              }
          }.addJob();
      }
  
      public void removeUser(ButtonBase button) {
          final DB db = getDbNode().getDb();
  
          final String user = getComponentStringFieldValue(Item.userList);
          if (user == null) {
              return;
          }
          if (!((ConfirmDialog) getBoundUnit(Item.userChange)).show()) {
              return;
          }
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  return db.removeUser(user);
              }
  
              @Override
              public String getNS() {
                  return "system.users";
              }
  
              @Override
              public String getShortName() {
                  return "Remove User";
              }
  
              @Override
              public void wrapUp(Object res) {
                  super.wrapUp(res);
                  refreshUserList();
              }
          }.addJob();
      }
  
      public void editUser(ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBCollection col = db.getCollection("system.users");
  
          final String user = getComponentStringFieldValue(Item.userList);
          if (user == null) {
              return;
          }
  
          final BasicDBObject userObj = (BasicDBObject) col.findOne(new BasicDBObject("user", user));
          UserDialog ud = (UserDialog) getBoundUnit(Item.userDialog);
          ud.resetForEdit(userObj);
          if (!ud.show()) {
              return;
          }
  
          final BasicDBObject newUser = ud.getUser(userObj);
  
          new DbJob() {
  
              @Override
              public Object doRun() throws IOException {
                  return col.save(newUser);
              }
  
              @Override
              public String getNS() {
                  return col.getName();
              }
  
              @Override
              public String getShortName() {
                  return "Edit User";
              }
  
              @Override
              public void wrapUp(Object res) {
                  super.wrapUp(res);
                  refreshUserList();
              }
          }.addJob();
  
      }
  
      public void createCollection(final ButtonBase button) {
          final DbNode node = getDbNode();
          final DB db = getDbNode().getDb();
          final String name = getStringFieldValue(Item.createCollName);
          final boolean capped = getBooleanFieldValue(Item.createCollCapped);
          final int size = getIntFieldValue(Item.createCollSize);
          final int count = getIntFieldValue(Item.createCollCount);
  //        final boolean autoIndexId = getBooleanFieldValue(Item.createCollAutoIndex);
          final boolean usePowerOf2Sizes = getBooleanFieldValue(Item.createCollUsePowerOf2Sizes);
  
          DBObject createCmd = new BasicDBObject("create", name);
          DBObject opt = new BasicDBObject("capped", capped);
          if (capped) {
              if (size > 0) {
                  opt.put("size", size);
              }
              if (count > 0) {
                  opt.put("max", count);
              }
          }
  
  //                // deprecated
  //                if (!autoIndexId) {
  //                    opt.put("autoIndexId", false);
  //                }
          // usePowerOf2Sizes uses flags name :(
          opt.put("flags", usePowerOf2Sizes ? 1 : 0);
  
          createCmd.putAll(opt);
          new DbJobCmd(db, createCmd, null, node, button).addJob();
      }
  
      public void getStats(ButtonBase button) {
          new DbJobCmd(getDbNode().getDb(), "dbstats").addJob();
      }
  
      public void dbHash(ButtonBase button) {
          new DbJobCmd(getDbNode().getDb(), "dbHash").addJob();
      }
  
      public void enableSharding(ButtonBase button) {
          MongoClient m = getDbNode().getMongoNode().getMongoClient();
          DB admin = m.getDB("admin");
          DBObject cmd = new BasicDBObject("enableSharding", getDbNode().getDb().getName());
          new DbJobCmd(admin, cmd, this, null).addJob();
      }
  
      public void profile(ButtonBase button) {
          DB db = getDbNode().getDb();
          int level = getIntFieldValue(Item.profileLevel);
          final DBObject cmd = new BasicDBObject("profile", level);
          if (level == 1) {
              int slow = getIntFieldValue(Item.profileSlowMS);
              if (slow > 0) {
                  cmd.put("slowms", slow);
              }
          }
          new DbJobCmd(db, cmd).addJob();
      }
  
      public void analyzeProfilingData(ButtonBase button) {
          new DbJob() {
  
              @Override
              public Object doRun() throws Exception {
                  DBCollection prof = getDbNode().getDb().getCollection("system.profile");
                  BasicDBObject out = new BasicDBObject();
                  CommandResult res = null;
                  DocumentDeserializer dd = null;
                  BasicDBObject aggCmd = null;
  
  
                  // response time by operation type
                  dd = new DocumentDeserializer(DocumentDeserializer.Format.JSON_SINGLE_DOC, null);
                  dd.setInputStream(DbPanel.class.getResourceAsStream("/json/profOperationType.json"));
                  aggCmd = (BasicDBObject) dd.readObject();
                  res = getDbNode().getDb().command(aggCmd);
                  out.put("byOperationType", res.get("result"));
                  dd.close();
  
                  // slowest by namespace
                  dd = new DocumentDeserializer(DocumentDeserializer.Format.JSON_SINGLE_DOC, null);
                  dd.setInputStream(DbPanel.class.getResourceAsStream("/json/profNamespace.json"));
                  aggCmd = (BasicDBObject) dd.readObject();
                  res = getDbNode().getDb().command(aggCmd);
                  out.put("byNamespace", res.get("result"));
                  dd.close();
  
                  // slowest by client
                  dd = new DocumentDeserializer(DocumentDeserializer.Format.JSON_SINGLE_DOC, null);
                  dd.setInputStream(DbPanel.class.getResourceAsStream("/json/profClient.json"));
                  aggCmd = (BasicDBObject) dd.readObject();
                  res = getDbNode().getDb().command(aggCmd);
                  out.put("byClient", res.get("result"));
                  dd.close();
  
                  // summary moved vs non-moved
                  dd = new DocumentDeserializer(DocumentDeserializer.Format.JSON_SINGLE_DOC, null);
                  dd.setInputStream(DbPanel.class.getResourceAsStream("/json/profMoved.json"));
                  aggCmd = (BasicDBObject) dd.readObject();
                  res = getDbNode().getDb().command(aggCmd);
                  out.put("movedVsNonMoved", res.get("result"));
                  dd.close();
  
                  // response time analysis
                  dd = new DocumentDeserializer(DocumentDeserializer.Format.JSON_SINGLE_DOC, null);
                  dd.setInputStream(DbPanel.class.getResourceAsStream("/json/profResponseTimeAnalysis.json"));
                  aggCmd = (BasicDBObject) dd.readObject();
                  res = getDbNode().getDb().command(aggCmd);
                  out.put("responseTimeAnalysis", res.get("result"));
                  dd.close();
  
                  return out;
              }
  
              @Override
              public String getNS() {
                  return getDbNode().getDb().getName();
              }
  
              @Override
              public String getShortName() {
                  return "Analyze Profiling Data";
              }
          }.addJob();
      }
  
      public void repair(ButtonBase button) {
          DB db = getDbNode().getDb();
          final DBObject cmd = new BasicDBObject("repairDatabase", 1);
          if (!UMongo.instance.getGlobalStore().confirmLockingOperation()) {
              return;
          }
          new DbJobCmd(db, cmd).addJob();
      }
  
      public void shardingInfo(ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DB config = db.getSisterDB("config");
          final DBCollection col = config.getCollection("databases");
          CollectionPanel.doFind(col, new BasicDBObject("_id", db.getName()));
      }
  
      public void movePrimary(ButtonBase button) {
          FormDialog dialog = (FormDialog) ((MenuItem) getBoundUnit(Item.movePrimary)).getDialog();
          ComboBox combo = (ComboBox) getBoundUnit(Item.mvpToShard);
          combo.value = 0;
          combo.items = getDbNode().getMongoNode().getShardNames();
          combo.structureComponent();
  
          if (!dialog.show()) {
              return;
          }
  
          MongoClient m = getDbNode().getMongoNode().getMongoClient();
          DB admin = m.getDB("admin");
          String shard = getStringFieldValue(Item.mvpToShard);
          DBObject cmd = new BasicDBObject("movePrimary", getDbNode().getDb().getName());
          cmd.put("to", shard);
          new DbJobCmd(admin, cmd, this, null).addJob();
      }
  
      public void findJSFunction(ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBCollection col = db.getCollection("system.js");
          final String name = getStringFieldValue(Item.findJSFunctionName);
          DBObject query = new BasicDBObject();
          if (name != null && !name.isEmpty()) {
              query.put("_id", name);
          }
          CollectionPanel.doFind(col, query);
      }
  
      public void addJSFunction(final ButtonBase button) {
          final DB db = getDbNode().getDb();
          final DBCollection col = db.getCollection("system.js");
          final String name = getStringFieldValue(Item.addJSFunctionName);
          final String code = getStringFieldValue(Item.addJSFunctionCode);
          CollectionPanel.doFind(col, new BasicDBObject("_id", name));
  
          new DbJob() {
  
              @Override
              public Object doRun() throws Exception {
                  DBObject obj = new BasicDBObject("_id", name);
                  obj.put("value", new Code(code));
                  return col.insert(obj);
              }
  
              @Override
              public String getNS() {
                  return col.getFullName();
              }
  
              @Override
              public String getShortName() {
                  return "Add JS Function";
              }
  
              @Override
              public ButtonBase getButton() {
                  return null;
              }
          }.addJob();
      }
  
      public void summarizeData(final ButtonBase button) {
          final DbNode dbnode = getDbNode();
          
          new DbJob() {
              @Override
              public Object doRun() throws IOException {
                  return dbnode.summarizeData();
              }
  
              @Override
              public String getNS() {
                  return dbnode.getDb().getName();
              }
  
              @Override
              public String getShortName() {
                  return "Summarize Data";
              }
          }.addJob();
  
      }
  
  }
  