/*
 * 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 legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * 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 legal-notices/CDDLv1_0.txt.
 * 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-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.guitools.controlpanel.ui;

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

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;

import javax.swing.SwingUtilities;

import org.forgerock.i18n.LocalizableMessage;
import org.opends.guitools.controlpanel.browser.BrowserController;
import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
import org.opends.guitools.controlpanel.task.NewEntryTask;
import org.opends.guitools.controlpanel.task.Task;
import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
import org.opends.guitools.controlpanel.util.BackgroundTask;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.util.LDIFException;
import org.opends.server.util.LDIFReader;

/**
 * Abstract class used to re-factor some code among the different panels that
 * are used to create a new entry.
 */
public abstract class AbstractNewEntryPanel extends StatusGenericPanel
{
  private static final long serialVersionUID = 6894546787832469213L;

  /** The parent node that was selected when the user clicked on the new entry action. */
  protected BasicNode parentNode;
  /** The browser controller. */
  protected BrowserController controller;

  /**
   * Sets the parent and the browser controller for this panel.
   * @param parentNode the selected parent node (or <CODE>null</CODE> if no
   * parent node was selected).
   * @param controller the browser controller.
   */
  public void setParent(BasicNode parentNode, BrowserController controller)
  {
    this.parentNode = parentNode;
    this.controller = controller;
  }

  /**
   * Returns the title for the progress dialog.
   * @return the title for the progress dialog.
   */
  protected abstract LocalizableMessage getProgressDialogTitle();
  /**
   * Returns the LDIF representation of the new entry.
   * @return the LDIF representation of the new entry.
   */
  protected abstract String getLDIF();

  /**
   * Updates the list of errors by checking the syntax of the entry.
   * @param errors the list of errors that must be updated.
   */
  protected abstract void checkSyntax(ArrayList<LocalizableMessage> errors);

  /**
   * Returns <CODE>true</CODE> if the syntax of the entry must be checked in
   * the background and <CODE>false</CODE> otherwise.
   * @return <CODE>true</CODE> if the syntax of the entry must be checked in
   * the background and <CODE>false</CODE> otherwise.
   */
  protected boolean checkSyntaxBackground()
  {
    return false;
  }

  /** {@inheritDoc} */
  public void okClicked()
  {
    final ArrayList<LocalizableMessage> errors = new ArrayList<>();

    if (checkSyntaxBackground())
    {
      BackgroundTask<Void> worker = new BackgroundTask<Void>()
      {
        public Void processBackgroundTask()
        {
          try
          {
            Thread.sleep(2000);
          }
          catch (Throwable t)
          {
          }
          checkSyntax(errors);
          return null;
        }
        public void backgroundTaskCompleted(Void returnValue, Throwable t)
        {
          if (t != null)
          {
            errors.add(ERR_CTRL_PANEL_UNEXPECTED_DETAILS.get(t));
          }
          displayMainPanel();
          setEnabledCancel(true);
          setEnabledOK(true);
          handleErrorsAndLaunchTask(errors);
        }
      };
      displayMessage(INFO_CTRL_PANEL_CHECKING_SUMMARY.get());
      setEnabledCancel(false);
      setEnabledOK(false);
      worker.startBackgroundTask();
    }
    else
    {
      checkSyntax(errors);
      handleErrorsAndLaunchTask(errors);
    }
  }

  /**
   * Checks that there are not errors in the list and launches a new entry
   * task.
   * @param errors the list of errors.
   */
  private void handleErrorsAndLaunchTask(ArrayList<LocalizableMessage> errors)
  {
    Entry entry = null;
    if (errors.isEmpty())
    {
      try
      {
        entry = getEntry();
      }
      catch (Throwable t)
      {
        // Unexpected error: getEntry() should work after calling checkSyntax
        throw new RuntimeException("Unexpected error: "+t, t);
      }
      String dn = entry.getName().toString();
      // Checking for the existence of an entry is fast enough so we can do
      // it on the event thread.
      if (entryExists(dn))
      {
        errors.add(ERR_CTRL_PANEL_ENTRY_ALREADY_EXISTS.get(dn));
      }
    }
    if (errors.isEmpty())
    {
      final ProgressDialog dlg = new ProgressDialog(
          Utilities.createFrame(), Utilities.getParentDialog(this),
          getProgressDialogTitle(), getInfo());
      try
      {
        NewEntryTask newTask =
          new NewEntryTask(getInfo(), dlg, entry, getLDIF(),
              parentNode, controller);
        for (Task task : getInfo().getTasks())
        {
          task.canLaunch(newTask, errors);
        }
        if (errors.isEmpty())
        {
          launchOperation(newTask,
              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUMMARY.get(),
              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUCCESSFUL_SUMMARY.get(),
              INFO_CTRL_PANEL_CREATING_NEW_ENTRY_SUCCESSFUL_DETAILS.get(),
              ERR_CTRL_PANEL_CREATING_NEW_ENTRY_ERROR_SUMMARY.get(),
              ERR_CTRL_PANEL_CREATING_NEW_ENTRY_ERROR_DETAILS.get(),
              null,
              dlg);
          dlg.setVisible(true);
          Utilities.getParentDialog(this).setVisible(false);
          SwingUtilities.invokeLater(new Runnable()
          {
            public void run()
            {
              dlg.toFront();
            }
          });
        }
      }
      catch (Throwable t)
      {
        // Unexpected error: getEntry() should work after calling checkSyntax
        throw new RuntimeException("Unexpected error: "+t, t);
      }
    }
    if (!errors.isEmpty())
    {
      displayErrorDialog(errors);
    }
  }

  /** {@inheritDoc} */
  public void configurationChanged(ConfigurationChangeEvent ev)
  {
    updateErrorPaneIfServerRunningAndAuthRequired(ev.getNewDescriptor(),
        INFO_CTRL_PANEL_NEW_ENTRY_REQUIRES_SERVER_RUNNING.get(),
        INFO_CTRL_PANEL_NEW_ENTRY_REQUIRES_AUTHENTICATION.get());
  }

  /**
   * Returns the entry object representing what the user provided as data.
   * @return the entry object representing what the user provided as data.
   * @throws LDIFException if there is an error with the LDIF syntax.
   * @throws IOException if there is an error creating the internal stream.
   */
  protected Entry getEntry() throws LDIFException, IOException
  {
    LDIFImportConfig ldifImportConfig = null;
    try
    {
      String ldif = getLDIF();
      if (ldif.trim().length() == 0)
      {
        throw new LDIFException(ERR_LDIF_REPRESENTATION_REQUIRED.get());
      }

      ldifImportConfig = new LDIFImportConfig(new StringReader(ldif));
      LDIFReader reader = new LDIFReader(ldifImportConfig);
      Entry entry = reader.readEntry(checkSchema());
      if (entry == null)
      {
        throw new LDIFException(ERR_LDIF_REPRESENTATION_REQUIRED.get());
      }
      if (entry.getObjectClasses().isEmpty())
      {
        throw new LDIFException(ERR_OBJECTCLASS_FOR_ENTRY_REQUIRED.get());
      }
      return entry;
    }
    finally
    {
      if (ldifImportConfig != null)
      {
        ldifImportConfig.close();
      }
    }
  }

  /**
   * Returns <CODE>true</CODE> if the schema must be checked and
   * <CODE>false</CODE> otherwise.
   * @return <CODE>true</CODE> if the schema must be checked and
   * <CODE>false</CODE> otherwise.
   */
  protected boolean checkSchema()
  {
    return getInfo().getServerDescriptor().isSchemaEnabled();
  }
}
