/***************************************************************************
    copyright            : (C) 2001-2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License as  *
 *   published by the Free Software Foundation;                            *
 *                                                                         *
 ***************************************************************************/

#ifndef TELLICO_FIELD_H
#define TELLICO_FIELD_H

#include "datavectors.h"

#include <tqstringlist.h>
#include <tqstring.h>
#include <tqregexp.h>

namespace Tellico {
  namespace Data {

/**
 * The Field class encapsulates all the possible properties of a entry.
 *
 * A field can be one of eleven types. It has a name, a title, and a category,
 * along with some flags characterizing certain properties
 *
 * @author Robby Stephenson
 */
class Field : public TDEShared {
public:
  /**
   * The possible field types. A Line is represented by a KLineEdit,
   * a Para is a TQMultiLineEdit encompassing multiple lines, a Choice is
   * limited to set values shown in a KComboBox, and a Bool is either true
   * or not and is thus a TQCheckBox. A Number type is an integer, though it used
   * to be a Year. A ReadOnly is a hidden value. A URL is obvious, too.
   * A Table looks like a small spreadsheet with one column, and a Table2
   * type has two columns.  An Image points to a TQImage. A Dependent field
   * depends on the values of other attributes. A Date contains a date.
   *
   * Don't forget to change Field::typeMap().
   *
   * @see KLineEdit
   * @see TQTextEdit
   * @see KComboBox
   * @see TQCheckBox
   * @see TQTable
   **/
  enum Type {
    Undef      = 0,
    Line       = 1,
    Para       = 2,
    Choice     = 3,
    Bool       = 4,
    ReadOnly   = 5,
    Number     = 6,
    URL        = 7,
    Table      = 8,
    Table2     = 9, // deprecated in favor of property("columns")
    Image      = 10,
    Dependent  = 11,
    Date       = 12,
    // Michael Zimmermann used 13 for his Keyword field, so go ahead and skip it
    Rating     = 14 // similar to a Choice field, but allowed values are numbers only
    // if you add your own field type, best to start at a high number
    // say 100, to ensure uniqueness
  };
  typedef TQMap<Field::Type, TQString> FieldMap;

  /**
   * The field flags. The properties should be bit-wise OR'd together.
   *
   * @li AllowCompletion - Include a completion object in the lineedit.
   * @li AllowMultiple - Multiple values are allowed in one field and are
   *                     separated by a semi-colon (";").
   * @li AllowGrouped - Entries may be grouped by this field.
   * @li NoDelete - The user may not delete this field.
   */
  enum FieldFlags {
    AllowMultiple   = 1 << 0,   // allow multiple values, separated by a semi-colon
    AllowGrouped    = 1 << 1,   // this field can be used to group entries
    AllowCompletion = 1 << 2,   // allow auto-completion
    NoDelete        = 1 << 3    // don't allow user to delete this field
  };

  /**
   * The field formatting flags.
   *
   * @li FormatTitle - The field should be formatted as a title
   * @li FormatName - The field should be formatted as a personal name
   * @li FormatDate - The field should be formatted as a date.
   * @li FormatPlain - The field only be formatted with capitalization.
   * @li FormatNone - The field should not be formatted.
   */
  enum FormatFlag {
    FormatPlain     = 0,   // format plain, allows capitalization
    FormatTitle     = 1,   // format as a title, i.e. shift articles to end
    FormatName      = 2,   // format as a personal full name
    FormatDate      = 3,   // format as a date
    FormatNone      = 4    // no format, i.e. no capitalization allowed
  };

  /**
   * The constructor for all types except Choice. The default type is Line.
   * By default, the field category is set to "General", and should be modified
   * using the @ref setCategory() method.
   *
   * @param name The field name
   * @param title The field title
   * @param type The field type
   */
  Field(const TQString& name, const TQString& title, Type type = Line);
  /**
   * The constructor for Choice types attributes.
   * By default, the field category is set to "General", and should be modified
   * using the @ref setCategory() method.
   *
   * @param name The field name
   * @param title The field title
   * @param allowed The allowed values of the field
   */
  Field(const TQString& name, const TQString& title, const TQStringList& allowed);
  /**
   * The copy constructor
   */
  Field(const Field& field);
  /**
   * The assignment operator
   */
  Field& operator=(const Field& field);
  /**
   * Destructor
   */
  ~Field();

  /**
   * Returns the id of the field.
   *
   * @return The id
   */
  long id() const { return m_id; }
  /**
   * Returns the name of the field.
   *
   * @return The field name
   */
  const TQString& name() const { return m_name; }
  /**
   * Sets the name of the field. This should only be changed before the field is added
   * to a collection, i.e. before any entries use it, etc.
   *
   * @param name The field name
   */
  void setName(const TQString& name) { m_name = name; }
  /**
   * Returns the title of the field.
   *
   * @return The field title
   */
  const TQString& title() const { return m_title; }
  /**
   * Sets the title of the field.
   *
   * @param title The field title
   */
  void setTitle(const TQString& title);
  /**
   * Returns the category of the field.
   *
   * @return The field category
   */
  const TQString& category() const { return m_category; }
  /**
   * Sets the category of the field.
   *
   * @param category The field category
   */
  void setCategory(const TQString& category);
  /**
   * Returns the name of the field.
   *
   * @return The field name
   */
  const TQStringList& allowed() const { return m_allowed; }
  /**
   * Sets the allowed values of the field.
   *
   * @param allowed The allowed values
   */
  void setAllowed(const TQStringList& allowed) { m_allowed = allowed; }
  /**
   * Add a value to the allowed list
   *
   * @param value The value to allow
   */
  void addAllowed(const TQString& value);
  /**
   * Returns the type of the field.
   *
   * @return The field type
   */
  Type type() const { return m_type; }
  /**
   * Sets the type of the field. Be careful with this!
   *
   * @param type The field type
   */
  void setType(Type type);
  /**
   * Returns the flags for the field.
   *
   * @return The field flags
   */
  int flags() const { return m_flags; }
  /**
   * Sets the flags of the field. The value is
   * set to the argument, so old flags are effectively removed.
   *
   * @param flags The field flags
   */
  void setFlags(int flags);
  /**
   * Returns the formatting flag for the field.
   *
   * @return The format flag
   */
  FormatFlag formatFlag() const { return m_formatFlag; }
  /**
   * Sets the formatting flag of the field.
   *
   * @param flag The field flag
   */
  void setFormatFlag(FormatFlag flag);
  /**
   * Returns the description for the field.
   *
   * @return The field description
   */
  const TQString& description() const { return m_desc; }
  /**
   * Sets the description of the field.
   *
   * @param desc The field description
   */
  void setDescription(const TQString& desc) { m_desc = desc; }
  /**
   * Returns the default value for the field.
   *
   * @return The field default value
   */
  const TQString& defaultValue() const;
  /**
   * Sets the default value of the field.
   *
   * @param value The field default value
   */
  void setDefaultValue(const TQString& value);
  /**
   * Some attributes are always a category by themselves.
   *
   * @return Whether the field is th eonly member of its category
   */
  bool isSingleCategory() const;
  /**
   * Extends a field with an additional key and value property pair.
   *
   * @param key The property key
   * @param value The property value
   */
  void setProperty(const TQString& key, const TQString& value);
  /**
   * Sets all the extended properties. Any existing ones get erased.
   *
   * @param properties The property list
   */
  void setPropertyList(const StringMap& properties);
  /**
   * Return a property value.
   *
   * @param key The property key
   * @returnThe property value
   */
  const TQString& property(const TQString& key) const { return m_properties[key]; }
  /**
   * Return the list of properties.
   *
   * @return The property list
   */
  const StringMap& propertyList() const { return m_properties; }
  /**
   * Return a vector of all the fields on which the value of this field depends.
   * Returns an empty vector for non-Dpendent fields
   */
  FieldVec dependsOn(CollPtr coll) const;
  TQStringList dependsOn() const;

  /*************************** STATIC **********************************/

  /**
   * A wrapper method around all the format functions. The flags
   * determine which is called on the string.
   */
   static TQString format(const TQString& value, FormatFlag flag);
  /**
   * A convenience function to format a string as a title.
   * At the moment, this means that some articles such as "the" are placed
   * at the end of the title. If autoCapitalize() is true, the title is capitalized.
   *
   * @param title The string to be formatted
   */
  static TQString formatTitle(const TQString& title);
  /**
   * A convenience function to format a string as a personal name.
   * At the moment, this means that the name is split at the last white space,
   * and the last name is moved in front. If multiple=true, then the string
   * is split using a semi-colon (";"), and each string is formatted and then
   * joined back together. If autoCapitalize() is true, the names are capitalized.
   *
   * @param name The string to be formatted
   * @param multiple A boolean indicating if the string can contain multiple values
   */
  static TQString formatName(const TQString& name, bool multiple=true);
  /**
   * A convenience function to format a string as a date.
   *
   * @param date The string to be formatted
   */
  static TQString formatDate(const TQString& date);
  /**
   * Helper method to fix capitalization.
   *
   * @param str String to fix
   */
  static TQString capitalize(TQString str);
  /**
   * Return the key to be used for sorting titles
   */
  static TQString sortKeyTitle(const TQString& title);
  /**
   * Returns a mapping of the FieldType enum to translated titles for the types.
   */
  static FieldMap typeMap();
  /**
   * Returns a list of the titles of the field types.
   */
  static TQStringList typeTitles();
  /**
   * Splits a string into multiple values;
   *
   * @param string The string to be split
   */
  static TQStringList split(const TQString& string, bool allowEmpty);
  /**
   * Returns the delimiter used to split field values
   *
   * @return The delimeter regexp
   */
  static const TQRegExp& delimiter() { return s_delimiter; }
  /**
   * reset if the field is a rating field used for syntax version 7 and earlier */
  static void convertOldRating(Data::FieldPtr field);
  static void stripArticles(TQString& value);
  static void articlesUpdated();

private:
  /*
   * Gets the preferred ID of the collection. Currently, it just gets incremented as
   * new collections are created.
   */
  static long getID();

  long m_id;
  TQString m_name;
  TQString m_title;
  TQString m_category;
  TQString m_desc;
  Type m_type;
  TQStringList m_allowed;
  int m_flags;
  FormatFlag m_formatFlag;
  StringMap m_properties;

  static TQStringList s_articles;
  // need to remember articles with apostrophes for capitalization
  static TQStringList s_articlesApos;
  static TQRegExp s_delimiter;
};

  } // end namespace
} // end namespace
#endif
