/*
 * 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 2009 Sun Microsystems, Inc.
 *      Portions Copyright 2014-2015 ForgeRock AS
 */
package org.opends.guitools.controlpanel.datamodel;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

import org.forgerock.i18n.LocalizableMessage;

import static org.opends.guitools.controlpanel.util.Utilities.*;
import static org.opends.messages.AdminToolMessages.*;
import static org.opends.server.util.CollectionUtils.*;

/** The table model used to display all the database monitoring information. */
public class DatabaseMonitoringTableModel extends SortableTableModel implements Comparator<BackendDescriptor>
{
  private static final long serialVersionUID = 548035716525600536L;
  private Set<BackendDescriptor> data = new HashSet<>();
  private ArrayList<String[]> dataArray = new ArrayList<>();

  private String[] columnNames = {};
  private LocalizableMessage NO_VALUE_SET = INFO_CTRL_PANEL_NO_MONITORING_VALUE.get();
  private LocalizableMessage NOT_IMPLEMENTED = INFO_CTRL_PANEL_NOT_IMPLEMENTED.get();

  /** The fields to be displayed. */
  private LinkedHashSet<String> attributes = new LinkedHashSet<>();
  /** The sort column of the table. */
  private int sortColumn;
  /** Whether the sorting is ascending or descending. */
  private boolean sortAscending = true;

  /**
   * Sets the data for this table model.
   * @param newData the data for this table model.
   */
  public void setData(Set<BackendDescriptor> newData)
  {
    if (!newData.equals(data))
    {
      data.clear();
      data.addAll(newData);
      updateDataArray();
      fireTableDataChanged();
    }
  }

  /**
   * Updates the table model contents and sorts its contents depending on the
   * sort options set by the user.
   */
  @Override
  public void forceResort()
  {
    updateDataArray();
    fireTableDataChanged();
  }

  /**
   * Updates the table model contents, sorts its contents depending on the
   * sort options set by the user and updates the column structure.
   */
  public void forceDataStructureChange()
  {
    updateDataArray();
    fireTableStructureChanged();
    fireTableDataChanged();
  }

  /** {@inheritDoc} */
  @Override
  public int getColumnCount()
  {
    return columnNames.length;
  }

  /** {@inheritDoc} */
  @Override
  public int getRowCount()
  {
    return dataArray.size();
  }

  /** {@inheritDoc} */
  @Override
  public Object getValueAt(int row, int col)
  {
    return dataArray.get(row)[col];
  }

  /** {@inheritDoc} */
  @Override
  public String getColumnName(int col) {
    return columnNames[col];
  }

  /** {@inheritDoc} */
  @Override
  public int compare(BackendDescriptor desc1, BackendDescriptor desc2)
  {
    CustomSearchResult monitor1 = desc1.getMonitoringEntry();
    CustomSearchResult monitor2 = desc2.getMonitoringEntry();

    ArrayList<Integer> possibleResults = newArrayList(getName(desc1).compareTo(getName(desc2)));
    computeMonitoringPossibleResults(monitor1, monitor2, possibleResults, attributes);

    int result = possibleResults.get(getSortColumn());
    if (result == 0)
    {
      result = getFirstNonZero(possibleResults);
    }
    if (!isSortAscending())
    {
      result = -result;
    }
    return result;
  }

  private int getFirstNonZero(ArrayList<Integer> possibleResults)
  {
    for (int i : possibleResults)
    {
      if (i != 0)
      {
        return i;
      }
    }
    return 0;
  }

  /**
   * Returns whether the sort is ascending or descending.
   * @return <CODE>true</CODE> if the sort is ascending and <CODE>false</CODE>
   * otherwise.
   */
  @Override
  public boolean isSortAscending()
  {
    return sortAscending;
  }

  /**
   * Sets whether to sort ascending of descending.
   * @param sortAscending whether to sort ascending or descending.
   */
  @Override
  public void setSortAscending(boolean sortAscending)
  {
    this.sortAscending = sortAscending;
  }

  /**
   * Returns the column index used to sort.
   * @return the column index used to sort.
   */
  @Override
  public int getSortColumn()
  {
    return sortColumn;
  }

  /**
   * Sets the column index used to sort.
   * @param sortColumn column index used to sort..
   */
  @Override
  public void setSortColumn(int sortColumn)
  {
    this.sortColumn = sortColumn;
  }

  /**
   * Returns the fields displayed by this table model.
   * @return the fields displayed by this table model.
   */
  public Collection<String> getAttributes()
  {
    return attributes;
  }

  /**
   * Sets the fields displayed by this table model.
   * @param fields the statistic fields displayed by this table model.
   */
  public void setAttributes(LinkedHashSet<String> fields)
  {
    this.attributes.clear();
    this.attributes.addAll(fields);
    columnNames = new String[fields.size() + 1];
    columnNames[0] = INFO_CTRL_PANEL_DB_HEADER.get().toString();
    int i = 1;
    for (String field : fields)
    {
      columnNames[i] = field;
      i++;
    }
  }

  /**
   * Updates the array data.  This includes resorting it.
   */
  private void updateDataArray()
  {
    TreeSet<BackendDescriptor> sortedSet = new TreeSet<>(this);
    sortedSet.addAll(data);
    dataArray.clear();
    for (BackendDescriptor ach : sortedSet)
    {
      String[] s = getLine(ach);
      dataArray.add(s);
    }

    // Add the total: always at the end

    String[] line = new String[attributes.size() + 1];
    line[0] = "<html><b>" + INFO_CTRL_PANEL_TOTAL_LABEL.get() + "</b>";
    for (int i=1; i<line.length; i++)
    {
      boolean valueSet = false;
      boolean notImplemented = false;
      long totalValue = 0;
      for (String[] l : dataArray)
      {
        String value = l[i];
        try
        {
          long v = Long.parseLong(value);
          totalValue += v;
          valueSet = true;
        }
        catch (Throwable t)
        {
          try
          {
            double v = Double.parseDouble(value);
            totalValue += v;
            valueSet = true;
          }
          catch (Throwable t2)
          {
            notImplemented = NOT_IMPLEMENTED.toString().equals(value);
          }
        }
      }
      if (notImplemented)
      {
        line[i] = NOT_IMPLEMENTED.toString();
      }
      else if (valueSet)
      {
        line[i] = String.valueOf(totalValue);
      }
      else
      {
        line[i] = NO_VALUE_SET.toString();
      }
    }
    dataArray.add(line);
  }

  /**
   * Returns the label to be used for the provided backend.
   * @param backend the backend.
   * @return the label to be used for the provided backend.
   */
  protected String getName(BackendDescriptor backend)
  {
    return backend.getBackendID();
  }

  /**
   * Returns the monitoring entry associated with the provided backend.
   * @param backend the backend.
   * @return the monitoring entry associated with the provided backend.  Returns
   * <CODE>null</CODE> if there is no monitoring entry associated.
   */
  protected CustomSearchResult getMonitoringEntry(BackendDescriptor backend)
  {
    return backend.getMonitoringEntry();
  }

  private String[] getLine(BackendDescriptor backend)
  {
    String[] line = new String[attributes.size() + 1];
    line[0] = getName(backend);
    int i = 1;
    CustomSearchResult monitoringEntry = getMonitoringEntry(backend);
    for (String attr : attributes)
    {
      String o = getFirstValueAsString(monitoringEntry, attr);
      if (o != null)
      {
        line[i] = o;
      }
      else
      {
        line[i] = NO_VALUE_SET.toString();
      }
      i++;
    }
    return line;
  }

}
