/*
 * 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 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;



import java.text.NumberFormat;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;

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

import org.opends.server.admin.BooleanPropertyDefinition;
import org.opends.server.admin.DurationPropertyDefinition;
import org.opends.server.admin.DurationUnit;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyValueVisitor;
import org.opends.server.admin.SizePropertyDefinition;
import org.opends.server.admin.SizeUnit;



/**
 * A class responsible for displaying property values. This class
 * takes care of using locale specific formatting rules.
 */
final class PropertyValuePrinter {

  /**
   * Perform property type specific print formatting.
   */
  private static class MyPropertyValueVisitor extends
      PropertyValueVisitor<Message, Void> {

    // The requested size unit (null if the property's unit should be
    // used).
    private final SizeUnit sizeUnit;

    // The requested time unit (null if the property's unit should be
    // used).
    private final DurationUnit timeUnit;

    // Whether or not values should be displayed in a script-friendly
    // manner.
    private final boolean isScriptFriendly;

    // The formatter to use for numeric values.
    private final NumberFormat numberFormat;



    // Private constructor.
    private MyPropertyValueVisitor(SizeUnit sizeUnit, DurationUnit timeUnit,
        boolean isScriptFriendly) {
      this.sizeUnit = sizeUnit;
      this.timeUnit = timeUnit;
      this.isScriptFriendly = isScriptFriendly;

      this.numberFormat = NumberFormat.getNumberInstance();
      if (this.isScriptFriendly) {
        numberFormat.setGroupingUsed(false);
        numberFormat.setMaximumFractionDigits(2);
      } else {
        numberFormat.setGroupingUsed(true);
        numberFormat.setMaximumFractionDigits(2);
      }
    }



    /**
     * {@inheritDoc}
     */
    @Override
    public Message visitBoolean(BooleanPropertyDefinition pd, Boolean v,
        Void p) {
      if (v == false) {
        return INFO_VALUE_FALSE.get();
      } else {
        return INFO_VALUE_TRUE.get();
      }
    }



    /**
     * {@inheritDoc}
     */
    @Override
    public Message visitDuration(DurationPropertyDefinition pd, Long v,
        Void p) {
      if (pd.getUpperLimit() == null && (v < 0 || v == Long.MAX_VALUE)) {
        return INFO_VALUE_UNLIMITED.get();
      }

      MessageBuilder builder = new MessageBuilder();
      long ms = pd.getBaseUnit().toMilliSeconds(v);

      if (timeUnit == null && !isScriptFriendly && ms != 0) {
        // Use human-readable string representation by default.
        builder.append(DurationUnit.toString(ms));
      } else {
        // Use either the specified unit or the property definition's
        // base unit.
        DurationUnit unit = timeUnit;
        if (unit == null) {
          unit = pd.getBaseUnit();
        }

        builder.append(numberFormat.format(unit.fromMilliSeconds(ms)));
        builder.append(' ');
        builder.append(unit.getShortName());
      }

      return builder.toMessage();
    }



    /**
     * {@inheritDoc}
     */
    @Override
    public Message visitSize(SizePropertyDefinition pd, Long v, Void p) {
      if (pd.isAllowUnlimited() && v < 0) {
        return INFO_VALUE_UNLIMITED.get();
      }

      SizeUnit unit = sizeUnit;
      if (unit == null) {
        if (isScriptFriendly) {
          // Assume users want a more accurate value.
          unit = SizeUnit.getBestFitUnitExact(v);
        } else {
          unit = SizeUnit.getBestFitUnit(v);
        }
      }

      MessageBuilder builder = new MessageBuilder();
      builder.append(numberFormat.format(unit.fromBytes(v)));
      builder.append(' ');
      builder.append(unit.getShortName());

      return builder.toMessage();
    }



    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Message visitUnknown(PropertyDefinition<T> pd, T v, Void p) {
      // For all other property definition types the default encoding
      // will do.
      String s = pd.encodeValue(v);
      if (isScriptFriendly) {
        return Message.raw("%s", s);
      } else if (s.trim().length() == 0 || s.contains(",")) {
        // Quote empty strings or strings containing commas
        // non-scripting mode.
        return Message.raw("\"%s\"", s);
      } else {
        return Message.raw("%s", s);
      }
    }

  }

  // The property value printer implementation.
  private final MyPropertyValueVisitor pimpl;



  /**
   * Creates a new property value printer.
   *
   * @param sizeUnit
   *          The user requested size unit or <code>null</code> if
   *          best-fit.
   * @param timeUnit
   *          The user requested time unit or <code>null</code> if
   *          best-fit.
   * @param isScriptFriendly
   *          If values should be displayed in a script friendly
   *          manner.
   */
  public PropertyValuePrinter(SizeUnit sizeUnit, DurationUnit timeUnit,
      boolean isScriptFriendly) {
    this.pimpl = new MyPropertyValueVisitor(sizeUnit, timeUnit,
        isScriptFriendly);
  }



  /**
   * Print a property value according to the rules of this property
   * value printer.
   *
   * @param <T>
   *          The type of property value.
   * @param pd
   *          The property definition.
   * @param value
   *          The property value.
   * @return Returns the string representation of the property value
   *         encoded according to the rules of this property value
   *         printer.
   */
  public <T> Message print(PropertyDefinition<T> pd, T value) {
    return pd.accept(pimpl, value, null);
  }
}
