/***********************************************************************
**
**   mapmatrix.h
**
**   This file is part of KFLog2.
**
************************************************************************
**
**   Copyright (c):  2001 by Heiner Lamprecht, Florian Ehinger
**
**   This file is distributed under the terms of the General Public
**   Licence. See the file COPYING for more information.
**
**   $Id: mapmatrix.h,v 1.17 2004/06/18 22:02:26 axel Exp $
**
***********************************************************************/

#ifndef MAPMATRIX_H
#define MAPMATRIX_H

#include <qobject.h>
#include <qwmatrix.h>

#include "projectionlambert.h"
#include "projectioncylindric.h"


/**
 * This class provides functions for converting coordinates between
 * several coordinate-systems. It takes control over the mapscale
 * and the pfijection-type. To avoid problems, there should be only
 * one element per application.
 *
 * @author Heiner Lamprecht, Florian Ehinger
 * @version $Id: mapmatrix.h,v 1.17 2004/06/18 22:02:26 axel Exp $
 */
class MapMatrix : public QObject
{
  Q_OBJECT

  public:
    /**
     * Creates a new mapmatrix-object.
     */
    MapMatrix(QObject*);
    /**
     * Destructor
     */
    virtual ~MapMatrix();

    /**
     * Converts the given geographic-data into the current map-projection.
     *
     * @param  point  The point to be converted. The point must be in
     *                the internal format of 1/10.000 minutes.
     *
     * @return the projected point
     */
    QPoint wgsToMap(const QPoint& point) const;

    /**
     * Converts the given geographic-data into the current map-projection.
     *
     * @param  lat  The latitude of the point to be converted. The point must
     *              be in the internal format of 1/10.000 minutes.
     * @param  lon  The longitude of the point to be converted. The point must
     *              be in the internal format of 1/10.000 minutes.
     *
     * @return the projected point
     */
    QPoint wgsToMap(int lat, int lon) const;

    /**
     * Converts the given geographic-data into the current map-projection.
     *
     * @param  rect  The rectangle to be converted. The points must
     *              be in the internal format of 1/10.000 minutes.
     *
     * @return the projected rectangle
     */
    QRect wgsToMap(const QRect& rect) const;

    /**
     * Maps the given projected pointarray into the current map-matrix.
     *
     * @param  pArray  The pointarray to be mapped
     *
     * @return the mapped pointarray
     */
    QPointArray map(const QPointArray& pArray) const;

    /**
     * Maps the given projected point into the current map-matrix.
     *
     * @param  point  The point to be mapped
     *
     * @return the mapped point
     */
    QPoint map(const QPoint& point) const;

    /**
     * Maps the given bearing into the current map-matrix.
     *
     * @param  bearing  The bearing to be mapped
     *
     * @return the mapped bearing
     */
    double map(double bearing) const;


    /**
     * @param  type  The type of scale to be returned.
     *
     * @return the selected scale
     */

    double getScale(unsigned int type = MapMatrix::CurrentScale);
    /**
     * @param  type  The ration of MaxScale to the current scale
     *               is returned as int.
     *
     * @return the selected scale
     */

    inline int getScaleRatio() { return ( _MaxScaleToCScaleRatio ); };

    inline QRect getMapBorder() { return ( mapBorder ); };
    /**
     * @return the lat/lon-border of the current map.
     * @see getMapBorder()
     */
    QRect getViewBorder() const;
    /**
     * @return the lat/lon-border of the current map (in projected coordinates).
     * @see getViewBorder()
     */
    QRect getMapBorder() const;

    /**
     * Initializes the matrix for displaying the map.
     */
    void createMatrix(const QSize& newSize);

    /**
     * @return "true", if the given point in visible in the current map.
     */
    bool isVisible(const QPoint& pos) const;

    /**
     * @return "true", if the given rectangle intersects with the current map.
     */
    bool isVisible( const QRect& itemBorder, int typeID) const;

    /** */
    enum MoveDirection {NotSet = 0, North = 1, West = 2, East = 4,
        South = 8, Home = 16, Waypoint = 32};
    /**
     * CurrentScale muss immer die größte Zahl sein!
     */
    enum ScaleType {LowerLimit = 0, Border1 = 1, Border2 = 2, Border3 = 3,
        UpperLimit = 4, SwitchScale = 5, CurrentScale = 6};
    /**
     * Centers the map to the given point.
     */
    void centerToPoint(const QPoint&);
    /**
     * Centers the map to the given rectangle and scales the map, so that
     * the rectangle will be seen completly.
     */
    double centerToRect(const QRect&, const QSize& = QSize(0,0));
    /** */
    QPoint mapToWgs(const QPoint& pos) const;
    /**
     *
     */
    int getScaleRange() const;
    /**
     * @return "true", if the current scale is smaller than the switch-scale.
     */
    bool isSwitchScale() const;
     /**
     * @return "true", if the current scale is smaller than the second switch-scale.
     */
    bool isSwitchScale2() const;
    /**
     * @return the lat/lon-position of the map-center.
     */
    QPoint getMapCenter(bool isPrint = false) const;
    /** */
    void centerToLatLon(const QPoint& center);
    /** */
    void centerToLatLon(int latitude, int longitude);
    /** */
    void writeMatrixOptions();
    
  /** This function tries to make the given point visible on the map, using only scaling.
    * This may fail if the point is too far away, so that the requiered scale is bigger
    * than the limit. If the function fails, the current projection is not changed and
    * false is returned. If the function succeeds, the scale is changed so that the greatest
    * level of detail can be seen while keeping the point on the map. The new scale is returned. */
  double ensureVisible(const QPoint& point);

  /**
   * @returns an indication of the current draw scale, 0 to 2. 0 is small scale, 2 very big scale
   */
  unsigned int currentDrawScale() const;
  /**
   * @returns the coordinates of the homesite
   */
  QPoint getHomeCoord();
  /**
   * @returns true if the point is close to the center of the map, and false otherwise.
   */
  bool isInCenterArea(const QPoint& coord);
  /**
   * @returns true if the rectangle @arg r intersects with the center rectangle of the map
   */
  bool isInCenterArea(const QRect& r);
  /**
   * @returns true if the projected rectangle @arg r intersects
   * with the center rectangle of the map
   */
  bool isInProjCenterArea(const QRect& r);
  /**
   * @returns the current projectiontype
   */
  ProjectionBase * getProjection() const {return currentProjection;} ;      

  public slots:
    /** */
    void slotInitMatrix();
    /** */
    void slotSetScale(const double&);
    /** */
    void slotCenterTo(int latitude, int longitude);
    /** set new home position */
    void slotSetNewHome(const QPoint *newHome);
  signals:
    /** */
    void displayMatrixValues(int, bool);
    /** */
    void printMatrixValues(int);
    /**
     * Emitted each time the matrix has changed.
     */
    void matrixChanged();
    /**
     * Emitted each time the projection changed.
     */
    void projectionChanged();

  private:
    /**
     * Moves the map into the given direction.
     */
    void __moveMap(int dir);
    /**
     */
    QPoint __mapToWgs(const QPoint&) const;
    /**
     */
    QPoint __mapToWgs(int x, int y) const;
    /**
     */
    QWMatrix worldMatrix;
    /**
     */
    QWMatrix printMatrix;
    /**
     * The mapCenter is the position displayed in the center of the map.
     * It is used in two different ways:
     * 1.: Determine the area shown in the map-widget
     * 2.: Calculating the difference in latitude between a point in the
     * map and the center.
     *
     * The latitude of the center of the map.
     */
    int mapCenterLat;
    /**
     * The longitude of the center of the map.
     */
    int mapCenterLon;
    /** */
    int printCenterLat;
    int printCenterLon;
    /** */
    int homeLat;
    /** */
    int homeLon;
    /**
     * Contains the geographical border of the map (lat/lon).
     */
    QRect viewBorder;
    // QRect printBorder;
    QRect mapBorder;
    //the mapCenterArea is the rectangle in which the glidersymbol can move around before the map is redrawn
    QRect mapCenterArea;
    QRect mapCenterAreaProj;
    /** */
    QSize mapViewSize;
    /** Number of meters per pixel? */
    double cScale;
    /** */
    double pScale;
    /** */
    double rotationArc;
    /** */
    double printArc;
    /** */
    int scaleBorders[6];
    /** */
    QSize matrixSize;

    /** */
    ProjectionBase* currentProjection;
    /** optimisation to prevent recurring recalculation of this value */
    int _MaxScaleToCScaleRatio;

    bool firstLoad;

};

#endif
