/***************************************************************************
 *   Copyright (C) 2003 by S�astien Laot                                 *
 *   slaout@linux62.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#ifndef NOTE_H
#define NOTE_H

#include <tqstring.h>
#include <tqpixmap.h>
#include <kpixmap.h>
#include <tqdatetime.h>

#include "notecontent.h"
#include "tag.h"

class Basket;
class FilterData;
class HtmlExportData;

class NoteSelection;

class TQPainter;
class TQSimpleRichText;

/** Handle basket notes and groups!\n
  * After creation, the note is a group. You should create a NoteContent with this Note
  * as constructor parameter to transform it to a note with content. eg:
  * @code
  * Note *note = new Note(basket);   // note is a group!
  * new TextContent(note, fileName); // note is now a note with a text content!
  * new ColorContent(note, TQt::red); // Should never be done!!!!! the old Content should be deleted...
  * @endcode
  * @author S�astien Laot
  */
class Note
{
/// CONSTRUCTOR AND DESTRUCTOR:
  public:
	Note(Basket *parent);
	~Note();

/// DOUBLY LINKED LIST:
  private:
	Note *m_prev;
	Note *m_next;
  public:
	inline void  setNext(Note *next) { m_next = next; }
	inline void  setPrev(Note *prev) { m_prev = prev; }
	inline Note* next()              { return m_next; }
	inline Note* prev()              { return m_prev; }

/// GEOMETRY MANAGEMENT:
  private:
	int m_x;
	int m_y;
	int m_width;
	int m_height;
//	int m_minContentWidth;
  public:
	void setWidth(int width);
	void setWidthForceRelayout(int width);
	void setInitialHeight(int height) { m_height = height; } /// << Do NEVER use it unless you know what you do!
	void setX(int x);
	void setY(int y);
	void setXRecursivly(int x);
	void setYRecursivly(int y);
	inline int  x()      { return m_x;                }
	inline int  y()      { return m_y;                }
	inline int  width()  { return (isGroup() ? (isColumn() ? 0 : GROUP_WIDTH) : m_width); }
	inline int  height() { return m_height;           }
	inline int  bottom() { return m_y + m_height - 1; }
	TQRect rect();
	TQRect resizerRect();
	TQRect visibleRect();
	void relayoutAt(int x, int y, bool animate);
	int contentX();
	int minWidth();
	int minRight();
	void unsetWidth();
	void requestRelayout();
	void setHeight(int height) { m_height = height; } /// << DO NEVER USE IT!!! Only available when moving notes, groups should be recreated with the exact same state as before!

/// FREE AND COLUMN LAYOUTS MANAGEMENT:
  private:
	int m_groupWidth;
  public:
	int  groupWidth();
	void setGroupWidth(int width);
	int  rightLimit();
	int  finalRightLimit();
	bool isFree();
	bool isColumn();
	bool hasResizer();
	int  resizerHeight();

/// GROUPS MANAGEMENT:
  private:
	bool  m_isFolded;
	Note *m_firstChild;
	Note *m_parentNote;
  public:
	inline bool isGroup()                 { return m_content == 0L;  }
	inline bool isFolded()                { return m_isFolded;       }
	inline Note* firstChild()             { return m_firstChild;     }
	inline Note* parentNote()             { return m_parentNote;     }
	/*inline*/ bool showSubNotes();//            { return !m_isFolded || !m_collapseFinished; }
	inline void setParentNote(Note *note) { m_parentNote = note;     }
	inline void setFirstChild(Note *note) { m_firstChild = note;     }
	bool isShown();
	void  toggleFolded(bool animate);
	Note* noteAt(int x, int y);
	Note* firstRealChild();
	Note* lastRealChild();
	Note* lastChild();
	Note* lastSibling();
	int   yExpander();
	bool  isAfter(Note *note);
	bool  contains(Note *note);

/// NOTES VARIOUS PROPERTIES:       // CONTENT MANAGEMENT?
  private:
	Basket      *m_basket;
	NoteContent *m_content;
	TQDateTime    m_addedDate;
	TQDateTime    m_lastModificationDate;
  public:
	inline Basket*      basket()  { return m_basket;  }
	inline NoteContent* content() { return m_content; }
	inline void setAddedDate(const TQDateTime &dateTime)            { m_addedDate            = dateTime; }
	inline void setLastModificationDate(const TQDateTime &dateTime) { m_lastModificationDate = dateTime; }
	inline void setParentBasket(Basket *basket) { m_basket = basket; }
	TQDateTime addedDate()            { return m_addedDate;            }
	TQDateTime lastModificationDate() { return m_lastModificationDate; }
	TQString addedStringDate();
	TQString lastModificationStringDate();
	TQString toText(const TQString &cuttedFullPath);
	bool saveAgain();
	void deleteChilds();

  protected:
	void setContent(NoteContent *content);
	friend class NoteContent;
	friend class AnimationContent;

/// DRAWING:
  private:
	TQPixmap m_bufferedPixmap;
	KPixmap m_bufferedSelectionPixmap;
  public:
	void draw(TQPainter *painter, const TQRect &clipRect);
	void drawBufferOnScreen(TQPainter *painter, const TQPixmap &contentPixmap);
	static void getGradientColors(const TQColor &originalBackground, TQColor *colorTop, TQColor *colorBottom);
	static void drawExpander(TQPainter *painter, int x, int y, const TQColor &background, bool expand, Basket *basket);
	void drawHandle(   TQPainter *painter, int x, int y, int width, int height, const TQColor &background, const TQColor &foreground);
	void drawResizer(  TQPainter *painter, int x, int y, int width, int height, const TQColor &background, const TQColor &foreground, bool rounded);
	void drawRoundings(TQPainter *painter, int x, int y, int type, int width = 0, int height = 0);
	void unbufferizeAll();
	void bufferizeSelectionPixmap();
	inline void unbufferize()  { m_bufferedPixmap.resize(0, 0); m_bufferedSelectionPixmap.resize(0, 0); }
	inline bool isBufferized() { return !m_bufferedPixmap.isNull(); }
	void recomputeBlankRects(TQValueList<TQRect> &blankAreas);
	static void drawInactiveResizer(TQPainter *painter, int x, int y, int height, const TQColor &background, bool column);

/// VISIBLE AREAS COMPUTATION:
  private:
	TQValueList<TQRect> m_areas;
	bool              m_computedAreas;
	bool              m_onTop;
	void recomputeAreas();
	bool recomputeAreas(Note *note, bool noteIsAfterThis);
  public:
	void setOnTop(bool onTop);
	inline bool isOnTop() { return m_onTop; }
	bool isEditing();

/// MANAGE ANIMATION:
  private:
	int  m_deltaX;
	int  m_deltaY;
	int  m_deltaHeight;
	bool m_collapseFinished;
	bool m_expandingFinished;
	public:
	inline int  deltaX()          { return m_deltaX;                 }
	inline int  deltaY()          { return m_deltaY;                 }
	inline int  deltaHeight()     { return m_deltaHeight;            }
	inline int  finalX()          { return m_x + m_deltaX;           }
	inline int  finalY()          { return m_y + m_deltaY;           }
	inline int  finalHeight()     { return m_height + m_deltaHeight; }
	inline int  finalBottom()     { return finalY() + finalHeight() - 1; }
	inline void cancelAnimation() { m_deltaX = 0; m_deltaY = 0; m_deltaHeight = 0; }
	inline bool expandingOrCollapsing() { return !m_collapseFinished || !m_expandingFinished; }
	void addAnimation(int deltaX, int deltaY, int deltaHeight = 0);
	void setFinalPosition(int x, int y); /// << Convenient method for addAnimation()
	bool advance();
	void initAnimationLoad();
	void setRecursivelyUnder(Note *under, bool animate);

/// USER INTERACTION:
  public:
	enum Zone { None = 0,
	            Handle, TagsArrow, Custom0, /*CustomContent1, CustomContent2, */Content, Link,
	            TopInsert, TopGroup, BottomInsert, BottomGroup, BottomColumn,
	            Resizer,
	            Group, GroupExpander,
	            Emblem0 }; // Emblem0 should be at end, because we add 1 to get Emblem1, 2 to get Emblem2...
	inline void setHovered(bool hovered)  { m_hovered     = hovered; unbufferize(); }
	void        setHoveredZone(Zone zone);
	inline bool hovered()                 { return m_hovered;     }
	inline Zone hoveredZone()             { return m_hoveredZone; }
	Zone zoneAt(const TQPoint &pos, bool toAdd = false);
	TQRect zoneRect(Zone zone, const TQPoint &pos);
	void setCursor(Zone zone);
	TQString linkAt(const TQPoint &pos);
  private:
	bool m_hovered;
	Zone m_hoveredZone;

/// SELECTION:
  public:
	NoteSelection* selectedNotes();
	void setSelected(bool selected);
	void setSelectedRecursivly(bool selected);
	void invertSelectionRecursivly();
	void selectIn(const TQRect &rect, bool invertSelection, bool unselectOthers = true);
	void setFocused(bool focused);
	inline bool isFocused()  { return m_focused; }
	inline bool isSelected() { return m_selected; }
	bool allSelected();
	void resetWasInLastSelectionRect();
	void unselectAllBut(Note *toSelect);
	void invertSelectionOf(Note *toSelect);
	Note* theSelectedNote();
  private:
	bool m_focused;
	bool m_selected;
	bool m_wasInLastSelectionRect;

/// TAGS:
  private:
	State::List m_states;
	State       m_computedState;
	int         m_emblemsCount;
	bool        m_haveInvisibleTags;
  public:
	/*const */State::List& states() const;
	inline int emblemsCount() { return m_emblemsCount; }
	void addState(State *state, bool orReplace = true);
	void addTag(Tag *tag);
	void removeState(State *state);
	void removeTag(Tag *tag);
	void removeAllTags();
	void addTagToSelectedNotes(Tag *tag);
	void removeTagFromSelectedNotes(Tag *tag);
	void removeAllTagsFromSelectedNotes();
	void addStateToSelectedNotes(State *state, bool orReplace = true);
	void changeStateOfSelectedNotes(State *state);
	bool selectedNotesHaveTags();
	void inheritTagsOf(Note *note);
	bool hasTag(Tag *tag);
	bool hasState(State *state);
	State* stateOfTag(Tag *tag);
	State* stateForEmblemNumber(int number);
	bool stateForTagFromSelectedNotes(Tag *tag, State **state);
	void   recomputeStyle();
	void   recomputeAllStyles();
	bool   removedStates(const TQValueList<State*> &deletedStates);
	TQFont  font(); // Computed!
	TQColor backgroundColor(); // Computed!
	TQColor textColor(); // Computed!

/// FILTERING:
  private:
	bool m_matching;
  public:
	bool computeMatching(const FilterData &data);
	int  newFilter(const FilterData &data);
	bool matching() { return m_matching; }

/// ADDED:
  public:
	void deleteSelectedNotes(bool deleteFilesToo = true);
	int count();
	int countDirectChilds();

	TQString fullPath();
	Note* noteForFullPath(const TQString &path);

	void update();
	void linkLookChanged();

	void usedStates(TQValueList<State*> &states);

	void listUsedTags(TQValueList<Tag*> &list);


	Note* nextInStack();
	Note* prevInStack();
	Note* nextShownInStack();
	Note* prevShownInStack();

	Note* parentPrimaryNote(); // TODO: There are places in the code where this methods is hand-copied / hand-inlined instead of called.

	Note* firstSelected();
	Note* lastSelected();
	Note* selectedGroup();
	void groupIn(Note *group);

	bool tryExpandParent();
	bool tryFoldParent();

	int distanceOnLeftRight(Note *note, int side);
	int distanceOnTopBottom(Note *note, int side);

	bool convertTexts();

	void debug();

/// SPEED OPTIMIZATION
  public:
	void finishLazyLoad();

  public:
	// Values are provided here as info:
	// Please see Settings::setBigNotes() to know whats values are assigned.
	static int NOTE_MARGIN      /*= 2*/;
	static int INSERTION_HEIGHT /*= 5*/;
	static int EXPANDER_WIDTH   /*= 9*/;
	static int EXPANDER_HEIGHT  /*= 9*/;
	static int GROUP_WIDTH      /*= 2*NOTE_MARGIN + EXPANDER_WIDTH*/;
	static int HANDLE_WIDTH     /*= GROUP_WIDTH*/;
	static int RESIZER_WIDTH    /*= GROUP_WIDTH*/;
	static int TAG_ARROW_WIDTH  /*= 5*/;
	static int EMBLEM_SIZE      /*= 16*/;
	static int MIN_HEIGHT       /*= 2*NOTE_MARGIN + EMBLEM_SIZE*/;
};

/*class InsertionData
{
  public:
	enum Type { Free = 0, NoteRelative, ColumnBottom };
	Type type;

	InsertionData(int _x, int _y)
	 : type(Free),
	   x(_x), y(_y),
	   note(0), group(false), onTop(false),
	   column(0)
	{}
	int x;
	int y;

	InsertionData(Note *_note, bool _group, bool _onTop)
	 : type(NoteRelative),
	   x(0), y(0),
	   note(_note), group(_group), onTop(_onTop),
	   column(0)
	{}
	Note *note;
	bool group;
	bool onTop;

	InsertionData(Note *_column)
	 : type(NoteRelative),
	   x(0), y(0),
	   note(0), group(false), onTop(false),
	   column(_column)
	{}
	Note *column;
};*/

#endif // NOTE_H