/***********************************************************************
**
**   map.cpp
**
**   This file is part of Cumulus.
**
************************************************************************
**
**   Copyright (c):  1999, 2000 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: map.cpp,v 1.149 2005/02/28 10:23:51 andre Exp $
**
***********************************************************************/
#include "targets.h"

#include <cmath>
#include <qpe/resource.h>
#include <qpe/qpeapplication.h>
#include <qpe/global.h>
#include <qdragobject.h>
#include <qpainter.h>
#include <qpen.h>
#include <qregexp.h>
#include <qtimer.h>
#include <qwhatsthis.h>
#include <qlist.h>
#include <qsimplerichtext.h>

#include <ctype.h>
#include <stdlib.h>

#include "airport.h"
#include "airspace.h"
#include "cucalc.h"
#include "cupainter.h"
#include "distance.h"
//#include "flight.h"
//#include "flightgroup.h"
#include "generalconfig.h"
#include "hwinfo.h"
#include "map.h"
#include "mapcalc.h"
#include "mapconfig.h"
#include "mapcontents.h"
#include "mapdefaults.h"
#include "mapmatrix.h"
#include "radiopoint.h"
#include "reachablelist.h"
#include "runway.h"
#include "singlepoint.h"
#include "translationlist.h"
#include "wgspoint.h"
#include "whatsthat.h"
#include "wp.h"
#include "wpeditdialog.h"

// Festlegen der Groesse der Pixmaps auf Desktop-Groesse
#ifdef t_klfog //size on desktop is variable
  #define PIX_WIDTH  QApplication::desktop()->width()
  #define PIX_HEIGHT QApplication::desktop()->height()
#else //size on PDA is fixed
  #define PIX_WIDTH  240
  #define PIX_HEIGHT 320
#endif

// These values control the borders at which to pan the map
// NOTE: These values are only for testing, and need revision.
#define MIN_X_TO_PAN 30
#define MAX_X_TO_PAN QApplication::desktop()->width()-30
#define MIN_Y_TO_PAN 30
#define MAX_Y_TO_PAN QApplication::desktop()->height()-30

extern TranslationList waypointTypes;
extern MapContents * _globalMapContents;
extern MapMatrix * _globalMapMatrix;
extern MapConfig * _globalMapConfig;

Map * Map::instance=0;

Map::Map(QWidget* parent, const char* name)
        :QWidget(parent, name),
        prePos(-50, -50), preCur1(-50, -50), preCur2(-50, -50),
        planning(-1), preSnapPoint(-999, -999)
{
    _isEnable = false;  // Disable map redrawing at startup
    _isResizeEvent = false;
    _isRedrawEvent = false;
    mapRot=0;
    curMapRot=0;
    heading=0;
    bearing=0;
    mode=northUp;
    ShowGlider=false;
    setMutex (false);
    installEventFilter(this); //install an event filter on the map object itself, so all events are routed through eventFilter()
    zoomProgressive = 0;
    zoomProgressiveVal[0] = 1.25;
    for( int i=1; i<8; i++ )
    {
        zoomProgressiveVal[i] = zoomProgressiveVal[i-1]*1.25;
    }
    pixAero.resize( PIX_WIDTH, PIX_HEIGHT );
    //  pixAirspace.resize( PIX_WIDTH, PIX_HEIGHT );
    //  pixFlight.resize( PIX_WIDTH, PIX_HEIGHT );
    pixPlan.resize( PIX_WIDTH, PIX_HEIGHT );
    //  pixGrid.resize( PIX_WIDTH, PIX_HEIGHT );
    //  pixUnderMap.resize( PIX_WIDTH, PIX_HEIGHT );
    pixIsoMap.resize( PIX_WIDTH, PIX_HEIGHT );
    //  pixWaypoints.resize( PIX_WIDTH, PIX_HEIGHT );
    bitMapMask.resize( PIX_WIDTH, PIX_HEIGHT );
    bitAeroMask.resize( PIX_WIDTH, PIX_HEIGHT );
    //  bitAirspaceMask.resize( PIX_WIDTH, PIX_HEIGHT );
    bitPlanMask.resize( PIX_WIDTH, PIX_HEIGHT );
    bitFlightMask.resize( PIX_WIDTH, PIX_HEIGHT );
    //  bitWaypointsMask.resize( PIX_WIDTH, PIX_HEIGHT );

    airspaceRegList.setAutoDelete(true);

    //setMouseTracking(true);
    setBackgroundColor(Qt::white);
    //setCursor(crossCursor);
    //setAcceptDrops(true);

    redrawTimerShort = new QTimer(this);
    redrawTimerLong = new QTimer(this);
    connect( redrawTimerShort, SIGNAL(timeout()), this, SLOT(slotRedrawMap()));
    connect( redrawTimerLong, SIGNAL(timeout()), this, SLOT(slotRedrawMap()));
    zoomFactor=_globalMapMatrix->getScale(MapMatrix::CurrentScale);
    curPos=_globalMapMatrix->getMapCenter();

    /** @ee load icons */
    _cross = Resource::loadPixmap("cumulus/cross.png");
    _glider = Resource::loadPixmap("cumulus/gliders.png");
    QPEApplication::setStylusOperation(this, QPEApplication::RightOnHold);

     instance = this;
}

Map::~Map()
{
}


void  Map::slot_settingschanged()
{

}

void Map::__displayMapInfo(QPoint current)
{
    /*
     * Gliding sites, if available, will be the first entry
     */
    if( mutex() )
    {
        //qDebug("Map::__displayMapInfo: Map drawing in progress: return");
        return;
    }
    if (!isVisible())
      return;

    int itemcount=0;

    BaseFlightElement *baseFlight = _globalMapContents->getFlight();

    // Radius for Mouse Snapping
    int delta=18, dX=0, dY=0;

    QString text;

    bool show = false, isAirport = false;

#if 0 // Gliderfields and airfields are not more handled by this method

    double cs = _globalMapMatrix->getScale(MapMatrix::CurrentScale);

    // @AP: On map scale higher as 1024 we don't evelute anything
    for(unsigned int loop = 0;
	loop < _globalMapContents->getListLength(MapContents::GliderList) &&
	  cs <= 1024.0; loop++)
    {
        SinglePoint* hitElement = (SinglePoint*)_globalMapContents->getElement(
                                      MapContents::GliderList, loop);
        QPoint sitePos (hitElement->getMapPosition());
        QString name = ((SinglePoint*)hitElement)->getName();
        dX = sitePos.x() - current.x();
        dY = sitePos.y() - current.y();
        // Distance determines the icon-size
        if( ( ( dX < delta ) && ( dX > -delta ) ) &&
                ( ( dY < delta ) && ( dY > -delta ) ) )
        {
            text += ((SinglePoint*)hitElement)->getInfoString();
	    text += 
	    "Distance:    " + ReachableList::getDistance(name).getText(true,2) + "<BR>" + 
	    "Arrival Alt: " + Altitude(ReachableList::getArrivalAlt(name)).getText(true,0)+ "<BR>";
            isAirport = true;
            itemcount++;
        }

    }

    // @AP: On map scale higher as 1024 we don't evalute anything
    for(unsigned int loop = 0;
	loop < _globalMapContents->getListLength(MapContents::AirportList) &&
	  cs <= 1024.0; loop++)
    {
        SinglePoint* hitElement = (SinglePoint*)_globalMapContents->getElement(
                                      MapContents::AirportList, loop);
        QPoint sitePos (hitElement->getMapPosition());
        QString name = ((SinglePoint*)hitElement)->getName();
        dX = sitePos.x() - current.x();
        dY = sitePos.y() - current.y();

        // Abstand entspricht der Icon-Groesse
        if( ( ( dX < delta ) && ( dX > -delta ) ) &&
                ( ( dY < delta ) && ( dY > -delta ) ) )
        {
            // Draw text
            //QWhatsThis::enterWhatsThisMode();
            //QWhatsThis::leaveWhatsThisMode(text);
            text += ((SinglePoint*)hitElement)->getInfoString();
            text += "Distance:    " + ReachableList::getDistance(name).getText(true,2) + "<BR>" + 
               "Arrival Alt: " + Altitude( ReachableList::getArrivalAlt(name) ).getText(true,0) + "<BR>";

            isAirport = true;
            itemcount++;
        }
    }

#endif

    if(baseFlight && baseFlight->getTypeID() == BaseMapElement::Flight)
    {
        bool isWP = false;
        QString wpText = "<B>"+tr("Waypoint:")+"</B><UL>";

        for (QListIterator<wayPoint> it (baseFlight->getWPList()); it.current(); ++it)
        {
            wayPoint* wp = it.current();
            QPoint sitePos (_globalMapMatrix->map(wp->projP));

            dX = sitePos.x() - current.x();
            dY = sitePos.y() - current.y();

            // We do not search for the sector ...
            if( ( ( dX < delta ) && ( dX > -delta ) ) &&
                    ( ( dY < delta ) && ( dY > -delta ) ) )
            {
                isWP = true;

                QString tmpText, timeText;

                if(wp->sector1 != 0)
                {
                    timeText = printTime(wp->sector1);
                    tmpText = tr("Sector 1");
                }
                else if(wp->sector2 != 0)
                {
                    timeText = printTime(wp->sector2);
                    tmpText = tr("Sector 2");
                }
                else if(wp->sectorFAI != 0)
                {
                    timeText = printTime(wp->sectorFAI);
                    tmpText = tr("FAI-Sector");
                }
                else
                {
                    timeText = "&nbsp;" + tr("not reached");
                }

                switch(wp->type)
                {
                case FlightTask::TakeOff:
                    tmpText = tr("Take Off");
                    break;
                case FlightTask::Begin:
                    tmpText = tr("Begin of task");
                    break;
                case FlightTask::End:
                    tmpText = tr("End of task");
                    break;
                case FlightTask::Landing:
                    tmpText = tr("Landing");
                    break;
                }

                wpText += "<LI><B>" + wp->name +
                          "</B>  " +
                          "&nbsp;" + timeText + " / " + tmpText + "<BR>" +
                          WGSPoint::printPos(wp->origP.lat()) + " / " +
                          WGSPoint::printPos(wp->origP.lon(), false) + "</LI>";
            }
        }

        if(isWP)
        {
            wpText += "</UL>";
            text += wpText;
            itemcount++;
            isAirport = true;
        }
    }

    if(isAirport)
      {
	GeneralConfig *conf = GeneralConfig::instance();
        int airfieldTime = conf->getAirfieldDisplayTime();
        int showTime = 0;

        if( airfieldTime != 0 )
        {
            // qDebug("airfield itemcount=%d", itemcount);
            showTime = MAX((itemcount * airfieldTime), MIN_POPUP_DISPLAY_TIME);
            showTime *= 1000;
        }

        // qDebug("airfield timer %dms", showTime);

        WhatsThat *box = new WhatsThat(this, text, this, "", showTime);
        box->show();
        return;
    }

#if 0 // Waypoints are not more handled by this method

    // let's show waypoints
    for (QListIterator<wayPoint> it (*(_globalMapContents->getWaypointList()));
	 it.current() && cs <= 1024.0;
	 ++it)
    {
        wayPoint* wp = it.current();

	// evelute only points, which are visible on the map
	if( wp->importance < _globalMapMatrix->currentDrawScale() )
	  {
	    continue;
	  }

        QPoint sitePos (_globalMapMatrix->map(_globalMapMatrix->wgsToMap(wp->origP)));
        double dX = abs(sitePos.x() - current.x());
        double dY = abs(sitePos.y() - current.y());

        // Abstand entspricht der Icon-Groesse
        if (dX < delta && dY < delta)
        {
            // qDebug ("waypoint: %s", wp->name.latin1());
            QString wpText = "<B>"+tr(waypointTypes.itemById(wp->type)->text)+"</B><UL>";
            wpText += "<B>" + wp->name + "<BR><BR>" +
	    "Elevation:   " + Altitude::getText(wp->elevation,true,0).replace(QRegExp("\\s"),"&nbsp;")+ "</B><BR>" +
	    "Distance:    " + ReachableList::getDistance(wp->name).getText(true,2) + "</B>" + 
	    "Arrival Alt: " +  Altitude(ReachableList::getArrivalAlt(wp->name)).getText(true,0) + "</B><BR>"; 
            wpText += "</UL>";

            int showTime = MAX(_globalMapConfig->getWaypointDisplayTime() * 1000, MIN_POPUP_DISPLAY_TIME);

            // qDebug("Waypoint timer %dms", showTime);
            WhatsThat * box=new WhatsThat(this, wpText, this, "", showTime );
            box->show();
            return;
        }
    }

#endif

    text += "<p align=\"left\"><B><nobr>" +
      tr("Airspace Structure") +
      "</nobr></B></p><hr><UL>";

    for(unsigned int loop = 0; loop < airspaceRegList.count(); loop++)
    {
        if(airspaceRegList.at(loop)->region->contains(current))
        {
            Airspace* pSpace = airspaceRegList.at(loop)->airspace;
            //      qDebug ("name: %s", pSpace->getName().latin1());
            //      qDebug ("lower limit: %d", pSpace->getLowerL());
            //      qDebug ("upper limit: %d", pSpace->getUpperL());
            //      qDebug ("lower limit type: %d", pSpace->getLowerT());
            //      qDebug ("upper limit type: %d", pSpace->getUpperT());
            //work around the phenemenon that airspaces tend to appear in the list twice -> this should be dealt with properly!
            if (text.find(pSpace->getInfoString())==-1) {     //only add if the string has not been added before
                text += "<LI>" + pSpace->getInfoString() + "</LI>";
                itemcount++;
            }
            show = true;
        }
    }

    text += "</UL>";

    // @AP: replace long labels to small one, otherwise the window is
    // sometimes to small for display whole line.
    text.replace( QRegExp("STANDARD"), "STD" );
     // remove term Restricted, to display all info behind
    text.replace( QRegExp("Restricted "), "" );

    if(show)
    {
        // Text anzeigen
        GeneralConfig *conf = GeneralConfig::instance();
        int airspaceTime = conf->getAirspaceDisplayTime();
        int showTime = 0;

        if( airspaceTime != 0 )
        {
            //qDebug("airspace itemcount=%d", itemcount);
            showTime = MAX((itemcount * airspaceTime), MIN_POPUP_DISPLAY_TIME);
            showTime *= 1000;
        }

        // qDebug("airspace timer %dms", showTime);
        WhatsThat * box=new WhatsThat(this, text, this, "", showTime);
        box->show();
        return;
    }
}

void Map::__displayDetailedMapInfo(const QPoint& current)
{
    if( mutex() )
    {
        //qDebug("Map::__displayDetailedMapInfo: Map drawing in progress: return");
        return;
    }

    // select WayPoint
    QRegExp blank("[ ]");
    bool found = false;

    // Radius for Mouse Snapping
    int delta=18, dX=0, dY=0;
    /*if(_globalMapMatrix->isSwitchScale())
        delta = 18.0;*/
    int lastDist=2*delta+1; //Manhattan-distance to found point.
    
    // add WPList !!!
    int searchList[] = {MapContents::GliderList, MapContents::AirportList};
    wayPoint *w = NULL;

    double cs = _globalMapMatrix->getScale(MapMatrix::CurrentScale);

    // @AP: On map scale higher as 1024 we don't evelute anything
    for (int l = 0; l < 2 && cs < 1024.0; l++)
    {
        for(unsigned int loop = 0;
                loop < _globalMapContents->getListLength(searchList[l]); loop++)
        {
            Airport* hitElement = (Airport*)_globalMapContents->getElement(
                                      searchList[l], loop);
            QPoint sitePos (hitElement->getMapPosition());

            dX = abs(sitePos.x() - current.x());
            dY = abs(sitePos.y() - current.y());

            // Abstand entspricht der Icon-Groesse
            if (dX < delta && dY < delta)
            {
                if (found && ((dX+dY) >= lastDist)) {
                  continue; //the point we found earlier was closer
                }
                
                //      qDebug ("waypoint: %s", hitElement->getName().latin1());
                //w = new wayPoint;  // create new wp later, only if added to list
                w = &wp;
                // w->name = w->name.replace(blank, QString::null).left(6).upper();
		w->name = hitElement->getWPName();
                w->description = hitElement->getName();
                w->type = hitElement->getTypeID();
                w->origP = hitElement->getWGSPosition();
                w->elevation = hitElement->getElevation();
                w->icao = hitElement->getICAO();
                // qDebug("|%s|", hitElement->getFrequency().latin1());
                w->frequency = hitElement->getFrequency().toDouble();
                // qDebug(" hitElement->getRunway(0) %d",hitElement->getRunway(0).direction );
                w->isLandable = hitElement->getRunway(0).isOpen;
                w->surface = hitElement->getRunway(0).surface;
                w->runway = hitElement->getRunway(0).direction;
                w->length = hitElement->getRunway(0).length;
                w->sectorFAI = 0;
                w->sector1 = 0;
                w->sector2 = 0;

                found = true;
                lastDist = dX+dY;
                if (lastDist< (delta/3)) //if we're very near, stop searching the list
                {
                  break;
                }
            }
        }
    }

    // let's show waypoints
    // @AP: On map scale higher as 1024 we don't evelute anything
   for( QListIterator<wayPoint> it (*(_globalMapContents->getWaypointList()));
	  it.current() && cs <= 1024.0;
	  ++it )
    {
        wayPoint* wp = it.current();

	// consider only points, which are visible on the map
	if( wp->importance < _globalMapMatrix->currentDrawScale() )
	  {
	    continue;
	  }

        QPoint sitePos (_globalMapMatrix->map(_globalMapMatrix->wgsToMap(wp->origP)));
        dX = abs(sitePos.x() - current.x());
        dY = abs(sitePos.y() - current.y());

        // Abstand entspricht der Icon-Groesse
        if (dX < delta && dY < delta)
        {
            if (found && ((dX+dY) > lastDist)) { //subtle difference with airfields: replace allready found waypoint if we find a waypoint at the same distance.
              continue; //the point we found earlier was closer
            }
            found = true;
            w=wp;
            lastDist = dX+dY;
            if (lastDist< (delta/3)) //if we're very near, stop searching the list
            {
              break;
            }
        }
    }

    if (found)
    {
        emit waypointSelected(w);
        return;
    }

    /** @ee maybe we can show airspace info */
    if (!found)
        __displayMapInfo(current);
}

void Map::keyReleaseEvent(QKeyEvent* event)
{
  if( mutex() )
    {
        // qDebug("Map::keyReleaseEvent(): mutex is locked, returning");
        return;
    }

    // qDebug("Map::keyReleaseEvent()");

    /** @ee button states must be checked bitwise ! */
    if(event->state() & QEvent::ShiftButton)
    {
        __showLayer();
    }
}

void Map::mousePressEvent(QMouseEvent* event)
{
  // qDebug("Map::mousePressEvent():");

  if( mutex() )
    {
        // qDebug("Map::mousePressEvent(): mutex is locked, returning");
        return;
    }

    // qDebug("Map::mousePressEvent");
    switch (event->button())
    {
        // we don't have a mid button on the Z, but ...
        /*
        case MidButton:
          // Move Map
          _globalMapMatrix->centerToPoint(event->pos());
          _globalMapMatrix->createMatrix(this->size());
          __redrawMap();
          break;
        */

        // On Zaurus we use the press-on-hold feature
    case RightButton: // press and hold generates mouse RightButton
      // qDebug("RightButton");
      break;
    case LeftButton: // press generates mouse LeftButton immediately
      // qDebug("LeftButton");
      __displayDetailedMapInfo(event->pos());
      break;
    case MidButton:
      // qDebug("MidButton");
      break;
      
    default:
        break;
    }
}

void Map::mouseReleaseEvent(QMouseEvent* event)
{
  // qDebug("Map::mouseReleaseEvent():");

  if( mutex() )
    {
        // qDebug("Map::mouseReleaseEvent(): mutex is locked, returning");
        return;
    }

  switch (event->button())
    {
    case LeftButton:
      // __displayMapInfo(event->pos());
      break;
    default:
      break;
    }
}

void Map::paintEvent(QPaintEvent* event = 0)
{
  if( mutex() )
    {
      // qDebug("Map::paintEvent(): mutex is locked, returning");
        return;
    }

    if(event == 0)
        bitBlt(this, 0,0, &pixBuffer);
    else
        bitBlt(this, event->rect().topLeft(), &pixBuffer, event->rect());
}

void Map::__drawWindArrow()
{
     bitBlt(&pixBuffer, 4, 4, &windArrow, windArrow.width()/4,windArrow.width()/4,36,36);
}

void Map::slotNewWind()
{
  Vector wind = calculator->getlastWind();
  if( wind.getSpeed().getMps() != 0 )
  {
     int angle = wind.getAngleDeg();
     angle = ((angle+5)/10)*10;  // quantisize modulo 10
     while( angle > 360 )
       angle -= 360;
     QString resource; 
     resource.sprintf("cumulus/windarrows/wind-arrow-%d.png", angle );
     // qDebug("Loading resource %s", (const char *) resource );
     windArrow = Resource::loadPixmap(resource);
  }
}

void Map::__drawGrid()
{
    const QRect mapBorder = _globalMapMatrix->getViewBorder();

    QPainter gridP(&pixIsoMap);
    gridP.setBrush(NoBrush);

    // die Kanten des Bereichs
    const int lon1 = mapBorder.left() / 600000 - 1;
    const int lon2 = mapBorder.right() / 600000 + 1;
    const int lat1 = mapBorder.top() / 600000 + 1;
    const int lat2 = mapBorder.bottom() / 600000 - 1;

    // Step between two degree-lines (in 1/60 degree)
    int step = 60;
    int gridStep = 1;
    int lineWidth = 1;

    switch(_globalMapMatrix->getScaleRange())
    {
    case 0:
        step = 10;
        break;
    case 1:
        step = 30;
        break;
    case 2:
        gridStep = 2;
        break;
    default:
        gridStep = 4;
    }

    QPoint cP, cP2;

    // First the latitudes:
    for(int loop = 0; loop < (lat1 - lat2 + 1) ; loop += gridStep)
    {
        int size = (lon2 - lon1 + 1) * 10;
        QPointArray pointArray(size);

        for(int lonloop = 0; lonloop < size; lonloop++)
        {
            cP = _globalMapMatrix->wgsToMap( ( lat2 + loop ) * 600000,
                                             (int)( lon1 + ( lonloop / 10.0 ) ) * 600000);
            pointArray.setPoint(lonloop, cP);
        }

        // Draw the small lines between:
        int number = 60 / step;
        for(int loop2 = 1; loop2 < number; loop2++)
        {
            QPointArray pointArraySmall(size);

            for(int lonloop = 0; lonloop < size; lonloop++)
            {
                cP = _globalMapMatrix->wgsToMap(
                         (int)((lat2 + loop + ( loop2 * ( step / 60.0 ) ) ) * 600000),
                         (int)((lon1 + (lonloop / 10.0)) * 600000));
                pointArraySmall.setPoint(lonloop, cP);
            }

            if(loop2 == (number / 2))
                gridP.setPen(QPen(Qt::black, 1, DashLine));
            else
                gridP.setPen(QPen(Qt::black, 1, DotLine));
            gridP.drawPolyline(_globalMapMatrix->map(pointArraySmall));
        }
        // Draw the main lines
        gridP.setPen(QPen(Qt::black, 1));
        gridP.drawPolyline(_globalMapMatrix->map(pointArray));
    }

    // Now the longitudes:
    for(int loop = lon1; loop <= lon2; loop += gridStep)
    {
        cP = _globalMapMatrix->wgsToMap(lat1 * 600000, (loop * 600000));
        cP2 = _globalMapMatrix->wgsToMap(lat2 * 600000, (loop * 600000));

        // Draw the main longitudes:
        gridP.setPen(QPen(Qt::black, 1));
        gridP.drawLine(_globalMapMatrix->map(cP), _globalMapMatrix->map(cP2));

        // Draw the small lines between:
        int number = 60 / step;

        for(int loop2 = 1; loop2 < number; loop2++)
        {
            cP = _globalMapMatrix->wgsToMap((lat1 * 600000),
                                            (int)((loop + (loop2 * step / 60.0)) * 600000));

            cP2 = _globalMapMatrix->wgsToMap((lat2 * 600000),
                                             (int)((loop + (loop2 * step / 60.0)) * 600000));

            if(loop2 == (number / 2))
                gridP.setPen(QPen(Qt::black, 1, DashLine));
            else
                gridP.setPen(QPen(Qt::black, lineWidth, DotLine));

            gridP.drawLine(_globalMapMatrix->map(cP), _globalMapMatrix->map(cP2));
        }
    }
    gridP.end();
}


void Map::__drawLandableSites()
{
    pixAero=pixIsoMap;

    double cs = _globalMapMatrix->getScale(MapMatrix::CurrentScale);

    if( !_isEnable || cs > 1024.0 )
      {
        return;
      }

    QPainter aeroP(&pixAero);

    _globalMapContents->drawList(&aeroP, 0, MapContents::AirportList);
    _globalMapContents->drawList(&aeroP, 0, MapContents::OutList);
    _globalMapContents->drawList(&aeroP, 0, MapContents::GliderList);
    __drawWaypoints(&aeroP, 0);
}


void Map::__drawMap()
{
  qDebug("Map::__drawMap(): zoomFactor=%f, loopLevel=%d",
	 zoomFactor, qApp->loopLevel() );

    if( !_isEnable )
        return;

    // save free memory
    int memFreeBegin = HwInfo::instance()->getFreeMemory();

    double cs = _globalMapMatrix->getScale(MapMatrix::CurrentScale);

    QTime t1;    // timer for performace measurement
    t1.start();

    QPainter isoMapP(&pixIsoMap);
    QPainter mapMaskP(&bitMapMask);

    airspaceRegList.clear();
    // this is a light blue, qt has no predefined color
    pixIsoMap.fill(QColor(96,128,248));

    Global::statusMessage(tr("Starting map drawing"));

    _globalMapContents->drawIsoList(&isoMapP, &mapMaskP);

    qApp->processEvents();  // ensure time for GPS processing

    _globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::TopoList);
    _globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::CityList);

    if( cs < 819.2 )
      {
	_globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::RoadList);
	_globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::RailList);
     }

    if( cs < 1024.0 )
      {
	_globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::LandmarkList);
	_globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::ObstacleList);
	_globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::ReportList);
	_globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::HydroList);
     }

    _globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::HighwayList);
    _globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::LakeList);
    _globalMapContents->drawList(&isoMapP, &mapMaskP, MapContents::NavList);

    qApp->processEvents();  // ensure time for GPS processing

    Airspace* currentAirS;
    QRegion * reg=0;
    AirRegion * region=0;

    for(unsigned int loop = 0; loop < _globalMapContents->getListLength(
                MapContents::AirspaceList); loop++)
    {
        currentAirS = (Airspace*)_globalMapContents->getElement(
                          MapContents::AirspaceList, loop);
        // we should only store the airspaces that actually get drawn in the list,
        // because we can search faster that way.
        //      airspaceRegList->append(currentAirS->drawRegion(&airSpaceP,
        //            &airspaceMaskP));
	// As long there is no extra config, use this
        if( !GeneralConfig::instance()->getAirspaceWarningEnabled( currentAirS->getTypeID() ) )
            continue;
        reg=currentAirS->drawRegion(&isoMapP, &mapMaskP);
        if (reg)
        {
            region=new AirRegion(reg, currentAirS);
            airspaceRegList.append(region);
        }
    }

    mapMaskP.end();

    // landable sites are drawn from their own routine now

    Global::statusMessage(tr("Map drawing finished"));

    // save free memory
    int memFreeEnd = HwInfo::instance()->getFreeMemory();

    qDebug("Total time for map drawing: %d msec, MemStart=%dKB, MemEnd=%dKB, SEDelta=%dKB",
           t1.restart(), memFreeBegin, memFreeEnd, memFreeBegin-memFreeEnd  );
}

void Map::__drawPlannedTask(bool /*solid*/ )
{
    CuPainter planP;
    FlightTask* task;

    //  if(solid)
    //      task = (FlightTask*)_globalMapContents->getFlight();
    task = (FlightTask*)_globalMapContents->getCurrentTask();
    if (task==0) return;
    //  else
    //       task = &tempTask;

    if(task && task->getTypeID() == BaseMapElement::Task)
    {
        QList<wayPoint> WPList = task->getWPList();

        // Strecke zeichnen
        //      if(solid)
        planP.begin(&pixPlan);
        //      else
        //          planP.begin(this);

        CuPainter planPMask(&bitPlanMask);
        bitPlanMask.fill(Qt::color0);
        pixPlan.fill(white);
        //bitFlightMask.fill(Qt::color0);
        //pixFlight.fill(white);

        //      QPen drawP(QColor(170,0,0), 5);
        QPen drawP(Qt::darkRed, 5);
        drawP.setJoinStyle(Qt::MiterJoin);
        planP.setBrush(NoBrush);
        planP.setPen(drawP);

        // Aufgabe mit Sektoren
        task->drawMapElement(&planP,&planPMask);
        emit showTaskText(task);
        planPMask.end();
        planP.end();
    }

}

/**
 * Draw a trail displaying the flightpath if feature is turned on
 */
void Map::__drawTrail()
{
  if (true) //disabled for now
  {
    return;
  }
  
  if (calculator->samplelist->count()<5) 
  {
    return;
  }
  
  QPainter p(&pixAero);
  QPen pen1(black, 2);
  QTime minTime=QTime::currentTime().addSecs(-150);
  //minTime.addSecs(-maxSecs);
  QPoint pos;
  QPoint lastPos;
  uint loop=5;
  lastPos=_globalMapMatrix->map(_globalMapMatrix->wgsToMap(calculator->samplelist->at(0)->position));
  p.setPen(pen1);
  while (loop < calculator->samplelist->count() && calculator->samplelist->at(loop)->time >= minTime)
  {
    pos=_globalMapMatrix->map(_globalMapMatrix->wgsToMap(calculator->samplelist->at(loop)->position));
    p.drawLine(pos,lastPos);
    lastPos=pos;
 
    loop+=5;
  }
}


void Map::setDrawing(bool isEnable)
{
    _isEnable = isEnable;
}

void Map::resizeEvent(QResizeEvent* event)
{
    // qDebug("Map::resizeEvent");

    if(!event->size().isEmpty())
    {
        // qDebug("Map::resizeEvent: event is queued for later execution");

        // Queue resize event for later execution. There is only the
        // last request considered.
        _isResizeEvent = true;
        resizeEventSize = event->size();
        // @AP: execute it later, all other will cause crashes
        sceduleRedraw();
    }

    // emit changed(event->size());
}

void Map::__redrawMap()
{
    //qDebug("Map::__redrawMap");

    if( ! _isEnable ){
        //qDebug("__redrawMap() disabled");
        // @AP: we reset also the redraw request flag.
        _isRedrawEvent = false;
        return;
    }

    if( mutex() )
    {
        // @AP: we queue only the redraw request, timer will be started
        // again by __redrawMap() method.
        _isRedrawEvent = true;
        //qDebug("Map::__redrawMap(): mutex is locked, returning");
        return;
    }

    // set mutex to block recursive entries and unwanted data
    // modifications.

    setMutex(true);

    // Check, if a resize event is queued. In this case it must be
    // considered to create the right matrix size.
    if( _isResizeEvent )
    {
        _isResizeEvent = false;
        _globalMapMatrix->createMatrix(resizeEventSize);
        pixBuffer.resize(resizeEventSize);
        pixBuffer.fill(white);
    }

    curMapRot=mapRot;
    pixPlan.fill(white);

    //  pixAero.fill(white);
    //  pixAirspace.fill(white);
    //  pixGrid.fill(white);
    //  pixUnderMap.fill(black);
    pixIsoMap.fill(white);
    //  pixGrid.fill(white);
    //  pixFlight.fill(white);
    //  pixWaypoints.fill(white);

    //  bitMapMask.fill(Qt::color0);
    //  bitFlightMask.fill(Qt::color0);
    bitPlanMask.fill(Qt::color0);
    //  bitAirspaceMask.fill(Qt::color0);
    //  bitWaypointsMask.fill(Qt::color0);

    //  QPoint temp1(preCur1);
    //  QPoint temp2(preCur2);
    preCur1.setX(-50);
    preCur2.setX(-50);

    _globalMapContents->proofeSection();

    __drawMap();
    //  __drawFlight();
    __drawGrid();

    //copy the pixIsoMap (containing everyting belonging to the map) to the buffer
    pixBuffer=pixIsoMap;
    __drawLandableSites();
    __drawPlannedTask();
    __drawTrail();
    // Linie zum aktuellen Punkt lschen
    prePlanPos.setX(-999);
    prePlanPos.setY(-999);

    // unlock mutex
    setMutex(false);
    __showLayer();

    // @AP: check, if a pending redraw request is active. In this case
    // the scheduler timers will be restarted to handle it.

    if( _isRedrawEvent )
    {
        //qDebug("Map::__redrawMap(): queued redraw event found, schedule Redraw");
        _isRedrawEvent = false;
        sceduleRedraw();
    }

    // @AP: check, if a pending resize event exists. In this case the
    // scheduler timers will be restarted to handle it.
    if( _isResizeEvent )
    {
        //qDebug("Map::__redrawMap(): queued resize event found, schedule Redraw");
        sceduleRedraw();
    }


}

void Map::draw()
{
    slotRedrawMap();
}

// This slot is called, if one of the timers redrawTimerShort or
// redrawTimerLong is expired. It starts the redrawing of the map, if
// the mutx is not locked. In the other case the new redraw request
// will be queued only.


void Map::slotRedrawMap()
{
    //qDebug("Map::slotRedrawMap(): LoopLevel=%d", qApp->loopLevel());

    // @AP: Don't allows changes on matrix data during drawing!!!

    if( mutex() )
    {
        // @AP: we queue only the redraw request, timer will be
        // started again by __redrawMap() method.
        _isRedrawEvent = true;
        //qDebug("Map::slotRedrawMap(): is locked by mutex, returning");
        return;
    }

    // stop timers
    redrawTimerShort->stop();
    redrawTimerLong->stop();

    // load map data
    _globalMapMatrix->slotSetScale(zoomFactor);
    _globalMapMatrix->slotCenterTo(curPos.x(),curPos.y());
    zoomFactor=_globalMapMatrix->getScale();
    _globalMapMatrix->createMatrix(this->size());
    //emit changed(this->size());
    __redrawMap();
}

void Map::__showLayer()
{
    /*
    double a=M_PI/180 * curMapRot;
    double sina = sin(a);
    double cosa = cos(a);
    int Rx=pixIsoMap.width()/2;
    int Ry=pixIsoMap.height()/2;
    double dx=-Rx*cosa-Ry*sina-Rx;
    double dy=Ry-Ry*cosa-Rx*sina;

    QWMatrix m(cosa, sina, -sina, cosa, dx,dy);
    pixBuffer = pixIsoMap.xForm(m);
    */

    //copy the pixAero (containing everyting belonging to the map) to the buffer
    pixBuffer=pixAero;

    //overlay the landable sites and the waypoints
    //pixAero.setMask(bitAeroMask);
    //bitBlt(&pixBuffer, 0, 0, &pixAero);

    //overlay the flightplan
    #warning FIXME: bitblt-ing with a mask is slow. This is not really needed here, the task can move to the map below.
    if (_globalMapContents->getCurrentTask()!=0)
    { //we only need to do this if we actually have a task planned. Saves a lot of time not to do it.
      pixPlan.setMask(bitPlanMask);
      bitBlt(&pixBuffer, 0, 0, &pixPlan);
    }
    
    // This can change so draw it here
    __drawWindArrow();

    //draw a location symbol on the buffer
    if (ShowGlider)
    {
        __drawGlider();
    } else {
        __drawX();
    }

    // and finaly draw a scale indicator on top of this
    __drawScale();

    
    // The paint event will take care of actually using the buffer.
    paintEvent();
}

void Map::slotCenterToWaypoint(const unsigned int) // id)
{
    // qDebug("Map::slotCenterToWaypoint");
    /*
      if(id >= _globalMapContents->getFlight()->getWPList().count())
        {
          warning("KFLog: Map::slotCenterToWaypoint: wrong Waypoint-ID");
          return;
        }

      _globalMapMatrix->centerToPoint(_globalMapMatrix->map(
          _globalMapContents->getFlight()->getWPList().at(id)->projP));
      _globalMapMatrix->slotSetScale(_globalMapMatrix->getScale(MapMatrix::LowerLimit));

      emit changed(this->size());
    */
}

void Map::slotCenterToFlight()
{
    // qDebug("Map::slotCenterToFlight");
    /*
      unsigned int i;

      Flight *f = (Flight *)_globalMapContents->getFlight();
      if (f) {
        QRect r;
        QRect r2;
        QList<Flight> fl;

        switch (f->getTypeID()) {
          case BaseMapElement::Flight:
            r = f->getFlightRect();
            break;
          case BaseMapElement::FlightGroup:
            fl = ((FlightGroup *)f)->getFlightList();
            r = fl.at(0)->getFlightRect();
            for (i = 1; i < fl.count(); i++) {
              r2 = fl.at(i)->getFlightRect();
              r.setLeft(QMIN(r.left(), r2.left()));
              r.setTop(QMIN(r.top(), r2.top()));
              r.setRight(QMAX(r.right(), r2.right()));
              r.setBottom(QMAX(r.bottom(), r2.bottom()));
            }
            break;
          default:
            return;
        }

        // check if the Rectangle is zero
        // is it necessary here?
        if (!r.isNull()) {
          _globalMapMatrix->centerToRect(r);
          _globalMapMatrix->createMatrix(this->size());
          __redrawMap();
        }

      emit changed(this->size());
      }
    */
}

void Map::slotCenterToTask()
{
    // qDebug("Map::slotCenterToTask");
    /*
      unsigned int i;
      BaseFlightElement *f = _globalMapContents->getFlight();

      if(f)
        {
          QRect r;
          QRect r2;
          QList<Flight> fl;

          switch (f->getTypeID())
            {
              case BaseMapElement::Flight:
                r = ((Flight *)f)->getTaskRect();
                break;
              case BaseMapElement::Task:
                r = ((FlightTask *)f)->getRect();
                break;
              case BaseMapElement::FlightGroup:
                fl = ((FlightGroup *)f)->getFlightList();
                r = fl.at(0)->getTaskRect();
                for (i = 1; i < fl.count(); i++) {
                  r2 = fl.at(i)->getTaskRect();
                  r.setLeft(QMIN(r.left(), r2.left()));
                  r.setTop(QMIN(r.top(), r2.top()));
                  r.setRight(QMAX(r.right(), r2.right()));
                  r.setBottom(QMAX(r.bottom(), r2.bottom()));
                }
                break;
              default:
                return;
            }

          // check if the Rectangle is zero
          if(!r.isNull())
            {
              _globalMapMatrix->centerToRect(r);
              _globalMapMatrix->createMatrix(this->size());
              __redrawMap();

              emit changed(this->size());
            }
        }
    */
}



/** search for a waypoint
First look in task itself
Second look in map contents */
bool Map::__getTaskWaypoint(QPoint current, struct wayPoint *wp, QList<wayPoint> &taskPointList)
{
    unsigned int i;
    struct wayPoint *tmpPoint;
    QPoint sitePos;
    double dX, dY;
    // Radius for Mouse Snapping
    double delta(12.0);
    if(_globalMapMatrix->isSwitchScale())
        delta = 6.0;

    bool found = false;
    RadioPoint *hitElement;

    for(i = 0; i < taskPointList.count(); i++)
    {
        tmpPoint = taskPointList.at(i);
        sitePos = _globalMapMatrix->map(_globalMapMatrix->wgsToMap(tmpPoint->origP));
        dX = abs(sitePos.x() - current.x());
        dY = abs(sitePos.y() - current.y());

        // Abstand entspricht der Icon-Groesse
        if (dX < delta && dY < delta)
        {
            *wp = *tmpPoint;
            found = true;
            break;
        }
    }

    if(!found)
    {
        /*
         *  Should be done for all Point-data
         */
        QArray<int> contentArray(2);
        contentArray.at(0) = MapContents::GliderList;
        contentArray.at(1) = MapContents::AirportList;

        for(unsigned int n = 0; n < contentArray.count(); n++)
        {
            for(unsigned int loop = 0; loop < _globalMapContents->getListLength(contentArray.at(n)); loop++)
            {
                hitElement = (RadioPoint*)_globalMapContents->getElement(contentArray.at(n), loop);
                sitePos = hitElement->getMapPosition();
                dX = abs(sitePos.x() - current.x());
                dY = abs(sitePos.y() - current.y());

                if (dX < delta && dY < delta)
                {
                    wp->name = hitElement->getWPName();
                    wp->origP = hitElement->getWGSPosition();
                    wp->elevation = hitElement->getElevation();
                    wp->projP = hitElement->getPosition();
                    wp->description = hitElement->getName();
                    wp->type = hitElement->getTypeID();
                    wp->elevation = hitElement->getElevation();
                    wp->icao = hitElement->getICAO();
                    wp->frequency = hitElement->getFrequency().toDouble();
                    wp->runway = 0;
                    wp->length = 0;
                    wp->surface = 0;
                    wp->comment = "";
                    wp->isLandable = true;

                    found = true;
                    break;
                }
            }
        }
    }
    return found;
}


/** Draws the waypoints of the active waypoint catalog on the map */
void Map::__drawWaypoints(QPainter* wpPainter, QPainter * /*maskP*/){
    int i, n;
    QList<wayPoint> * wpList;
    wayPoint * wp;
    bool isSelected;
    QRect testRect(-100,-100,500,500);
    QString labelText;
    Altitude alt;
    Distance dist;
    
    wpList = _globalMapContents->getWaypointList();
    extern MapConfig * _globalMapConfig;

    wpPainter->setBrush(NoBrush);

    // now do complete list
    n =  wpList->count();
    for (i=0; i < n; i++){
        wp = (wayPoint*)wpList->at(i);

        //show now only is used for the currently selected wp, but could also be used for waypoints in the task or other super-important waypoints later on.
        isSelected=false;
        if (calculator!=0 && calculator->getselectedWp()!=0 && wp != 0 )
            if (calculator->getselectedWp()->name == wp->name )
                isSelected = true;

            if( _globalMapMatrix->getScale(MapMatrix::CurrentScale) > 1024.0 && ! isSelected )
            {
              // Don't draw waypoints at this high scale
              continue;
            }

        if ((wp->importance >= _globalMapMatrix->currentDrawScale()) || isSelected) { //if the waypoint is important enough for the current mapscale
            // make sure projection is ok, and map to screen

            wp->projP = _globalMapMatrix->wgsToMap(wp->origP.lat(), wp->origP.lon());
            QPoint P = _globalMapMatrix->map(wp->projP);

            if (testRect.contains(P)) {
                // draw marker
                // QColor col = ReachableList::getReachColor(wp->name);
                reachable reachable=ReachableList::getReachable(wp->origP);
                if( isSelected ) {
                    wpPainter->setPen(QPen(Qt::white, 3, SolidLine));
                } else {
                    wpPainter->setPen(QPen(Qt::black, 2, SolidLine));
                }
                QPixmap pm;
                if( _globalMapConfig->isRotatable(wp->type) )
                  pm = _globalMapConfig->getPixmapRotatable( wp->type,false );
                else
                  pm= _globalMapConfig->getPixmap(wp->type,false); //,col);

                int xOffset=16;
                int yOffset=16;
                int iconSize = 32;
                if (_globalMapConfig->useSmallIcons())
                  { xOffset=8; yOffset=8; iconSize = 16; }

                if (reachable == yes) {
                  //draw green circle
                  wpPainter->drawPixmap(P.x() - 9, P.y() -9, _globalMapConfig->getPixmap("green_circle.xpm"));
                } else if (reachable == belowSavety) {
                  //draw magenta circle
                  wpPainter->drawPixmap(P.x() - 9, P.y() -9, _globalMapConfig->getPixmap("magenta_circle.xpm"));
                }

                int rw2 = wp->runway >= 180 ? wp->runway-180 : wp->runway;
                int shift = ((rw2)/10);
                if( _globalMapConfig->isRotatable(wp->type) )
                  wpPainter->drawPixmap(P.x() - iconSize/2, P.y() - iconSize/2, pm,
                                shift*iconSize, 0, iconSize, iconSize);
                else
                  wpPainter->drawPixmap(P.x()-xOffset,P.y()-yOffset, pm);

                // draw name of wp
                if (_globalMapConfig->getShowWpLabels()) {
                    // do we need to show the labels at all?
                    labelText=wp->name;
                    if ( _globalMapConfig->getShowWpLabelsExtraInfo() ) { //just the name, or also additional info?
                      if ( wp->isLandable ) {
                        if (reachable==yes) { //reachable? then the label will become bold
                          labelText = "<qt><b>" + labelText + "</b><br>";
                        } else {
                          labelText = "<qt>" + labelText + "<br>";
                        }

                        dist = ReachableList::getDistance( wp->origP );
                        if ( dist.isValid() ) { //check if the distance is valid...
                          labelText += dist.getText( false, uint(0), uint(0) )
                                    +  "/";
                          alt = ReachableList::getArrivalAltitude( wp->origP );
                          if ( alt.getMeters()<0 ) //the color is dependent on the arrival altitude
                          {
                            labelText += "<font color=\"#FF0000\">"
                                      + alt.getText( false, 0 )
                                      + "</font></qt>";
                          } else {
                            labelText += "&nbsp;"
                                      + alt.getText( false, 0 )
                                      + "</qt>";
                          }
                        }
                      }
                    } else {
                      if ( wp->isLandable ) {
                        if (reachable==yes) {
                          labelText = "<qt><b>" + labelText + "</b></qt>";
                        }
                      }
                    }
                                  
                    QSimpleRichText rtext ( labelText, this->font() );
                    rtext.adjustSize();
                      
                    QRect textbox( 0, 0, rtext.widthUsed(), rtext.height() );
                    if (wp->origP.lon()<_globalMapMatrix->getMapCenter(false).y()) {
                        //the wp is on the left side of the map, so draw the textlabel on the right side
                        xOffset=10;
                        yOffset=(textbox.height()/4)*3;
                        if (_globalMapConfig->useSmallIcons()){
                            xOffset=6;
                            yOffset=-0;
                        }
                    } else {
                        //the wp is on the right side of the map, so draw the textlabel on the left side
                        xOffset=-textbox.width()-14;
                        yOffset=(textbox.height()/4)*3;
                        if (_globalMapConfig->useSmallIcons()){
                            xOffset=-textbox.width()-6;
                            yOffset=0;
                        }
                    }
                    
                    QColorGroup cg;

                    if (!isSelected) {
                        wpPainter->fillRect(P.x() + xOffset - 1, P.y() + yOffset + 1, textbox.width() + 2, -textbox.height() + 1, Qt::white);
                        cg.setColor( QColorGroup::Background, Qt::white );
                        cg.setColor( QColorGroup::Foreground, Qt::black );
                        cg.setColor( QColorGroup::Text, Qt::black );
                    } else {
                        wpPainter->fillRect(P.x() + xOffset - 1, P.y() + yOffset + 1 , textbox.width() + 2, -textbox.height() + 1, Qt::black);
                        cg.setColor( QColorGroup::Background, Qt::black );
                        cg.setColor( QColorGroup::Foreground, Qt::white );
                        cg.setColor( QColorGroup::Text, Qt::white );
                    }

                    rtext.draw(wpPainter, 
                               P.x() + xOffset + 1, 
                               P.y() + yOffset - textbox.height() + 2,
                               QRect( 0, 0, width() + 2, height() + 2 ), //textbox, 
                               cg); 
                }//draw labels?
            } //in visible area?
        } //important enough?
    } //for loop
}


/** Slot signalled when user selects another waypointcatalog.  */
void Map::slotWaypointCatalogChanged(WaypointCatalog* c)
{
    // qDebug("Map::slotWaypointCatalogChanged");
    emit changed(this->size());
    __redrawMap();
}

/** This function sets the maprotation and redraws the map if the maprotation differs too much from the current maprotation. */
void Map::setMapRot(int newRotation)
{
    // qDebug("Map::setMapRot");
    mapRot=newRotation;
    if (abs(mapRot-curMapRot) >= 2) {
        __redrawMap();
    }
}

/** Read property of int heading. */
const int& Map::getHeading(){
    return heading;
}

/** Write property of int heading. */
void Map::setHeading( const int& _newVal){
    heading = _newVal;
    setMapRot(calcMapRotation());
    __showLayer();
}

/** Read property of int bearing. */
const int& Map::getBearing(){
    return bearing;
}

/** Write property of int bearing. */
void Map::setBearing( const int& _newVal){
    bearing = _newVal;
    setMapRot(calcMapRotation());
}

/** calculates the maprotation based on the mapmode, the heading and the bearing. In degrees counterclockwise. */
int Map::calcMapRotation(){
    switch (mode) {
    case northUp:
        return 0;
    case headUp:
        return -heading;
    case trackUp:
        return -bearing;
    default:
        return 0;
    }
}

/** calculates the rotation of the glidersymbol based on the mapmode, the heading and the bearing. In degrees counterclockwise. */
int Map::calcGliderRotation(){
    switch (mode) {
    case northUp:
        return heading;
    case headUp:
        return 0;
    case trackUp:
        return heading-bearing;
    default:
        return heading;
    }
}

/** Read property of mapMode mode. */
Map::mapMode Map::getMode() const
{
    return mode;
}

/** Write property of mapMode mode. */
void Map::setMode( const mapMode& ){ //_newVal){
    //  mode = _newVal;
    mode = Map::northUp; //other modes are not supported yet
    setMapRot(calcMapRotation());
}

/** Write property of bool ShowGlider. */
void Map::setShowGlider( const bool& _newVal){
    if (ShowGlider!=_newVal) {
        ShowGlider = _newVal;
        __showLayer();
    }

}

/** Draws a scale indicator on the pixmap. */
void Map::__drawScale(){
    QPainter scaleP(&pixBuffer);

    QPen pen;
    QBrush brush(white);

    //  pen.setColor(QColor(10,10,10));
    pen.setColor(Qt::black);
    pen.setWidth(3);
    pen.setCapStyle(RoundCap);
    scaleP.setPen(pen);

    double scale = _globalMapMatrix->getScale(MapMatrix::CurrentScale);
    Distance barLen;

    /** select appropriate length of bar. This needs to be done for each unit
      * seperately, because else you'd get weird, broken barlengts.
      * Note: not all possible units are taken into account (meters, feet),
      * because we are not going to use these for horizontal distances (at least
      * not externaly.) */
    int len=1;
    switch (barLen.getUnit()) {
    case Distance::kilometers:
        len = 100;
        if (scale<1000) len=50;
        if (scale<475) len=25;
        if (scale<240) len=10;
        if (scale<100) len=5;
        if (scale<50) len=3;
        if (scale<20) len=1;
        barLen.setKilometers(len);
        break;

    case Distance::miles:
        len=60;
        if (scale<1000) len=30;
        if (scale<475) len=12;
        if (scale<200) len=6;
        if (scale<95) len=4;
        if (scale<60) len=2;
        if (scale<30) len=1;
        barLen.setMiles(len);
        break;

    case Distance::nautmiles:
        len=50;
        if (scale<1000) len=25;
        if (scale<450) len=10;
        if (scale<175) len=5;
        if (scale<90) len=3;
        if (scale<55) len=1;
        barLen.setNautMiles(len);
        break;

    default: //should not happen, other units are not used for horizontal distances.
        len = 100;
        if (scale<1000) len=50;
        if (scale<475) len=25;
        if (scale<240) len=10;
        if (scale<100) len=5;
        if (scale<50) len=3;
        if (scale<20) len=1;
        barLen.setKilometers(len);
        break;

    };

    //determine how long the bar should be in pixels
    int drawLength = (int)(barLen.getMeters()/scale);
    //...and where to start drawing.
    int leftXPos=this->width()-drawLength-5;
    //Now, draw the bar
    scaleP.drawLine(leftXPos,this->height()-5,this->width()-5,this->height()-5);       //main bar
    pen.setWidth(2);
    scaleP.setPen(pen);
    scaleP.drawLine(leftXPos,this->height()-9,leftXPos,this->height()-1);              //left endbar
    scaleP.drawLine(this->width()-5,this->height()-9,this->width()-5,this->height()-1);//right endbar

    //get the string to draw
    QString scaleText=barLen.getText(true,0);
    //get some metrics for this string
    QRect txtRect=scaleP.fontMetrics().boundingRect(scaleText);
    int leftTPos=this->width()+int((drawLength-txtRect.width())/2)-drawLength-5;

    //draw white box to draw text on
    scaleP.setBrush(brush);
    scaleP.setPen(NoPen);
    scaleP.drawRect(leftTPos,this->height()-txtRect.height()+2, txtRect.width(), txtRect.height()-2);

    //draw text itself
    scaleP.setPen(pen);
    scaleP.drawText(leftTPos,this->height()-7+txtRect.height()/2,scaleText);
    scaleP.end();
}

/** This slot is called to set a new position. The map object determines if it is necessary to recenter the map, or if the glider can just be drawn on a different position. */


void Map::slot_position(const QPoint& newPos)
{
    // qDebug("Map::slot_position x:%d y:%d",newPos.x(), newPos.y() );
    if( !_isEnable )
        return;
    if (curPos!=newPos) {
        curPos=newPos;
        if (!_globalMapMatrix->isInCenterArea(newPos)) {
            // qDebug("Map::slot_position:sceduleRedraw()");
            sceduleRedraw();
        } else {
            __showLayer();
        }
    }
}

/** Draws the glidersymbol on the pixmap */
void Map::__drawGlider()
{
    //get the projected coordinates of the current position
    QPoint projPos=_globalMapMatrix->wgsToMap(curPos);
    //map them to a coordinate on the pixmap
    int Rx=_globalMapMatrix->map(projPos).x();
    int Ry=_globalMapMatrix->map(projPos).y();
    //don't continue if we get a bizarre result
    if (Rx < -10000) return;
    if (Rx > 10000) return;

    int rot=calcGliderRotation();
    rot=((rot+7)/15) % 24;  //we only want to rotate in steps of 15 degrees. Finer is not usefull.

    //now, draw the line from the X symbol to the waypoint
    __drawDirectionLine(QPoint(Rx,Ry));

    /** @ee the glider pixmap contains all rotated glider symbols. */
    bitBlt(&pixBuffer, Rx-20, Ry-20, &_glider, rot*40, 0, 40, 40);
}

/** Draws the Xsymbol on the pixmap */
void Map::__drawX()
{
    //get the projected coordinates of the current position
    QPoint projPos=_globalMapMatrix->wgsToMap(curPos);
    //map them to a coordinate on the pixmap
    int Rx=_globalMapMatrix->map(projPos).x();
    int Ry=_globalMapMatrix->map(projPos).y();
    //don't continue if we get a bizarre result
    if (Rx < -10000) return;
    if (Rx > 10000) return;

    //now, draw the line from the X symbol to the waypoint
    __drawDirectionLine(QPoint(Rx,Ry));

    /** @ee bitblt preloaded pixmap */
    bitBlt(&pixBuffer, Rx-10, Ry-10, &_cross);


}

/** Used to zoom in on the map. Will schedule a redraw. */
void Map::slotZoomIn(){

    // @AP: block zoom events, if map is redrawn
    if( mutex() )
    {
        //qDebug("Map::slotZoomIn(): mutex is locked, returning");
        return;
    }

    if( _globalMapMatrix->getScale() == _globalMapMatrix->getScale(MapMatrix::LowerLimit) )
    {
        // @AP: lower limit reached, ignore event
        return;
    }

    if( zoomProgressive < 7 && redrawTimerShort->isActive() )
      zoomProgressive++;
    else if( zoomProgressive == 7 && redrawTimerShort->isActive() )
      return;
    else 
      zoomProgressive=0;

    zoomFactor/=zoomProgressiveVal[zoomProgressive];

    sceduleRedraw();
    QString msg;
    msg.sprintf(tr("Map zoom in, scale: %.1f"),zoomFactor/L_LIMIT ); 
    Global::statusMessage(tr(msg));
}

/** Used . Will schedule a redraw. */
void Map::slotRedraw(){
    // qDebug("Map::slotRedraw");
    sceduleRedraw();
}

/** Used to zoom the map out. Will scedule a redraw. */
void Map::slotZoomOut(){

    // @AP: block zoom events, if map is redrawn
    if( mutex() )
    {
        //qDebug("Map::slotZoomOut(): mutex is locked, returning");
        return;
    }

    if( _globalMapMatrix->getScale() == _globalMapMatrix->getScale(MapMatrix::UpperLimit) )
    {
        // @AP: upper limit reached, ignore event
        return;
    }

    if( zoomProgressive < 7 && redrawTimerShort->isActive() )
      zoomProgressive++;
    else if( zoomProgressive == 7 && redrawTimerShort->isActive() )
      return;
    else
      zoomProgressive=0;

    zoomFactor*=zoomProgressiveVal[zoomProgressive];

    sceduleRedraw();
    QString msg;
    msg.sprintf(tr("Map zoom out, scale: %.1f"),zoomFactor/L_LIMIT ); 

    Global::statusMessage(tr(msg));
}

/**
 * This function scedules a redraw of the map. It sets two timers:
 * The first timer is set for a small interval, and reset every time sceduleRedraw
 * is called. This allows for several modifications to the map being used for the
 * redraw at once.
 * The second timer is set for a larger interval, and is not reset. It makes sure
 * the redraw occurs once in awhile, even if events modifying the map keep comming
 * in and would otherwise prevent the map from being redrawn.
 *
 * If either of the two timers times out, the status of redrawSceduled is reset
 * and the map is redrawn to reflect the current position and zoom factor.
 */
void Map::sceduleRedraw(){
    //qDebug("Map::sceduleRedraw(): loopLevel=%d", qApp->loopLevel() );
    if( !_isEnable )
    {
        _isRedrawEvent = false;
        return;
    }

    if( mutex() )
    {
        // Map drawing is running therefore queue redraw request
        // only. The timers will be restarted at the end of the
        // drawing routine, if the _isRedrawEvent flag is set.
        _isRedrawEvent = true;
        return;
    }

    // qDebug("Map::sceduleRedraw");
    if (!redrawTimerLong->isActive() && ShowGlider) {
        redrawTimerLong->start(1500, true);

    }

    redrawTimerShort->start(800, true);
}

/** sets a new scale */
void Map::slotSetScale(const double& newScale){
    // qDebug("Map::slotSetScale");
    if (newScale!=zoomFactor) {
        zoomFactor=newScale;
        sceduleRedraw();
    }
}

/**
  * This function draws a "directionline" on the map if a waypoint has been
  * selected. The QPoint is the projected & mapped coordinate of the positionsymbol
  * on the map, so we don't have to calculate that all over again.
  */

void Map::__drawDirectionLine(const QPoint& from){
    if (!_globalMapConfig->getdrawBearing()) return;

    if (calculator && calculator->getselectedWp()!=0) {
        QPoint to=_globalMapMatrix->map(_globalMapMatrix->wgsToMap(calculator->getselectedWp()->origP));
        QColor col = ReachableList::getReachColor(calculator->getselectedWp()->origP);
        CuPainter lineP(&pixBuffer); //we are using the customized QPainter CuPainter for safe drawing

        lineP.setPen(QPen(col, 3, DashLine));
        lineP.drawLine(from, to, true);
        lineP.end();
    }
}

/** check if the new position is near to an airspace.
  */


void Map::checkAirspace(const QPoint& pos)
{
    if( mutex() )
    {
        // qDebug("Map::checkAirspace: Map drawing in progress: return");
        return;
    }

    if (!GeneralConfig::instance()->getAirspaceWarningEnabled()) return;

    // qDebug("Map::checkAirspace()");
  bool warn=false;
  // qDebug("Map::checkAirspace()");

    QPoint projPos=_globalMapMatrix->map(_globalMapMatrix->wgsToMap(pos));
    // dont know why this happens, but it does.
    if (projPos.x() > 10000)
        return;
    if (projPos.x() < -10000)
        return;

    double scale = _globalMapMatrix->getScale(MapMatrix::CurrentScale);
    Distance dist;
    AirspaceWarningDistance awd=GeneralConfig::instance()->getAirspaceWarningDistances();

    dist=awd.horClose * 2;
    int projDist = (int)(dist.getMeters()/scale);
    QRegion regNear (projPos.x()-projDist/2, projPos.y()-projDist/2, projDist, projDist, QRegion::Ellipse);

    dist=awd.horVeryClose*2;
    projDist = (int)(dist.getMeters()/scale);
    QRegion regVeryNear (projPos.x()-projDist/2, projPos.y()-projDist/2, projDist, projDist, QRegion::Ellipse);

    QString inside("");
    QString veryNear("");
    QString near("");

    AltitudeCollection alt=calculator->getAltitudeCollection();
    Airspace* pSpace;
    Airspace::ConflictType hConflict=Airspace::none, vConflict=Airspace::none, conflict=Airspace::none;

    // check if there are overlaps between the region around our current position and airspaces
    for(unsigned int loop = 0; loop < airspaceRegList.count(); loop++)
    {
        pSpace = airspaceRegList.at(loop)->airspace;
        vConflict=pSpace->conflicts(alt, awd);

        if (vConflict>Airspace::none) {
            if (airspaceRegList.at(loop)->region->contains(projPos)) {
               //horizontally, we are inside
               hConflict=Airspace::inside;
            } else if(!airspaceRegList.at(loop)->region->intersect(regVeryNear).isEmpty()) {
               hConflict=Airspace::veryNear;
            } else if(!airspaceRegList.at(loop)->region->intersect(regNear).isEmpty()) {
               hConflict=Airspace::near;
            }
            conflict=(hConflict<vConflict ? hConflict : vConflict); //the resulting conflict is always the lesser of the two

            if (GeneralConfig::instance()->getAirspaceWarningEnabled(pSpace->getTypeID()) && conflict>Airspace::none) warn=true;

            switch (conflict) {
            case Airspace::none:
              break;

            case Airspace::near:
              if (near.isEmpty())
                near = tr("Near") + " " + Airspace::getTypeName(pSpace->getTypeID());
              break;

            case Airspace::veryNear:
              if (veryNear.isEmpty())
                veryNear = tr("Very near") + " " + Airspace::getTypeName(pSpace->getTypeID());
              break;
            case Airspace::inside:
              if (inside.isEmpty())
                inside = tr("Inside") + " " + Airspace::getTypeName(pSpace->getTypeID());
              break;
            }
        }
    }

    bool changed = ((near != _near) || (veryNear != _veryNear) || (inside != _inside));
    _near=near;
    _veryNear=veryNear;
    _inside=inside;

    if (changed)
    {
        QString text = "<qt><p align=\"center\"><B><nobr>" +
	  tr("Airspace Warning") +
	  "</nobr></B></p><hr>";

        QString msg;

	// @AP: Only the highest priority will be displayed!

        if (!inside.isEmpty())
        {
            text += "<p align=\"center\">";
            msg += inside + " ";
            text += inside + "</p>";
        }
        else if (!veryNear.isEmpty())
        {
            text += "<p align=\"center\">";
            msg += veryNear + " ";
            text += veryNear + "</p>";
        }
        else if (!near.isEmpty())
        {
            text += "<p align=\"center\">";
            msg += near + " ";
            text += near + "</p>";
        }

        text += "</qt>";

        emit airspaceWarning (msg);

//#warning: FIXME replaced the popup with a status message, because the popup can crash Cumulus

        if (warn) Global::statusMessage(msg);

	GeneralConfig *conf = GeneralConfig::instance();

	// @AP: popup windows have made a lot of trouble (system
	// crashes), therefore they are disabled per default.

        if( warn && conf->getPopupAirspaceWarnings() )
	  {
            int showTime = conf->getWarningDisplayTime() * 1000;
	    
            // qDebug("airspace warning timer %dms, LoopLevel=%d",
            // showTime, qApp->loopLevel() );
            WhatsThat * box=new WhatsThat(this, text, 0, "", showTime);
            box->show();
            return;
	  }
    }
}

void Map::quickDraw() {
   if (mutex()) return;

   setMutex(true);
    //copy the pixIsoMap (containing everyting belonging to the map) to the buffer
    //pixBuffer=pixIsoMap;
    __drawLandableSites();
    __drawPlannedTask();
    __drawTrail();
    setMutex(false);
    __showLayer();
}

bool Map::mutex() {
  return _mutex;
}

void Map::setMutex(bool m) {
  _mutex=m;      
  emit isRedrawing(m);
}

bool Map::eventFilter(QObject * , QEvent * ) {
  if (_mutex) {
    //qDebug("eventfilter blocked event.");
    return true; //stop all events while we're redrawing the map!
  }
  return false;
}

AirRegion::AirRegion(QRegion * reg, Airspace * air) {
  region=reg;
  airspace=air;
}

AirRegion::~AirRegion() {
  delete region;
}

