/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */

package org.opends.guitools.controlpanel.task;

import static org.opends.messages.AdminToolMessages.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.swing.SwingUtilities;

import org.opends.guitools.controlpanel.datamodel.AbstractIndexDescriptor;
import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
import org.opends.guitools.controlpanel.datamodel.IndexDescriptor;
import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor;
import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
import org.opends.guitools.controlpanel.ui.ProgressDialog;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.messages.Message;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.client.ldap.LDAPManagementContext;
import org.opends.server.admin.std.client.LocalDBBackendCfgClient;
import org.opends.server.admin.std.client.RootCfgClient;
import org.opends.server.tools.RebuildIndex;
import org.opends.server.types.OpenDsException;
import org.opends.server.util.cli.CommandBuilder;

/**
 * The class that is used when a set of indexes must be rebuilt.
 *
 */
public class RebuildIndexTask extends IndexTask
{
  private SortedSet<AbstractIndexDescriptor> indexes =
    new TreeSet<AbstractIndexDescriptor>();

  /**
   * The indexes that must not be specified in the command-line.
   */
  public static final String[] INDEXES_NOT_TO_SPECIFY =
  {"id2children", "id2subtree"};

  /**
   * Constructor of the task.
   * @param info the control panel information.
   * @param dlg the progress dialog where the task progress will be displayed.
   * @param baseDNs the baseDNs corresponding to the indexes.
   * @param indexes the indexes.
   */
  public RebuildIndexTask(ControlPanelInfo info, ProgressDialog dlg,
      Collection<String> baseDNs, SortedSet<AbstractIndexDescriptor> indexes)
  {
    super(info, dlg, baseDNs);
    this.indexes.addAll(indexes);
  }

  /**
   * {@inheritDoc}
   */
  public Type getType()
  {
    return Type.REBUILD_INDEXES;
  }

  /**
   * {@inheritDoc}
   */
  public Message getTaskDescription()
  {
    if (baseDNs.size() == 1)
    {
      return INFO_CTRL_PANEL_REBUILD_INDEX_TASK_DESCRIPTION.get(
          baseDNs.iterator().next());
    }
    else
    {
      // Assume is in a backend
      return INFO_CTRL_PANEL_REBUILD_INDEX_TASK_DESCRIPTION.get(
          backendSet.iterator().next());
    }
  }

  /**
   * {@inheritDoc}
   */
  public boolean canLaunch(Task taskToBeLaunched,
      Collection<Message> incompatibilityReasons)
  {
    boolean canLaunch = true;
    if (state == State.RUNNING && runningOnSameServer(taskToBeLaunched))
    {
      // All the operations are incompatible if they apply to this
      // backend.
      Set<String> backends =
        new TreeSet<String>(taskToBeLaunched.getBackends());
      backends.retainAll(getBackends());
      if (backends.size() > 0)
      {
        incompatibilityReasons.add(getIncompatibilityMessage(this,
            taskToBeLaunched));
        canLaunch = false;
      }
    }
    return canLaunch;
  }

  /**
   * {@inheritDoc}
   */
  public void runTask()
  {
    state = State.RUNNING;
    lastException = null;
    try
    {
      boolean mustDisable = false;
      boolean mustEnable = false;
      boolean isLocal = getInfo().getServerDescriptor().isLocal();
      String backendName = backendSet.iterator().next();
      if (isServerRunning() && isLocal)
      {
        for (BackendDescriptor backend :
          getInfo().getServerDescriptor().getBackends())
        {
          if (backendName.equals(backend.getBackendID()))
          {
            mustDisable = backend.isEnabled();
            break;
          }
        }
      }

      if (mustDisable)
      {
        setBackendEnable(backendName, false);
        mustEnable = true;
      }

      for (final String baseDN : baseDNs)
      {
        ArrayList<String> arguments = getCommandLineArguments(baseDN);

        String[] args = new String[arguments.size()];

        arguments.toArray(args);

        final StringBuilder sb = new StringBuilder();
        sb.append(getCommandLinePath("rebuild-index"));
        Collection<String> displayArgs = getObfuscatedCommandLineArguments(
            getCommandLineArguments(baseDN));
        displayArgs.removeAll(getConfigCommandLineArguments());
        for (String arg : displayArgs)
        {
          sb.append(" "+CommandBuilder.escapeValue(arg));
        }
        sb.toString();
        final ProgressDialog progressDialog = getProgressDialog();

        SwingUtilities.invokeLater(new Runnable()
        {
          public void run()
          {
            progressDialog.appendProgressHtml(Utilities.applyFont(
                INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_REBUILD_INDEX.get(baseDN)+
                "<br><b>"+sb.toString()+"</b><br><br>",
                ColorAndFontConstants.progressFont));
          }
        });

        if (isLocal)
        {
          returnCode = executeCommandLine(getCommandLinePath("rebuild-index"),
              args);
        }
        else
        {
          returnCode = RebuildIndex.mainRebuildIndex(args, false,
              outPrintStream, errorPrintStream);
        }

        if (returnCode != 0)
        {
          break;
        }
      }
      if (mustEnable)
      {
        setBackendEnable(backendName, true);
      }

      if (returnCode != 0)
      {
        state = State.FINISHED_WITH_ERROR;
      }
      else
      {
        for (AbstractIndexDescriptor index : indexes)
        {
          getInfo().unregisterModifiedIndex(index);
        }

        state = State.FINISHED_SUCCESSFULLY;
      }
    }
    catch (Throwable t)
    {
      lastException = t;
      state = State.FINISHED_WITH_ERROR;
    }
  }

  /**
   * {@inheritDoc}
   */
  protected ArrayList<String> getCommandLineArguments()
  {
    return new ArrayList<String>();
  }

  /**
   * Returns the command line arguments required to rebuild the indexes
   * in the specified base DN.
   * @param baseDN the base DN.
   * @return the command line arguments required to rebuild the indexes
   * in the specified base DN.
   */
  protected ArrayList<String> getCommandLineArguments(String baseDN)
  {
    ArrayList<String> args = new ArrayList<String>();

    args.add("--baseDN");
    args.add(baseDN);

    if (rebuildAll())
    {
      args.add("--rebuildAll");
    }
    else
    {
      for (AbstractIndexDescriptor index : indexes)
      {
        args.add("--index");
        if (index instanceof VLVIndexDescriptor)
        {
          args.add(
              Utilities.getVLVNameInCommandLine((VLVIndexDescriptor)index));
        }
        else
        {
          args.add(index.getName());
        }
      }
    }

    boolean isLocal = getInfo().getServerDescriptor().isLocal();
    if (!isLocal)
    {
      args.addAll(getConnectionCommandLineArguments());
      args.addAll(getConfigCommandLineArguments());
    }

    return args;
  }

  /**
   * {@inheritDoc}
   */
  protected String getCommandLinePath()
  {
    return null;
  }

  /**
   * Enables a backend.
   * @param backendName the backend name.
   * @param enable whether to enable or disable the backend.
   * @throws OpenDsException if an error occurs.
   */
  private void setBackendEnable(final String backendName,
      final boolean enable) throws OpenDsException
      {
    ArrayList<String> args = new ArrayList<String>();
    args.add("set-backend-prop");
    args.add("--backend-name");
    args.add(backendName);
    args.add("--set");
    args.add("enabled:"+enable);

    args.addAll(getConnectionCommandLineArguments());
    args.add(getNoPropertiesFileArgument());
    args.add("--no-prompt");

    final StringBuilder sb = new StringBuilder();
    sb.append(getCommandLinePath("dsconfig"));
    for (String arg : args)
    {
      sb.append(" "+CommandBuilder.escapeValue(arg));
    }

    final ProgressDialog progressDialog = getProgressDialog();

    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        if (enable)
        {
          progressDialog.appendProgressHtml("<br><br>"+Utilities.applyFont(
              INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_ENABLE_BACKEND.get(
                  backendName)+"<br><b>"+sb.toString()+"</b><br><br>",
              ColorAndFontConstants.progressFont));
          progressDialog.appendProgressHtml(Utilities.getProgressWithPoints(
              INFO_CTRL_PANEL_ENABLING_BACKEND.get(backendName),
              ColorAndFontConstants.progressFont));
        }
        else
        {
          progressDialog.appendProgressHtml(Utilities.applyFont(
              INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DISABLE_BACKEND.get(
                  backendName)+"<br><b>"+sb.toString()+"</b><br><br>",
              ColorAndFontConstants.progressFont));
          progressDialog.appendProgressHtml(Utilities.getProgressWithPoints(
              INFO_CTRL_PANEL_DISABLING_BACKEND.get(backendName),
              ColorAndFontConstants.progressFont));
        }
      }
    });

    ManagementContext mCtx = LDAPManagementContext.createFromContext(
        JNDIDirContextAdaptor.adapt(getInfo().getDirContext()));
    RootCfgClient root = mCtx.getRootConfiguration();
    LocalDBBackendCfgClient backend =
      (LocalDBBackendCfgClient)root.getBackend(backendName);

    if (backend.isEnabled() != enable)
    {
      backend.setEnabled(enable);
      backend.commit();
    }

    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        progressDialog.appendProgressHtml(Utilities.getProgressDone(
            ColorAndFontConstants.progressFont)+
        "<br><br>");
      }
    });
  }

  private boolean rebuildAll()
  {
    boolean rebuildAll = true;
    Set<BackendDescriptor> backends = new HashSet<BackendDescriptor>();
    for (AbstractIndexDescriptor index : indexes)
    {
      backends.add(index.getBackend());
    }
    for (BackendDescriptor backend : backends)
    {
      Set<AbstractIndexDescriptor> allIndexes =
        new HashSet<AbstractIndexDescriptor>();
      allIndexes.addAll(backend.getIndexes());
      allIndexes.addAll(backend.getVLVIndexes());
      for (AbstractIndexDescriptor index : allIndexes)
      {
        if (!ignoreIndex(index))
        {
          boolean found = false;
          for (AbstractIndexDescriptor indexToRebuild : indexes)
          {
            if (indexToRebuild.equals(index))
            {
              found = true;
              break;
            }
          }
          if (!found)
          {
            rebuildAll = false;
            break;
          }
        }
      }
    }
    return rebuildAll;
  }

  private boolean ignoreIndex(AbstractIndexDescriptor index)
  {
    boolean ignoreIndex = false;
    if (index instanceof IndexDescriptor)
    {
      for (String name : INDEXES_NOT_TO_SPECIFY)
      {
        if (name.equalsIgnoreCase(index.getName()))
        {
          ignoreIndex = true;
          break;
        }
      }
    }
    return ignoreIndex;
  }
}
