/***************************************************************************
                          skycomponent.h  -  K Desktop Planetarium
                             -------------------
    begin                : 2005/07/08
    copyright            : (C) 2005 by Thomas Kabelmann
    email                : thomas.kabelmann@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef SKYCOMPONENT_H
#define SKYCOMPONENT_H


#include <QDebug>
#include "typedef.h"

class QString;

class KSNumbers;
class SkyObject;
class SkyPoint;
class SkyComposite;
class SkyPainter;

/**
 * @class SkyComponent
 SkyComponent represents an object on the sky map. This may be a  star, a planet or an imaginary line like the equator.
 The SkyComponent uses the composite design pattern.  So if a new object type is needed, it has to be derived from this class and
 must be added into the hierarchy of components.
  \section Overview Overview
  KStars utilizes the Composite/Component design pattern to store all elements
  that must be drawn on the SkyMap.  In this design pattern, elements to
  be drawn in the map are called "Components", and they are
  hierarchically organized into groups called "Composites".  A Composite
  can contain other Composites, as well as Components.  From an
  organizational point of view, you can think of Composites and
  Components as being analogous to "Directories" and "Files" in a
  computer filesystem.

  The advantage of this design pattern is that it minimizes code
  duplication and maximizes modularity.  There is a set of operations
  which all Components must perform periodically (such as drawing on
  screen, or updating position), but each Component may perform its task
  slightly differently.  Thus, each Component can implement its own
  version of the functions, and each Composite calls on its child
  Components to execute the desired function.  For example, if we wanted
  to draw all Components in the SkyMap, we would simly need to call
  "skyComposite()->draw()".  SkyComposite is the "toplevel" Composite;
  its "draw()" function simply calls draw() on each of its children.
  Its child Components will draw themselves, while its child Composites
  will call "draw()" for each of *their* children.  Note that we don't
  need to know *any* implementation details; when we need to draw the
  elements, we just tell the toplevel Composite to spread the word.

  \section ClassInheritance Class Inheritance
  The base class in this directory is SkyComponent; all other classes
  are derived from it (directly or indirectly).  Its most important
  derivative is SkyComposite, the base classes for all Composites.

  From SkyComponent, we derive three important subclasses:
  SingleComponent, ListComponent, and PointListComponent.
  SingleComponent represents a sky element consisting of a single
  SkyObject, such as the Sun.  ListComponent represents a list of
  SkyObjects, such as the Asteroids.  PointListComponent represents a
  list of SkyPoints (not SkyObjects), such as the Ecliptic.
  Almost all other Components are derived from one of
  these three classes (two Components are derived directly from
  SkyComponent).

  The Solar System bodies require some special treatment.  For example,
  only solar system bodies may have attached trails, and they move
  across the sky, so their positions must be updated frequently.  To
  handle these features, we derive SolarSystemComposite from
  SkyComposite to be the container for all solar system Components.  In
  addition, we derive SolarSystemSingleComponent from SingleComponent,
  and SolarSystemListComponent from ListComponent.  These classes simply
  add functions to handle Trails, and to update the positions of the
  bodies.  Also, they contain KSPlanetBase objects, instead of
  SkyObjects.

  The Sun, Moon and Pluto each get a unique Component class, derived
  from SolarSystemSingleComponent (this is needed because each of these
  bodies uses a unique class derived from KSPlanetBase: KSSun, KSMoon,
  and KSPluto).  The remaining major planets are each represented with a
  PlanetComponent object, which is also derived from
  SolarSystemSingleComponent.  Finally, we have AsteroidsComponent and
  CometsComponent, each of which is derived from
  SolarSystemListComponent.

  Next, we have the Components derived from PointListComponent.  These
  Components represent imaginary guide lines in the sky, such as
  constellation boundaries or the coordinate grid.  They are listed
  above, and most of them don't require further comment.  The
  GuidesComposite contains two sub-Composites: CoordinateGridComposite,
  and ConstellationLinesComposite.  Both of these contain a number of
  PointListComponents: CoordinateGridComposite contains one
  PointsListComponent for each circle in the grid, and
  ConstellationLineComposite contains one PointsListComponent for each
  constellation (representing the "stick figure" of the constellation).

  Finally, we have the Components which don't inherit from either
  SingleComponent, ListComponent, or PointListComponent:
  PlanetMoonsComponent inherits from SkyComponent directly, because
  the planet moons have their own class (PlanetMoons) not derived
  directly from a SkyPoint.

  \section StarComponent DeepStarComponent and StarComponent
  StarComponent and DeepStarComponent manage stars in
  KStars. StarComponent maintains a QVector of instances of
  DeepStarComponents and takes care of calling their draw routines
  etc. The machinery for handling stars is documented in great detail in \ref Stars
 *
 * @author Thomas Kabelmann
 */
class SkyComponent
{
public:
    /** @short Constructor
     * @p parent pointer to the parent SkyComposite
     */
    explicit SkyComponent( SkyComposite *parent );

    /** @short Destructor */
    virtual ~SkyComponent();

    /** @short Draw the object on the SkyMap
     * @p skyp a pointer to the SkyPainter to use
     */
    virtual void draw( SkyPainter *skyp ) = 0;

    /** @short Draw trails for objects. */
    virtual void drawTrails( SkyPainter *skyp );

    /**
     * @short Update the sky position(s) of this component.
     *
     * This function usually just updates the Horizontal (Azimuth/Altitude)
     * coordinates of its member object(s).  However, the precession and
     * nutation must also be recomputed periodically.
     * @p num Pointer to the KSNumbers object
     * @sa SingleComponent::update()
     * @sa ListComponent::update()
     * @sa ConstellationBoundaryComponent::update()
     */
    virtual void update( KSNumbers* ) {}
    virtual void updateSolarSystemBodies( KSNumbers * ) {}
    virtual void updateMoons( KSNumbers * ) {}

    /** @return true if component is to be drawn on the map. */
    virtual bool selected() { return true; }

    /** @return Parent of component. If there is no parent returns NULL. */
    SkyComposite* parent() { return m_parent; }

    /**
     * @short Search the children of this SkyComponent for
     * a SkyObject whose name matches the argument
     * @p name the name to be matched
     * @return a pointer to the SkyObject whose name matches
     * the argument, or a NULL pointer if no match was found.
     * @note This function simply returns the NULL pointer; it
     * is reimplemented in various sub-classes
     */
    virtual SkyObject* findByName( const QString &name );

    /**
     * @short Searches the region(s) and appends the SkyObjects found to the list of sky objects
     *
     * Look for a SkyObject that is in one of the regions 
     * If found, then append to the list of sky objects
     * @p list list of SkyObject to which matching list has to be appended to
     * @p region defines the regions in which the search for SkyObject should be done within
     * @return void
     * @note This function simply returns; it is
     * reimplemented in various sub-classes.
     */
    virtual void objectsInArea( QList<SkyObject*>& list, const SkyRegion& region );

    /**
     * @short Find the SkyObject nearest the given SkyPoint
     *
     * Look for a SkyObject that is nearer to point p than maxrad.
     * If one is found, then maxrad is reset to the separation of the new nearest object.
     * @p p pointer to the SkyPoint to search around
     * @p maxrad reference to current search radius in degrees
     * @return a pointer to the nearest SkyObject
     * @note This function simply returns a NULL pointer; it is
     * reimplemented in various sub-classes.
     */
    virtual SkyObject* objectNearest( SkyPoint *p, double &maxrad );

    /** @short Emit signal about progress.
     *
     * @sa SkyMapComposite::emitProgressText
     */
    virtual void emitProgressText( const QString &message );

    inline QHash<int, QStringList>& objectNames() { return getObjectNames(); }

    inline QStringList& objectNames(int type) { return getObjectNames()[type]; }

    inline QHash<int, QVector<QPair<QString, const SkyObject *>>>& objectLists() { return getObjectLists(); }

    inline QVector<QPair<QString, const SkyObject *>>& objectLists(int type) { return getObjectLists()[type]; }

protected:
    void removeFromNames(const SkyObject* obj);
    void removeFromLists(const SkyObject* obj);

private:
    /** */
    virtual QHash<int, QStringList>& getObjectNames();
    virtual QHash<int, QVector<QPair<QString, const SkyObject *>>>& getObjectLists();

    // Disallow copying and assignement
    SkyComponent(const SkyComponent&);
    SkyComponent& operator= (const SkyComponent&);

    /** Parent of sky component. */
    SkyComposite *m_parent;
};

#endif
