/***************************************************************************
    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_ENTRY_H
#define TELLICO_ENTRY_H

#include "datavectors.h"

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

#include <functional>

namespace Tellico {
  namespace Data {
    class Collection;

/**
 * The EntryGroup is simply a vector of entries which knows the name of its group,
 * and the name of the field to which that group belongs.
 *
 * An example for a book collection would be a group of books, all written by
 * David Weber. The @ref groupName() would be "Weber, David" and the
 * @ref fieldName() would be "author".
 *
 * It's a TQObject because EntryGroupItem holds a TQGuardedPtr
 *
 * @author Robby Stephenson
 */
class EntryGroup : public TQObject, public EntryVec {
Q_OBJECT
  TQ_OBJECT

public:
  EntryGroup(const TQString& group, const TQString& field);
  ~EntryGroup();

  const TQString& groupName() const { return m_group; }
  const TQString& fieldName() const { return m_field; }

private:
  TQString m_group;
  TQString m_field;
};

/**
 * The Entry class represents a book, a CD, or whatever is the basic entity
 * in the collection.
 *
 * Each Entry object has a set of field values, such as title, artist, or format,
 * and must belong to a collection. A unique id number identifies each entry.
 *
 * @see Field
 *
 * @author Robby Stephenson
 */
class Entry : public KShared {

public:
  /**
   * The constructor, which automatically sets the id to the current number
   * of entries in the collection.
   *
   * @param coll A pointer to the parent collection object
   */
  Entry(CollPtr coll);
  Entry(CollPtr coll, int id);
  /**
   * The copy constructor, needed since the id must be different.
   */
  Entry(const Entry& entry);
  /**
   * The assignment operator is overloaded, since the id must be different.
   */
  Entry& operator=(const Entry& other);
  /**
   * two entries are equal if all their field values are equal, except for
   * file catalogs which match on the url only
   */
  bool operator==(const Entry& other);

  ~Entry();

  /**
   * Every entry has a title.
   *
   * @return The entry title
   */
  TQString title() const;
  /**
   * Returns the value of the field with a given key name. If the key doesn't
   * exist, the method returns @ref TQString().
   *
   * @param fieldName The field name
   * @param formatted Whether to format the field or not.
   * @return The value of the field
   */
  TQString field(const TQString& fieldName, bool formatted=false) const;
  TQString field(Data::FieldPtr field, bool formatted=false) const;
  /**
   * Returns the formatted value of the field with a given key name. If the
   * key doesn't exist, the method returns @ref TQString(). The value is cached,
   * so the first time the value is requested, @ref Field::format is called.
   * The second time, that lookup isn't necessary.
   *
   * @param fieldName The field name
   * @return The formatted value of the field
   */
  TQString formattedField(const TQString& fieldName) const;
  TQString formattedField(Data::FieldPtr field) const;
  /**
   * Splits a field value. This is faster than calling Data::Field::split() since
   * a regexp is not used, only a string.
   *
   * @param field The field name
   * @param format Whether to format the values or not
   * @return The list of field values
   */
  TQStringList fields(const TQString& fieldName, bool formatted) const;
  TQStringList fields(Data::FieldPtr field, bool formatted) const;
  /**
   * Sets the value of an field for the entry. The method first verifies that
   * the value is allowed for that particular key.
   *
   * @param fieldName The name of the field
   * @param value The value of the field
   * @return A boolean indicating whether or not the field was successfully set
   */
  bool setField(const TQString& fieldName, const TQString& value);
  bool setField(Data::FieldPtr field, const TQString& value);
  /**
   * Returns a pointer to the parent collection of the entry.
   *
   * @return The collection pointer
   */
  CollPtr collection() const;
  /**
   * Changes the collection owner of the entry
   */
  void setCollection(CollPtr coll);
  /**
   * Returns the id of the entry
   *
   * @return The id
   */
  long id() const { return m_id; }
  void setId(long id) { m_id = id; }
  /**
   * Adds the entry to a group. The group list within the entry is updated
   * and the entry is added to the group.
   *
   * @param group The group
   * @return a bool indicating if it was successfully added. If the entry was already
   * in the group, the return value is false
   */
  bool addToGroup(EntryGroup* group);
  /**
   * Removes the entry from a group. The group list within the entry is updated
   * and the entry is removed from the group.
   *
   * @param group The group
   * @return a bool indicating if the group was successfully removed
   */
  bool removeFromGroup(EntryGroup* group);
  void clearGroups();
  /**
   * Returns a list of the groups to which the entry belongs
   *
   * @return The list of groups
   */
  const PtrVector<EntryGroup>& groups() const { return m_groups; }
  /**
   * Returns a list containing the names of the groups for
   * a certain field to which the entry belongs
   *
   * @param fieldName The name of the field
   * @return The list of names
   */
  TQStringList groupNamesByFieldName(const TQString& fieldName) const;
  /**
   * Returns a list of all the field values contained in the entry.
   *
   * @return The list of field values
   */
  TQStringList fieldValues() const { return m_fields.values(); }
  /**
   * Returns a list of all the formatted field values contained in the entry.
   *
   * @return The list of field values
   */
  TQStringList formattedFieldValues() const { return m_formattedFields.values(); }
  /**
   * Returns a boolean indicating if the entry's parent collection recognizes
   * it existence, that is, the parent collection has this entry in its list.
   *
   * @return Whether the entry is owned or not
   */
  bool isOwned();
  /**
   * Removes the formatted value of the field from the map. This should be used when
   * the field's format flag has changed.
   *
   * @param name The name of the field that changed. TQString() means invalidate all fields.
   */
  void invalidateFormattedFieldValue(const TQString& name=TQString());

  static int compareValues(EntryPtr entry1, EntryPtr entry2, FieldPtr field);
  static int compareValues(EntryPtr entry1, EntryPtr entry2, const TQString& field, ConstCollPtr coll);

  /**
   * Construct the derived valued for an field. The format string should be
   * of the form "%{name1} %{name2}" where the names are replaced by the value
   * of that field for the entry. Whether or not formatting is done on the
   * strings themselves should be taken into account.
   *
   * @param formatString The format string
   * @param formatted Whether the inserted values should be formatted.
   * @return The constructed field value
   */
  static TQString dependentValue(ConstEntryPtr e, const TQString& formatString, bool formatted);

private:
  CollPtr m_coll;
  long m_id;
  StringMap m_fields;
  mutable StringMap m_formattedFields;
  PtrVector<EntryGroup> m_groups;
};

class EntryCmp : public std::binary_function<EntryPtr, EntryPtr, bool> {

public:
  EntryCmp(const TQString& field) : m_field(field) {}

  bool operator()(EntryPtr e1, EntryPtr e2) const {
    return e1->field(m_field) < e2->field(m_field);
  }

private:
  TQString m_field;
};

  } // end namespace
} // end namespace

#endif
