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

#include <cmath>
#include <stdlib.h>
#include <unistd.h>

//#include <qpe/config.h>

#include <qdatastream.h>
#include <qbuffer.h>
#include <qdatetime.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qprogressdialog.h>
#include <qregexp.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qdom.h>
#include <qregion.h>
#include <qpe/global.h>

#include <iostream>

#include "mapcontents.h"
#include "mapmatrix.h"
#include "mapcalc.h"
#include "airport.h"
#include "airspace.h"
#include "basemapelement.h"
#include "glidersite.h"
#include "isohypse.h"
#include "lineelement.h"
#include "radiopoint.h"
#include "singlepoint.h"
#include "projectionbase.h"
#include "wgspoint.h"
#include "filetools.h"
#include "hwinfo.h"

#define OLD_AIRFIELD_FORMAT   // controls fix for twisted rwDirection and rwLength

#define MAX_FILE_COUNT 16200
#define ISO_LINE_NUM 50

//general KFLOG file token: @KFL
#define KFLOG_FILE_MAGIC    0x404b464c
//uncompiled mapfiles
#define FILE_TYPE_GROUND      0x47
#define FILE_TYPE_TERRAIN     0x54
#define FILE_TYPE_MAP         0x4d
#define FILE_TYPE_AERO        0x41
//compiled mapfiles
#define FILE_TYPE_GROUND_C    0x67
#define FILE_TYPE_TERRAIN_C   0x74
#define FILE_TYPE_MAP_C       0x6d
#define FILE_TYPE_AIRSPACE_C  0x61  //aero files are split up into airspace and airfield files
#define FILE_TYPE_AIRFIELD_C  0x62
//versions
#define FILE_FORMAT_ID        101
#define FILE_VERSION_GROUND   100

#define FILE_VERSION_TERRAIN  100

#define FILE_VERSION_MAP      101

#define FILE_VERSION_AIRSPACE 101

#define FILE_VERSION_AIRFIELD 101

//compiled version

#define FILE_VERSION_GROUND_C   102

#define FILE_VERSION_TERRAIN_C  102

#define FILE_VERSION_MAP_C      102

#define FILE_VERSION_AIRSPACE_C 102

#define FILE_VERSION_AIRFIELD_C 103


#define CHECK_BORDER if(i == 0) { \
	border.north = lat_temp;   border.south = lat_temp; \
	border.east = lon_temp;    border.west = lon_temp; \
} else { \
	border.north = MAX(border.north, lat_temp); \
	border.south = MIN(border.south, lat_temp); \
	border.east = MAX(border.east, lon_temp); \
	border.west = MIN(border.west, lon_temp); \
}

#define READ_POINT_LIST\
	if (compiling) {\
		in >> locLength;\
		tA.resize(locLength);\
		for(unsigned int i = 0; i < locLength; i++) { \
			in >> lat_temp;          in >> lon_temp; \
			tA.setPoint(i, _globalMapMatrix->wgsToMap(lat_temp, lon_temp)); \
		}\
		ShortSave(out, tA);\
	} else\
		ShortLoad(in, tA);\

#define READ_CONTACT_DATA in >> contactCount; \
if ( compiling ) outbuf << contactCount; \
for(unsigned int loop = 0; loop < contactCount; loop++) { \
	if ( compiling ) { \
		in >> frequency; \
		in >> contactType; \
		in >> callSign; \
		iFreq=frequency.left(3).toInt(&ok)*1000+frequency.right(3).toInt(&ok2)-100000; \
		if (ok && ok2) { \
		} else { \
			iFreq=0; \
			qWarning("Conversion NOT ok!"); \
		} \
		outbuf << iFreq; \
		outbuf << contactType; \
		ShortSave(outbuf, callSign.utf8()); \
	}  else { \
		in >> iFreq; \
		if (iFreq>0) { \
			frequency.sprintf("%3d.%03d",(iFreq+100000)/1000,(iFreq)%1000);\
		} else { \
			frequency=tr("unknown frequency"); \
		} \
		in >> contactType; \
		ShortLoad (in, utf8_temp); \
		callSign.fromUtf8(utf8_temp); \
	}\
\
}

#define READ_RUNWAY_DATA in >> rwCount; \
if ( compiling ) outbuf << rwCount; \
for(unsigned int loop = 0; loop < rwCount; loop++) { \
        Q_UINT8  dir; \
        Q_UINT16 len; \
	in >> dir; \
	in >> len; \
        if( formatID == FILE_VERSION_AIRFIELD-1 || \
            formatID == FILE_VERSION_AIRFIELD_C-1 ) { \
	    rwDirection = len; \
	    rwLength = dir; \
	    len = rwLength; \
	    dir = rwDirection/10; } \
        else { \
	    rwDirection = dir*10; \
	    rwLength = len; } \
	in >> rwMaterial; \
	in >> rwOpen; \
	if ( compiling ) { \
		outbuf << dir; \
		outbuf << len; \
		outbuf << rwMaterial; \
		outbuf << rwOpen; \
	} \
}

//minimum amount of required free memory to start loading a mapfile
#define MINIMUM_FREE_MEMORY 1024*4

// List of altitude-levels (50 in total):
const int MapContents::isoLines[] = { 0, 10, 25, 50, 75, 100, 150, 200, 250,
                                      300, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1250, 1500, 1750,
                                      2000, 2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000, 4250, 4500,
                                      4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7250,
                                      7500, 7750, 8000, 8250, 8500, 8750};

MapContents::MapContents(QObject* parent, WaitScreen * waitscreen)
        : QObject(parent), isFirst(true)
{
    ws=waitscreen;

    ws->slot_SetText1(tr("Loading map contents..."));

    sectionArray.resize(MAX_FILE_COUNT);
    sectionArray.fill(false);

    // Wir nehmen zunächst 4 Schachtelungstiefen an ...
    for(unsigned int loop = 0; loop < ( ISO_LINE_NUM * 4 ); loop++)
      {
        QList<Isohypse> *list = new QList<Isohypse>;
        list->setAutoDelete(true);
        isoList.append(list);
      }

    airportList.setAutoDelete(true);
    airspaceList.setAutoDelete(true);
    cityList.setAutoDelete(true);
    gliderList.setAutoDelete(true);
    //  flightList.setAutoDelete(true);
    hydroList.setAutoDelete(true);
    lakeList.setAutoDelete(true);
    isoList.setAutoDelete(true);
    landmarkList.setAutoDelete(true);
    navList.setAutoDelete(true);
    obstacleList.setAutoDelete(true);
    outList.setAutoDelete(true);
    railList.setAutoDelete(true);
    regIsoLines.setAutoDelete(true);
    _nextIsoLevel=10000;
    _lastIsoLevel=-1;
    _isoLevelReset=true;
    _lastIsoEntry=0;
    reportList.setAutoDelete(true);
    highwayList.setAutoDelete(true);
    roadList.setAutoDelete(true);
    //  stationList.setAutoDelete(true);
    topoList.setAutoDelete(true);
    villageList.setAutoDelete(true);
    wpList.setAutoDelete(true);

    currentTask=0;

    connect(this,SIGNAL(progress(int)),ws,SLOT(slot_Progress(int)));
    connect(this,SIGNAL(loadingFile(const QString&)),ws,SLOT(slot_SetText2(const QString&)));
    connect(this,SIGNAL(majorAction(const QString&)),ws,SLOT(slot_SetText1(const QString&)));
    qDebug("MapContents initialized...");
}

MapContents::~MapContents()
{
}

// @AP: only used by __readAsciiFile() method and this is switched off
// currently

int MapContents::degreeToNum(QString inDegree)
{
    /*
     * needed formats:
     *
     *  [g]gg mm' ss"
     *  [g]gg mm.mmmm'
     *  dddddddddd
     */
    QRegExp degreeDMS("^[0-9]?[0-9][0-9][ ]*[0-9][0-9]'[ ]*[0-9][0-9]\"");
    QRegExp degreeDMM("^[0-9]?[0-9][0-9][ ]*[0-9][0-9].[0-9][0-9][0-9]'");
    QRegExp number("^-?[0-9]+$");

    if(number.match(inDegree) != -1)
    {
        return inDegree.toInt();
    }
    else if(degreeDMS.match(inDegree) != -1)
    {
        int deg = 0, min = 0, sec = 0, result = 0;

        QRegExp deg1("");
        deg = inDegree.mid(0, deg1.match(inDegree)).toInt();
        inDegree = inDegree.mid(deg1.match(inDegree) + 1, inDegree.length());

        QRegExp deg2("'");
        min = inDegree.mid(0, deg2.match(inDegree)).toInt();
        inDegree = inDegree.mid(deg2.match(inDegree) + 1, inDegree.length());

        QRegExp deg3("\"");
        sec = inDegree.mid(0, deg3.match(inDegree)).toInt();

        result = (int)((600000.0 * deg) + (10000.0 * (min + (sec / 60.0))));

        // We add 1 to avoid rounding-errors ...
        result += 1;

        QRegExp dir("[swSW]$");
        if(dir.match(inDegree) >= 0) return -result;

        return result;
    }
    else if( degreeDMM.match(inDegree) != -1)
    {
        int deg = 0, result = 0;
        double min = 0;

        QRegExp deg1("");
        deg = inDegree.mid(0, deg1.match(inDegree)).toInt();
        inDegree = inDegree.mid(deg1.match(inDegree) + 1, inDegree.length());

        QRegExp deg2("'");
        min = inDegree.mid(0, deg2.match(inDegree)).toDouble();
        inDegree = inDegree.mid(deg2.match(inDegree) + 1, inDegree.length());

        result = (int)((600000.0 * deg) + (10000.0 * (min)));

        // We add 1 to avoid rounding-errors ...
        // result += 1;

        QRegExp dir("[swSW]$");
        if(dir.match(inDegree) >= 0) return -result;
        return result;
    }

    // @AP: inform the user that something has going wrong
    qWarning("%s(%d) MapContents::degreeToNum(): Wrong input format %s",
             __FILE__, __LINE__, inDegree.latin1() );
    return 0;
}

#ifndef t_cumulus
void MapContents::closeFlight()
{
    /*
    * close current flight
    */
    BaseFlightElement *f = flightList.current();
    if(f != 0)
    {
        for (FlightGroup *fg = flightList.first(); fg; fg = flightList.next())
        {
            if(fg->getTypeID() == BaseMapElement::FlightGroup)
                fg->removeFlight(f);
        }

        flightList.remove(f);
        if (flightList.current() == 0) flightList.last();

        emit currentFlightChanged();
    }
}
#endif

#ifdef t_kflog //only on KFLog desktop. On the PDA we don't support old formats

bool MapContents::__readAsciiFile(const QString& fileName)
{
    extern const MapMatrix _globalMapMatrix;

    QFile file(fileName);
    QTextStream stream(&file);
    QString line;

    unsigned int uLimit = 0, lLimit = 0, uLimitType = 0, lLimitType = 0;

    if(!file.open(IO_ReadOnly))
    {
        // Fehler-Handling bislang nicht ausreichend ...
        qWarning("KFLog: No Mapfile found: %s", fileName.latin1());
        return false;
    }

    qWarning("KFLog: parsing mapfile %s", fileName.latin1());

    QProgressDialog importProgress(0,0,true);

    importProgress.setCaption(tr("Import mapfile ..."));
    importProgress.setLabelText(
        (tr("Please wait while loading file")+"<BR><B>%1</B>").arg(fileName));
    importProgress.setMinimumWidth(importProgress.sizeHint().width() + 45);
    importProgress.show();
    importProgress.setMinimumDuration(0);

    importProgress.setProgress(0);

    QFileInfo fileInfo(fileName);
    unsigned int fileLength = fileInfo.size();
    unsigned int filePos = 0;

    bool isObject = false;
    bool isValley = false;
    int objectCount = 0;
    int l = 0;
    int* runwayDir;
    int* runwayLength;
    int* runwayType;
    int lat_temp = 0, lon_temp = 0;
    int* latitude;
    int* longitude;
    unsigned int posLength = 0;
    unsigned int runLength = 0;

    QString alias = 0;
    QString abbr = 0;
    unsigned int elev = 0;
    QString frequency = 0;
    QString name;
    unsigned int type = 0;
    bool vdf = false, winch = false, isWayP = false;
    int sortID = 0;

    QPointArray tA;
    QPoint position;
    WGSPoint wgsPos;

    while (!stream.eof())
    {
        if(importProgress.wasCancelled()) break;

        line = stream.readLine();
        filePos += line.length();
        importProgress.setProgress(( filePos * 100 ) / fileLength);
        line = line.simplifyWhiteSpace();
        // if it is a comment, ignore it!
        if(line.mid(0,1) == "#") continue;
        if(line.mid(0,5) == "[NEW]")
        {
            // a new object starts here
            isObject = true;
            objectCount++;

            l = 0;
            name = "";
            type = 0;
            uLimit = 0;
            vdf = false;
            isValley = false;
            frequency = "";
            alias = "";
            elev = 0;
            winch = false;
            lat_temp = 0;
            lon_temp = 0;
            posLength = 0;
            runLength = 0;
            sortID = 0;
            latitude = new int[1];
            longitude = new int[1];
            runwayDir = new int[1];
            runwayLength = new int[1];
            runwayType = new int[1];
            isWayP = false;

            tA.resize(0);
        }
        else if(isObject)
        {
            if(line.mid(0,4) == "TYPE")
                type = line.mid(5,2).toUInt();
            else if(line.mid(0,5) == "ALIAS")
                alias = line.mid(6,100);
            else if(line.mid(0,6) == "ABBREV")
                abbr = line.mid(7,6);
            else if(line.mid(0,2) == "AT")
            {
                unsigned int loop;
                for(loop = 3; loop < strlen(line); loop++)
                    if(line.mid(loop, 1) == " ") break;

                wgsPos.setPos(degreeToNum( line.mid( 3, ( loop - 3 ) ) ),
                              degreeToNum( line.mid( ( loop + 1 ), 100 ) ) );

                position = _globalMapMatrix.wgsToMap(wgsPos);
            }
            else if(line.mid(0,5) == "LTYPE")
                lLimitType = line.mid(6,1).toUInt();
            else if(line.mid(0,5) == "UTYPE")
                uLimitType = line.mid(6,1).toUInt();
            else if(line.mid(0,5) == "LOWER")
                lLimit = line.mid(6,100).toUInt();
            else if(line.mid(0,5) == "UPPER")
                uLimit = line.mid(6,100).toUInt();
            else if(line.mid(0,9) == "ELEVATION")
                elev = line.mid(10,100).toUInt();
            else if(line.mid(0,4) == "ELEV")
                elev = line.mid(5,100).toUInt();
            else if(line.mid(0,9) == "FREQUENCY")
                frequency = line.mid(10,100);
            else if(line.mid(0,8) == "MOUNTAIN")
            {
                if(line.mid(9,10).toInt() == 1)
                    isValley = true;
            }
            else if(line.mid(0,4) == "NAME")
                name = line.mid(5,1000);
            else if(line.mid(0,4) == "SORT")
                sortID = line.mid(5,10).toInt();
            else if(line.mid(0,6) == "RUNWAY")
            {
                runLength++;
                runwayDir = (int*)realloc(runwayDir,
                                          (runLength * sizeof(int)));
                runwayLength = (int*)realloc(runwayLength,
                                             (runLength * sizeof(int)));
                runwayType = (int*)realloc(runwayType,
                                           (runLength * sizeof(int)));

                unsigned int loop;
                unsigned int loop2;
                for(loop = 0; loop < strlen(line); loop++)
                    if(line.mid(loop, 1) == " ") break;

                runwayDir[runLength - 1] = line.mid(7,(loop - 7)).toInt();
                loop2 = ++loop;
                for(loop = loop; loop < strlen(line); loop++)
                    if(line.mid(loop, 1) == " ") break;

                runwayLength[runLength - 1] = line.mid(loop2,
                                                       (loop - loop2)).toInt();
                runwayType[runLength - 1] = line.mid(++loop,1).toInt();
            }
            else if((line.mid(0,1) >= "0") && (line.mid(0,1) <= "9"))
            {
                posLength++;
                tA.resize(posLength);

                unsigned int loop;
                for(loop = 0; loop < strlen(line); loop++)
                    if(line.mid(loop, 1) == " ") break;

                lat_temp = degreeToNum(line.left(loop));
                lon_temp = degreeToNum(line.mid((loop + 1), line.length() - loop));
                tA.setPoint(posLength - 1,
                            _globalMapMatrix.wgsToMap(lat_temp, lon_temp));
            }
            else if(line.mid(0,3) == "VDF")
                vdf = line.mid(4,1).toUInt();
            else if(line.mid(0,5) == "WINCH")
                winch = line.mid(6,1).toUInt();
            else if(line.mid(0,8) == "WAYPOINT")
                isWayP = line.mid(9,1).toUInt();
            else if(line.mid(0,5) == "[END]")
            {
                switch (type)
                {
                case BaseMapElement::IntAirport:
                case BaseMapElement::Airport:
                case BaseMapElement::MilAirport:
                case BaseMapElement::CivMilAirport:
                case BaseMapElement::Airfield:
                    airportList.append(new Airport(name, alias, abbr, type,
                                                   wgsPos, position, elev, frequency, vdf));
                    break;
                case BaseMapElement::ClosedAirfield:
                    airportList.append(new Airport(name, 0, abbr, type,
                                                   wgsPos, position, 0, 0, 0));
                    break;
                case BaseMapElement::CivHeliport:
                case BaseMapElement::MilHeliport:
                case BaseMapElement::AmbHeliport:
                    airportList.append(new Airport(name, alias, abbr, type,
                                                   wgsPos, position, elev, frequency, 0));
                    break;
                case BaseMapElement::Glidersite:
                    // Wieso knnen hier keine Startbahn-Daten angegeben werden?
                    gliderList.append(new GliderSite(name, alias, abbr,
                                                     wgsPos, position, elev, frequency, winch));
                    break;
                case BaseMapElement::UltraLight:
                case BaseMapElement::HangGlider:
                case BaseMapElement::Parachute:
                case BaseMapElement::Ballon:
                    break;
                case BaseMapElement::Outlanding:
                    break;
                case BaseMapElement::Vor:
                case BaseMapElement::VorDme:
                case BaseMapElement::VorTac:
                case BaseMapElement::Ndb:
                    navList.append(new RadioPoint(name, alias, abbr, type,
                                                  wgsPos, position, frequency));
                    break;
                case BaseMapElement::AirA:
                case BaseMapElement::AirB:
                case BaseMapElement::AirC:
                case BaseMapElement::AirD:
                case BaseMapElement::ControlD:
                case BaseMapElement::AirElow:
                case BaseMapElement::AirEhigh:
                case BaseMapElement::AirF:
                case BaseMapElement::Restricted:
                case BaseMapElement::Danger:
                case BaseMapElement::LowFlight:
                case BaseMapElement::Tmz:
                    airspaceList.append(new Airspace(name, type, tA,
                                                     uLimit, uLimitType, lLimit, lLimitType));
                    break;
                case BaseMapElement::Obstacle:
                case BaseMapElement::LightObstacle:
                case BaseMapElement::ObstacleGroup:
                case BaseMapElement::LightObstacleGroup:
                    obstacleList.append(new SinglePoint(0, 0, type, wgsPos,
                                                        position, elev));
                    break;
                case BaseMapElement::CompPoint:
                    //                    reportList.append(
                    //                        new SinglePoint(name, abbr, type, position, isWayP));
                    break;
                case BaseMapElement::City:
                    cityList.append(new LineElement(name, type, tA, sortID));
                    break;
                case BaseMapElement::Landmark:
                    break;
                case BaseMapElement::Highway:
                    highwayList.append(new LineElement(name, type, tA));
                    break;
                case BaseMapElement::Road:
                case BaseMapElement::Trail:
                    roadList.append(new LineElement(name, type, tA));
                    break;
                case BaseMapElement::Railway:
                case BaseMapElement::Railway_D:
                    railList.append(new LineElement(name, type, tA));
                    break;
                case BaseMapElement::AerialRailway:
                    railList.append(new LineElement(name, type, tA));
                    break;
                case BaseMapElement::Lake:
                case BaseMapElement::Lake_T:
                    lakeList.append(new LineElement(name, type, tA, sortID));
                    break;
                case BaseMapElement::River:
                case BaseMapElement::River_T:
                    hydroList.append(new LineElement(name, type, tA, sortID));
                    break;
                case BaseMapElement::Spot:
                    obstacleList.append(new SinglePoint(0, 0, type, wgsPos,
                                                        position, elev));
                    break;
                case BaseMapElement::Glacier:
                case BaseMapElement::PackIce:
                    topoList.append(new LineElement(name, type, tA));
                    break;
                case BaseMapElement::Isohypse:
                    break;
                default:
                    break;
                }
                isObject = false;
            }
            else
            {
                qWarning("KFLog: Unknown field detected: \"%s\"",
                        (const char*)line);
            }
        }
    }

    file.close();

    return true;
}

#endif //end: don't support old format import

/** JD: Here is the new code for managing plain and precomputed map files */

bool MapContents::__readTerrainFile(const int fileSecID,
                                    const int fileTypeID)
{
    extern const MapMatrix * _globalMapMatrix;
    extern MapConfig * _globalMapConfig;
    bool kflExists, kfcExists;
    bool compiling = false;

    if( fileTypeID != FILE_TYPE_TERRAIN && fileTypeID != FILE_TYPE_GROUND )
    {
        qWarning( "Cumulus: __readTerrainFile do not support requested file type %d!",
                  fileTypeID );
        return false;
    }

    // first, check if we need to load this file at all...
    if ((fileTypeID==FILE_TYPE_TERRAIN) && (!_globalMapConfig->getLoadIsolines()))
    {
        return true;
    }

    if (memoryFull) {   //if we allready know the memory if full and can't be emptied at this point, just return.
      Global::statusMessage(tr("Out of memory! Map not loaded."));
      return false;
    }

    //check free memory
    int memFree = HwInfo::instance()->getFreeMemory();

    if( memFree < MINIMUM_FREE_MEMORY ) {
      if ( !unloadDone) {
        unloadMaps();  //try freeing some memory
        memFree = HwInfo::instance()->getFreeMemory();  //re-asses free memory
        if( memFree < MINIMUM_FREE_MEMORY ) {
          memoryFull=true; //set flag to indicate that we need not try loading any more mapfiles now.
          qWarning("Cumulus couldn't load file, low on memory! Memory needed: %d kB, free: %d kB", MINIMUM_FREE_MEMORY, memFree );
          Global::statusMessage(tr("Out of memory! Map not loaded."));
          return false;
        }
      } else {
        memoryFull=true; //set flag to indicate that we need not try loading any more mapfiles now.   
        qWarning("Cumulus couldn't load file, low on memory! Memory needed: %d kB, free: %d kB", MINIMUM_FREE_MEMORY, memFree );
        Global::statusMessage(tr("Out of memory! Map not loaded."));
        return false;
      }
    }

    QString kflPathName, kfcPathName, pathName;
    QString kflName, kfcName;

    kflName.sprintf("%c_%.5d.kfl", fileTypeID, fileSecID);
    kflExists = locateFile(kflName, kflPathName);

    kfcName.sprintf("%c_%.5d.kfc", fileTypeID, fileSecID);
    kfcExists = locateFile(kfcName, kfcPathName);

    if ( ! (kflExists || kfcExists) )
    {
        qWarning( "Cumulus: no map file (%s/%s) found! Please install %s file", kflName.latin1(), kfcName.latin1(), kflName.latin1() );
        return false; // file could not be located in any of the possible map directories.
    }

    if( kflExists )
    {
      if( kfcExists )
        {
	  // kfl file newer than kfc ? Then compile it
	  if (getDateFromMapFile( kflPathName ) > getDateFromMapFile( kfcPathName ))
            {
	      compiling = true;
	      qDebug("Map file %s has a newer date! Recompiling it from source.",
		     kflPathName.latin1() );
            }
        }
      else
        {
	  // no kfc file, we compile anyway
	  compiling = true;
        }
    }

    // what file do we read after all ?
    if ( compiling )
      {
	pathName = kflPathName;
	kfcPathName = kflPathName;
	kfcPathName.replace( kfcPathName.length()-1, 1, QString("c") );
      }
    else
      {
        pathName = kfcPathName;
        kflPathName = kfcPathName;
        kflPathName.replace( kflPathName.length()-1, 1, QString("l") );
      }

    QFile eingabe(pathName);

    if( !eingabe.open(IO_ReadOnly) )
    {
      qWarning("Cumulus: Can't open map file %s for reading", pathName.latin1() );

      if( ! compiling && kflExists )
	{
	  qDebug("Try to use file %s", kflPathName.latin1());
	  // try to remove unopenable file, not sure if this works.
	  unlink( pathName.latin1() );
	  return __readTerrainFile( fileSecID, fileTypeID );
	}
      
      return false;
    }

    emit loadingFile(pathName);

    QDataStream in(&eingabe);
    in.setVersion(2);

    qDebug("reading file %s", pathName.latin1());

    Q_INT8 loadTypeID;
    Q_UINT16 loadSecID, formatID;
    Q_UINT32 magic;
    QDateTime createDateTime;
    ProjectionBase *projectionFromFile = 0;

    in >> magic;

    if( magic != KFLOG_FILE_MAGIC )
    {
      if( ! compiling && kflExists )
	{
	  qWarning("Cumulus: wrong magic key %x read!\n Retry to compile %s.",
		   magic, kflPathName.latin1());
	  eingabe.close();
	  unlink( pathName.latin1() );
	  return __readTerrainFile( fileSecID, fileTypeID );
	}

      qWarning("Cumulus: wrong magic key %x read! Aborting ...", magic);
      return false;
    }

    in >> loadTypeID;

    if(loadTypeID != fileTypeID) // wrong type
    {
      eingabe.close();

     if( ! compiling && kflExists )
	{
	  qWarning("Cumulus: wrong load type identifier %x read! "
		   "Retry to compile %s",
		   loadTypeID, kflPathName.latin1() );
	  unlink( pathName.latin1() );
	  return __readTerrainFile( fileSecID, fileTypeID );
	}

     qWarning("Cumulus: wrong load type identifier %x read! Aborting ...",
	      loadTypeID );
      return false;
    }

    in >> formatID;

    /*qDebug ("File=%s, magic=%x, fileTypeID=%x, loadTypeID=%x, formatID=%d",
     pathName.latin1(), magic, fileTypeID, loadTypeID, formatID );*/


    // Determine, which file format id is expected
    int expFormatID, expComFormatID;

    if( fileTypeID == FILE_TYPE_TERRAIN )
    {
        expFormatID = FILE_VERSION_TERRAIN;
        expComFormatID = FILE_VERSION_TERRAIN_C;
    }
    else
    {
        expFormatID = FILE_VERSION_GROUND;
        expComFormatID = FILE_VERSION_GROUND_C;
    }

    if( compiling )
      {
	// Check map file
	if( formatID < expFormatID )
	  {
	    // too old ...
	    qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		     "Aborting ...", formatID, expFormatID );
	    return false;
	  }
	else if(formatID > expFormatID )
	  {
	    // too new ...
	    qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
		   "Aborting ...", formatID,expFormatID );
	    return false;
	  }
      }
    else
      {
        // Check compiled file
        if( formatID < expComFormatID )
	  {
            // too old ...
	    if( kflExists )
	      {
		qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
			 "Retry to compile %s",
			 formatID, expComFormatID, kflPathName.latin1() );
		eingabe.close();
		unlink( pathName.latin1() );
		return __readTerrainFile( fileSecID, fileTypeID );
	      }

	    qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		     "Aborting ...", formatID, expFormatID );
            return false;
	  }
        else if(formatID > expComFormatID )
	  {
            // too new ...
	  if( kflExists )
	    {
	      qWarning( "Cumulus: File format too new! (version %d, expecting: %d) "
			"Retry to compile %s",
			formatID, expComFormatID, kflPathName.latin1() );
	      eingabe.close();
	      unlink( pathName.latin1() );
	      return __readTerrainFile( fileSecID, fileTypeID );
	    }

	    qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
		   "Aborting ...", formatID, expFormatID );
            return false;
	  }
      }
    
    in >> loadSecID;

    if ( loadSecID != fileSecID )
    {
      if( ! compiling && kflExists )
	{
	  qWarning( "Cumulus: %s: wrong section, bogus file name!"
		    "\n Retry to compile %s",
		    pathName.latin1(), kflPathName.latin1() );
	  eingabe.close();
	  unlink( pathName.latin1() );
	  return __readTerrainFile( fileSecID, fileTypeID );
	}

      qWarning("Cumulus: %s: wrong section, bogus file name! Arborting ...",
	       pathName.latin1() );
      return false;
    }

    in >> createDateTime;

    if( ! compiling ) {
        // check projection parameters from file against current used values
        projectionFromFile = LoadProjection(in);
        ProjectionBase *currentProjection = _globalMapMatrix->getProjection();

        if( ! compareProjections( projectionFromFile, currentProjection ) )
	  {
            delete projectionFromFile;
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning( "Cumulus: %s, can't use file, compiled for another projection!"
			  "\n Retry to compile %s",
			  pathName.latin1(), kflPathName.latin1() );
		
		unlink( pathName.latin1() );
		return __readTerrainFile( fileSecID, fileTypeID );
	      }

            qWarning( "Cumulus: %s, can't use file, compiled for another projection!"
		      " Please install %s file and restart.",
		      pathName.latin1(), kflPathName.latin1() );
            return false;
	  }
	else
	  {
            // Must be deleted after use to avoid memory leak
            delete projectionFromFile;
	  }
    }

    // Got to initialize "out" stream properly, even if write file is not needed
    QFile ausgabe(kfcPathName);
    QDataStream out(&ausgabe);

    if( compiling )
    {
        out.setDevice(&ausgabe);
        out.setVersion(2);

        if(!ausgabe.open(IO_WriteOnly))
        {
	  eingabe.close();
	  qWarning("Cumulus: Can't open compiled map file %s for writing!"
		   " Arborting...",
		   kfcPathName.latin1() );
	  return false;
        }

        qDebug("writing file %s", kfcPathName.latin1());

        out << magic;
        out << loadTypeID;
        out << Q_UINT16(expComFormatID);
        out << loadSecID;

        //set time one second later than the time of the original file;
        out << createDateTime.addSecs(1);

        // save current projection data
        SaveProjection(out, _globalMapMatrix->getProjection() );
    }

    uint loop = 0;

    while( !in.atEnd() )
    {
        int sort_temp;

        Q_UINT8 type;
        Q_INT16 elevation;
        Q_INT8 valley, sort;
        Q_INT32 locLength, latList_temp, lonList_temp, lastLat, lastLon;
        QPointArray tA;

        //we can safely reset lastLat and lastLon to 0, since that is a spot in the Atlantic ocean.
        lastLat=0;
        lastLon=0;

        in >> type;
        in >> elevation;
        in >> valley;
        in >> sort;

        if ( compiling )
        {
            out << type;
            out << elevation;
            out << valley;
            out << sort;
            in >> locLength;

            tA.resize( locLength );

            for(int i = 0; i < locLength; i++)
            {
                in >> latList_temp;
                in >> lonList_temp;
                // Check for double points
                if (latList_temp == lastLat && lonList_temp == lastLon) {
                  locLength--;
                  tA.resize( locLength );
                  i--;
                  qDebug("  Skipping double entry");
                } else { 
                  // This is what causes the long delays !!! Lots of floating point calcs
                  tA.setPoint(i, _globalMapMatrix->wgsToMap(latList_temp, lonList_temp));
                  lastLat=latList_temp; lastLon=lonList_temp;
                }
            }

            // And that is the whole trick: saving the computed result. Takes the same space!
            ShortSave( out, tA );
        }
        else
        {
            // Reading the computed result from kfc file
            ShortLoad( in, tA );
        }

        sort_temp = -1;

        // We must ignore it, when sort is more than 3 or less than 0!
        if(sort >= 0 && sort <= 3)
        {
            for(unsigned int pos = 0; pos < ISO_LINE_NUM; pos++)
                if(isoLines[pos] == elevation)
                    sort_temp = ISO_LINE_NUM * (int)sort + pos + 0;

            // If sort_temp is -1 here, we have an unused elevation and
            // must ignore it!
            if(sort_temp != -1)
            {
                Isohypse* newItem = new Isohypse(tA, elevation, valley, fileSecID);
                isoList.at(sort_temp)->append(newItem);
            }
        }

	// AP: Performance brake! emit progress calls waitscreen and
	// this steps into main loop
        if ( compiling && (++loop % 100) == 0 )
	  {
            emit progress(2);
	  }
    }

    // qDebug("loop=%d", loop);
    eingabe.close();

    if( compiling )
      {
	ausgabe.close();
	
	// kfl file is deleted after 'compilation' to save space, if is enabled
	// in configuration menu
	if ( _globalMapConfig->getDeleteMapfileAfterCompile() )
	  {
	    eingabe.remove();
	  }
      }

    ws->slot_SetText2(tr("Loading map ready"));
    return true;
}

bool MapContents::__readAirfieldFile(const QString& pathName)
{
    extern const MapMatrix * _globalMapMatrix;
    extern const MapConfig * _globalMapConfig;
    bool compiling = false;
    bool ok=false;
    bool ok2=false;
    QRect boundingBox;

    QString kfcPathName;
    QString kflPathName;
    bool kflExists = false;

    qDebug ("reading file %s", pathName.latin1() );

    if(pathName.isEmpty())
        // File does not exist
        return false;

    if ( pathName.contains( QString(".kfl") ) )
      {
	// File must be compiled
	compiling = true;
	kflPathName = pathName;
	kfcPathName = pathName;
	kfcPathName.replace( kfcPathName.length()-1, 1, QString("c") );
      }
    else
      {
	// compiled file is read
	kfcPathName = pathName;
	kflPathName = pathName;
	kflPathName.replace( kflPathName.length()-1, 1, QString("l") );
      }

    if( access( kflPathName.latin1(), R_OK ) == 0 )
      {
	kflExists = true;
      }

    QFile eingabe(pathName);

    if(!eingabe.open(IO_ReadOnly))
    {
      // Can't read input data:
      // We need to output a warning...
      if( ! compiling && kflExists )
	{
	  qDebug("Cumulus: Can't open map file %s for reading!"
		 " Try to use file %s.",
		 pathName.latin1(),kflPathName.latin1());
	  
	  // @AP: make a second try with the source file. Can fail, if
	  // source was removed but we are optimists
	  return __readAirfieldFile(kflPathName);
	}
      
      qWarning("Cumulus: Can't open map file %s for reading!"
	       "Arborting ...",
	       pathName.latin1() );
      
      return false;
    }

    //qDebug ("open airfield file: %s", pathName.latin1());

    emit loadingFile(pathName);

    QDataStream in(&eingabe);
    in.setVersion(2);

    // Got to initialize "out" stream properly, even if write file is not needed

    Q_INT8 loadTypeID;
    Q_UINT16 formatID;
    Q_INT32 lat_temp, lon_temp;
    Q_UINT32 magic;
    QDateTime createDateTime;

    QString name;
    QCString utf8_temp;
    QString idString, icaoName, gpsName;
    Q_INT16 elevation;
    Q_INT8 isWinch, vdf;
    vdf=0;

    QString frequency;
    Q_UINT16 iFreq;
    Q_INT8 contactType;
    QString callSign;
    Q_UINT8 contactCount;

    Q_UINT8 rwCount;
    Q_UINT16 rwDirection; // 0 -> 360
    Q_UINT16 rwLength;
    Q_UINT8 rwMaterial;
    Q_INT8 rwOpen;
    QPoint position;
    WGSPoint wgsPos;
    runway *rw;
    ProjectionBase * projectionFromFile;

    in >> magic;

    if( magic != KFLOG_FILE_MAGIC ) // wrong source file
      {
	eingabe.close();

	if( ! compiling && kflExists )
	  {
	    qWarning("Cumulus: wrong magic key %x read!\n Retry to compile %s.",
		     magic, kflPathName.latin1() );
	    return __readAirfieldFile(kflPathName);
	  }
	else
	  {
	    qWarning("Cumulus: wrong magic key %x read! Arborting ...", magic);
	    return false;
	  }
      }

    in >> loadTypeID;

    if( ! compiling )
      {
	if( loadTypeID != FILE_TYPE_AIRFIELD_C)
	  {
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning("Cumulus: wrong load type identifier %x read! "
			 "Retry to compile %s.",
			 loadTypeID, kflPathName.latin1() );

		return __readAirfieldFile(kflPathName);
	      }

	    qWarning("Cumulus: wrong load type identifier %x read! "
		     "Aborting ...",
		     loadTypeID );

	    return false;
	  }
      }
    else
      {
	// uncompiled maps have a different format identifier than compiled
	// maps
        if(loadTypeID != FILE_TYPE_AERO)
	  {
	    qWarning("Cumulus: wrong load type identifier %x read! Arborting ...",
		     loadTypeID );
	    eingabe.close();
	    return false;
	  }
      }

    in >> formatID;

    if( ! compiling )
      {
        if( formatID < FILE_VERSION_AIRFIELD_C-1)
	  {
            // to old ...
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
			 "Retry to compile %s.",
			 formatID, FILE_VERSION_AIRFIELD_C-1, kflPathName.latin1() );
		return __readAirfieldFile(kflPathName);
	      }

	    qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		     "Aborting ...",
		     formatID, FILE_VERSION_AIRFIELD_C-1 );
	    return false;
	  }
        else if(formatID > FILE_VERSION_AIRFIELD_C)
	  {
            // to new ...
	    eingabe.close();
	    
 	    if( kflExists )
	      {
		qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
			 "Retry to compile %s.",
			 formatID, FILE_VERSION_AIRFIELD_C, kflPathName.latin1() );
		return __readAirfieldFile(kflPathName);
	      }

	    qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
		     "Aborting ...",
		     formatID, FILE_VERSION_AIRFIELD_C );
	    return false;
	  }
      }
    else
      {
        if( formatID < FILE_VERSION_AIRFIELD-1)
	  {
            // to old ...
            qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		    "Aborting ...", formatID, FILE_VERSION_AIRFIELD-1 );
	    eingabe.close();
            return false;
	  }
        else if(formatID > FILE_VERSION_AIRFIELD)
	  {
            // to new ...
            qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
		    "Aborting ...", formatID, FILE_VERSION_AIRFIELD );
	    eingabe.close();
            return false;
	  }
      } 

    in >> createDateTime;

    if( ! compiling )
      {
        in >> boundingBox;

        // check projection parameters from file against current used values
        projectionFromFile = LoadProjection(in);
        ProjectionBase *currentProjection = _globalMapMatrix->getProjection();

        if( ! compareProjections( projectionFromFile, currentProjection ) )
	  {
            delete projectionFromFile;
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning( "Cumulus: can't use file %s, compiled for another projection!"
			  "\n Retry to compile %s.",
			  pathName.latin1(), kflPathName.latin1() );
		return __readAirfieldFile(kflPathName);
	      }

	    qWarning( "Cumulus: can't use file %s, compiled for another projection!"
		      " Please install %s",
		      pathName.latin1(), kflPathName.latin1() );
	    return false;
	  }
	else
	  {
            // Must be deleted after use to avoid memory leak
            delete projectionFromFile;
	  }

	// because we're using a buffer, the length will be written too. This
	// has the same type as lat_temp, and we don't need it.
	in >> lat_temp;
      }

    // @AP: opening of output file after all input checks have run to
    // avoid a zero size file.

    QFile ausgabe(kfcPathName);
    QDataStream out(&ausgabe);

    QCString bufdata;
    QBuffer buffer(bufdata);
    QDataStream outbuf(&buffer);

    if ( compiling )
    {
        out.setDevice(&ausgabe);
        out.setVersion(2);

        if(!ausgabe.open(IO_WriteOnly))
        {
            // Can't write data:
            // We need to output a warning...
            qWarning("Cumulus: Can't open compiled map file %s for writing!"
		     "Aborting ...",
		    kfcPathName.latin1() );
	    eingabe.close();
            return false;
        }

        qDebug("writing file %s", kfcPathName.latin1());

        out << magic;
        out << Q_INT8(FILE_TYPE_AIRFIELD_C);  //write back as a compiled mapfile
        out << UINT16(FILE_VERSION_AIRFIELD_C);
        out << createDateTime.addSecs(1);   //set time one second later than the time of the original file

        //create and prepare our buffer and the stream to it...
        buffer.open(IO_ReadWrite);
        outbuf.setDevice(&buffer);
        outbuf.setVersion(2);
    }

    /* Now, we need to insert additional data on the projection used and the bounding box of the data.
    Because this data is not complete before the whole file is read, we set up a buffer to output
    the data itself in compiled format. Afterwards, we write the bounding box and the projection
    data to the file, and then write the buffer to the file. */

    uint count = 0;
    uint loop = 0;

    while( ! in.atEnd() )
    {
        BaseMapElement::objectType typeIn = BaseMapElement::NotSelected;
        in >> (Q_UINT8&)typeIn;
        //check if we support this type.  In theory, we could skip this for compiled files, as we allready are sure of their contents.
        bool supported;
        switch (typeIn)
        {
        case BaseMapElement::IntAirport:
        case BaseMapElement::Airport:
        case BaseMapElement::MilAirport:
        case BaseMapElement::CivMilAirport:
        case BaseMapElement::Airfield:
        case BaseMapElement::ClosedAirfield:
        case BaseMapElement::CivHeliport:
        case BaseMapElement::MilHeliport:
        case BaseMapElement::AmbHeliport:
        case BaseMapElement::Glidersite:
            supported=true;
            break;
        default:
            supported=false;
        }

        //ok, the rest we only need and should do if this type is supported.
        if (supported) {
            count++;

            if ( compiling )  {
                outbuf << (Q_UINT8&)typeIn;
                in >> name;
                in >> idString;
                in >> icaoName;
                in >> gpsName;
                ShortSave(outbuf, name.utf8());
                ShortSave(outbuf, idString.utf8());
                ShortSave(outbuf, icaoName.utf8());
                ShortSave(outbuf, gpsName.utf8());
            } else {
                //in >> utf8_temp;
                ShortLoad(in, utf8_temp);
                name=QString::fromUtf8(utf8_temp);
                //in >> utf8_temp;
                ShortLoad(in, utf8_temp);
                idString=QString::fromUtf8(utf8_temp);
                //in >> utf8_temp;
                ShortLoad(in, utf8_temp);
                icaoName=QString::fromUtf8(utf8_temp);
                //in >> utf8_temp;
                ShortLoad(in, utf8_temp);
                gpsName=QString::fromUtf8(utf8_temp);
            }

            /*
            * The values must be reset!
            *
            * Currently only the last read frequency is passed on to the
            * Mapelement, because the elements can't handle more than one
            * frequency yet.
            *
            * Runway data is read, but not processed.
            */

            if ( compiling )
            {
                in >> lat_temp;
                in >> lon_temp;
                AddPointToRect(boundingBox, QPoint(lat_temp, lon_temp)); //update the bounding box

                wgsPos.setPos(lat_temp, lon_temp);
                position = _globalMapMatrix->wgsToMap(wgsPos);

                outbuf << wgsPos;
                outbuf << position;
            }
            else
            {
                in >> wgsPos;
                in >> position;
            }
            in >> elevation;
            if ( compiling )
                outbuf << elevation;

            switch (typeIn)
            {
            case BaseMapElement::IntAirport:
            case BaseMapElement::Airport:
            case BaseMapElement::MilAirport:
            case BaseMapElement::CivMilAirport:
            case BaseMapElement::Airfield:
                READ_CONTACT_DATA

                READ_RUNWAY_DATA
		  // cout << "name: " << name << "  RWc: " << (int)rwCount << " rwLength: "<< (int)rwLength << " rwD: " << (int)rwDirection << " rwMaterial: " << (int)rwMaterial << " rwOpen: " << (int)rwOpen << endl;

                rw = new runway(rwLength,rwDirection,rwMaterial,rwOpen);
                airportList.append(new Airport(name, icaoName, gpsName, typeIn,
                                               wgsPos, position, elevation, frequency, (bool)vdf, rw ));

                break;
            case BaseMapElement::ClosedAirfield:
                rw = new runway(0,0,0,0);
                airportList.append(new Airport(name, icaoName, gpsName, typeIn,
                                               wgsPos, position, 0, 0, 0, rw));

                break;
            case BaseMapElement::CivHeliport:
            case BaseMapElement::MilHeliport:
            case BaseMapElement::AmbHeliport:

                READ_CONTACT_DATA
                rw = new runway(0,0,0,0);
                airportList.append(new Airport(name, icaoName, gpsName, typeIn,
                                               wgsPos, position, elevation, frequency, 0,rw));

                break;
            case BaseMapElement::Glidersite:

                in >> isWinch;
                if ( compiling )
                    outbuf << isWinch;

                READ_CONTACT_DATA

                READ_RUNWAY_DATA
		  // cout << "name: " << name << "  RWc: " << (int)rwCount << " rwLength: "<< (int)rwLength << " rwD: " << (int)rwDirection << " rwMaterial: " << (int)rwMaterial << " rwOpen: " << (int)rwOpen << endl;

                rw = new runway(rwLength,rwDirection,rwMaterial,rwOpen);
                gliderList.append(new GliderSite(name, icaoName, gpsName,
                                                 wgsPos, position, elevation, frequency, isWinch, rw));

            default:
                //nothing happens
                break;
            }
        }  else {  // supported types
            qWarning ("MapContents::__readAirfieldFile; type not handled in switch: %d", typeIn);
        }
        
	// @AP: Performance brake! emit progress calls waitscreen and this
	// steps into main loop
	if((++loop % 100) == 0 )
	  {
	    emit progress(2);
	  }
    }

    // qDebug("loop=%d", loop);
    eingabe.close();

    /* So, all data is read, and if we were compiling, the buffer is filled
       with the output data.
    */

    if ( compiling )
    {
        buffer.close(); //close our output buffer
        //write metadata
        //qDebug("Bounding box is: (%d, %d),(%d, %d)",boundingBox.left(), boundingBox.top(), boundingBox.right(), boundingBox.bottom());
        out << boundingBox;

        SaveProjection(out, _globalMapMatrix->getProjection());
        //write data on airfields from bufferdata
        //out << "foo";
        out << bufdata;

        ausgabe.close();
        // kfl file is optionally deleted after 'compilation' to save space.
        if ( _globalMapConfig->getDeleteMapfileAfterCompile() )
            eingabe.remove();
    }

    ws->slot_SetText2(tr("Loading map ready"));
    return true;
}

bool MapContents::__readAirspaceFile(const QString& pathName)
{
    qDebug ("reading file %s", pathName.latin1() );

    extern const MapMatrix * _globalMapMatrix;
    extern const MapConfig * _globalMapConfig;
    bool compiling = false;
    QString kfcPathName;
    QString kflPathName;
    bool kflExists = false;
    QRect boundingBox;

    if(pathName.isEmpty())
        // Data does not exist ...
        return false;

    if( pathName.contains( QString(".kfl") ) )
      {
	// File must be compiled
	compiling = true;
	kflPathName = pathName;
	kfcPathName = pathName;
	kfcPathName.replace( kfcPathName.length()-1, 1, QString("c") );
      }
    else
      {
	// compiled file is read
	kfcPathName = pathName;
	kflPathName = pathName;
	kflPathName.replace( kflPathName.length()-1, 1, QString("l") );
      }

    if( access( kflPathName.latin1(), R_OK ) == 0 )
      {
	kflExists = true;
      }

    // Got to initialize "out" stream properly, even if write file is not
    // needed

    Q_INT8 loadTypeID;
    Q_UINT16 formatID;
    Q_UINT32 magic;
    QDateTime createDateTime;

    Q_INT32 lat_temp, lon_temp;

    QString name,idString, icaoName;
    Q_INT16 uLimit, lLimit;
    BaseMapElement::elevationType uLimitType, lLimitType;

    QString contactFrequency;
    Q_INT8 contactType;
    QString contactCallSign;
    Q_UINT8 contactCount;

    Q_INT8 exception = 0;
    QString exceptionText;

    ProjectionBase * projectionFromFile;

    //  QList<radioContact> contactList;

    Q_UINT32 locLength;

    QFile eingabe(pathName);

    if(!eingabe.open(IO_ReadOnly))
    {
        // Can't read input data:
        // We need to output a warning...
        qWarning("Cumulus: Can not open airspace file %s!",
		 pathName.latin1());

	if( ! compiling && kflExists )
	  {
	    qDebug("Cumulus: Can't open map file %s for reading!"
		   " Try to use file %s.",
		   pathName.latin1(), kflPathName.latin1());

	    // @AP: make a second try with the source file. Can fail, if
	    // source was removed but we are optimists
	    return __readAirspaceFile(kflPathName);
	  }

        return false;
    }

    emit loadingFile(pathName);

    //qDebug ("open airspacefile: %s", pathName.latin1());

    //allocate output stream
    QDataStream in(&eingabe);
    in.setVersion(2);

    //Allocate buffer
    QCString bufdata;
    QBuffer buffer(bufdata);
    QDataStream outbuf(&buffer);

    in >> magic;

    if( magic != KFLOG_FILE_MAGIC ) // wrong source file
      {
	if( ! compiling  && kflExists)
	  {
	    qWarning("Cumulus: wrong magic key %x read!\n Retry to compile %s.",
		     magic, kflPathName.latin1());
	    eingabe.close();
	    return __readAirspaceFile(kflPathName);
	  }
	else
	  {
	    qWarning("Cumulus: wrong magic key %x read! Aborting ...", magic);
	    eingabe.close();
	    return false;
	  }
      }

    in >> loadTypeID;

    if (compiling)
      {
	// uncompiled maps have a different format identifier than compiled
	// maps
        if(loadTypeID != FILE_TYPE_AERO)
	  {
	    qWarning("Cumulus: wrong load type identifier %x read!",
		     loadTypeID );
	    eingabe.close();
	    return false;
	  }

        in >> formatID;

        if(formatID < FILE_VERSION_AIRSPACE)
	  {
            // to old ...
            qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		    "Aborting ...",
		    formatID, FILE_VERSION_AIRSPACE);
	    eingabe.close();
            return false;
	  }
        else if(formatID > FILE_VERSION_AIRSPACE)
	  {
            // to new ...
            qWarning("Cumulus: File format too new! (version %d, expecting: %d.). "
		    "Aborting ...",
		    formatID, FILE_VERSION_AIRSPACE);
	    eingabe.close();
            return false;
	  }
      }
    else  // compiled version is checked here
      {
	if(loadTypeID != FILE_TYPE_AIRSPACE_C)
	{
	  qWarning("Cumulus: wrong load type identifier %x read!", loadTypeID );
	  eingabe.close();

	  if( kflExists )
	    return __readAirspaceFile(kflPathName);

	  return false;
	}
      
      in >> formatID;

      if(formatID < FILE_VERSION_AIRSPACE_C)
        {
	  // to old ...
	  qWarning("Cumulus: File format too old! (version %d, expecting: %d.)",
		   formatID, FILE_VERSION_AIRSPACE_C);
	  eingabe.close();

	  if( kflExists )
	    return __readAirspaceFile(kflPathName);

	  return false;
	  
        }
        else if(formatID > FILE_VERSION_AIRSPACE_C)
        {
	  // to new ...
	  qWarning("Cumulus: File format too new! (version %d, expecting: %d.)"
		  "Retry to compile kfl.",
		  formatID, FILE_VERSION_AIRSPACE_C);
	  eingabe.close();

	  if( kflExists )
	    return __readAirspaceFile(kflPathName);

	  return false;
        }
    }

    in >> createDateTime;

    if( ! compiling )
      {
        in >> boundingBox;

        // check projection parameters from file against current used values
        projectionFromFile = LoadProjection(in);
        ProjectionBase *currentProjection = _globalMapMatrix->getProjection();

        if( ! compareProjections( projectionFromFile, currentProjection ) )
	  {
            delete projectionFromFile;
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning( "Cumulus: can't use file %s, compiled for another projection!"
			  "\n Retry to compile %s.",
			  pathName.latin1(), kflPathName.latin1() );
		return __readAirspaceFile(kflPathName);
	      }

	    qWarning( "Cumulus: can't use file %s, compiled for another projection!"
		      " Please install %s",
		      pathName.latin1(), kflPathName.latin1() );
	    return false;
	  }
	else
	  {
            // Must be deleted after use to avoid memory leak
            delete projectionFromFile;
	  }

	// because we're using a buffer, the length will be written too. This
	// has the same type as lat_temp, and it is not used.
	in >> lat_temp;
    }

    // @AP: opening of output file after all input checks have run to
    // avoid a zero size file.

    QFile ausgabe(kfcPathName);
    QDataStream out(&ausgabe);

    if ( compiling )
    {
        out.setDevice(&ausgabe);
        out.setVersion(2);
        if(!ausgabe.open(IO_WriteOnly))
        {
	  // Can't write out data:
	  // We need to output a warning...
	  qWarning("Cumulus: Can't open compiled map file %s for writing!"
		   " Aborting ...",
		   kfcPathName.latin1() );
	  eingabe.close();
	  return false;
        }

        qDebug("writing file %s", kfcPathName.latin1());

        out << magic;
        out << (Q_INT8)FILE_TYPE_AIRSPACE_C;
        out << (Q_UINT16)FILE_VERSION_AIRSPACE_C;
        out << createDateTime.addSecs(1);   //set time one second later than the time of the original file;

        //create and prepare our buffer and the stream to it...
        buffer.open(IO_ReadWrite);
        outbuf.setDevice(&buffer);
        outbuf.setVersion(2);
    }

    uint count = 0;
    uint loop = 0;

    while(!in.atEnd())
    {
        BaseMapElement::objectType typeIn = BaseMapElement::NotSelected;
        in >> (Q_UINT8&)typeIn;
        if ( compiling )
            outbuf << (Q_UINT8&)typeIn;

        locLength = 0;

        name = "";
        idString = "";
        icaoName = "";
        lLimitType = BaseMapElement::NotSet;
        lLimit = 0;
        uLimitType = BaseMapElement::NotSet;
        uLimit = 0;
        exception = 0;
        exceptionText = "";
        contactFrequency = "";
        contactType = 0;
        contactCallSign = "";

        QPointArray tA;

        count++;

        switch (typeIn)
        {
        case BaseMapElement::AirA:
        case BaseMapElement::AirB:
        case BaseMapElement::AirC:
        case BaseMapElement::AirD:
        case BaseMapElement::AirElow:
        case BaseMapElement::AirEhigh:
        case BaseMapElement::AirF:
        case BaseMapElement::ControlC:
        case BaseMapElement::ControlD:
        case BaseMapElement::Danger:
        case BaseMapElement::LowFlight:
        case BaseMapElement::Restricted:
        case BaseMapElement::Tmz:

            if ( compiling )
            {
                in >> name;
                in >> idString;
                in >> icaoName;
                in >> (Q_UINT8&)lLimitType;
                in >> lLimit;
                in >> (Q_UINT8&)uLimitType;
                in >> uLimit;
                in >> exception;

                ShortSave(outbuf, name);
                //ShortSave(out, idString);    -> not used now
                //ShortSave(out, icaoName); -> not used now
                outbuf << (Q_UINT8&)lLimitType;
                outbuf << lLimit;
                outbuf << (Q_UINT8&)uLimitType;
                outbuf << uLimit;
                outbuf << exception;
            }  else {
                ShortLoad(in, name);
                //ShortLoad(in, idString);  -> not used now
                //ShortLoad(in, icaoName);-> not used now
                in >> (Q_UINT8&)lLimitType;
                in >> lLimit;
                in >> (Q_UINT8&)uLimitType;
                in >> uLimit;
                in >> exception;
            }

            if (exception != 0)
            {
                if ( compiling ) {
                    in >> exceptionText;
                    ShortSave(outbuf, exceptionText);
                } else {
                    ShortLoad(in, exceptionText);
                }
            }

            in >> contactCount;
            if ( compiling ) outbuf << contactCount;
            for(unsigned int loop = 0; loop < contactCount; loop++)
            {
                if ( compiling )
                {
                    in >> contactFrequency;
                    in >> contactType;
                    in >> contactCallSign;
                    ShortSave(outbuf, contactFrequency);
                    outbuf << contactType;
                    ShortSave(outbuf, contactCallSign);
                }
                {
                    ShortLoad(in, contactFrequency);
                    in >> contactType;
                    ShortLoad(in, contactCallSign);
                }
            }

            //				READ_POINT_LIST
            //this is READ_POINT_LIST slightly modified.
            //this will go back to a macro once the other pointlists are stored in the same way.

            if (compiling) {
                in >> locLength;
                // qDebug("number of points in structure %d",locLength);
                tA.resize(locLength);
                for(unsigned int i = 0; i < locLength; i++) {
                    in >> lat_temp;          in >> lon_temp;
                    tA.setPoint(i, _globalMapMatrix->wgsToMap(lat_temp, lon_temp));
                }
                ShortSave(outbuf, tA);
            } else
                ShortLoad(in, tA);
            //END MACRO

            airspaceList.append(new Airspace(name, typeIn, tA,
                                             uLimit, uLimitType, lLimit, lLimitType));
            //set bounding rectangle to include this pointarray
            if (!tA.isEmpty())
                boundingBox |= tA.boundingRect();


            break;
        default:
            qWarning ("MapContents::__readAirspaceFile; type not handled in switch: %d", typeIn);
        }

	// @AP: Performance brake! emit progress calls waitscreen and
	// this steps into main loop
        if ((++loop % 100) == 0 )
	  {
            emit progress(2);
	  }
    }

    // qDebug("loop=%d", loop);
    eingabe.close();

    if ( compiling )
    {
        buffer.close(); //close our output buffer
        //write metadata
        //qDebug("Bounding box is: (%d, %d),(%d, %d)",boundingBox.left(), boundingBox.top(), boundingBox.right(), boundingBox.bottom());
        out << boundingBox;

        SaveProjection(out, _globalMapMatrix->getProjection());

        //write data on airspaces from bufferdata
        out << bufdata;

        ausgabe.close();
        // kfl file is deleted after 'compilation' to save space. Please handle this "al gusto" ...
        if ( _globalMapConfig->getDeleteMapfileAfterCompile() )
            eingabe.remove();
    }

    ws->slot_SetText2(tr("Loading map ready"));
    return true;
}

bool MapContents::__readBinaryFile(const int fileSecID,
                                   const char fileTypeID)
{
    extern const MapMatrix * _globalMapMatrix;
    extern MapConfig * _globalMapConfig;
    bool kflExists, kfcExists;
    bool compiling = false;

    //first, check if we need to load this file at all...
    if ((fileTypeID==FILE_TYPE_TERRAIN) && (!_globalMapConfig->getLoadIsolines()))
      return true;

    if (memoryFull) { //if we allready know the memory if full and can't be emptied at this point, just return.
      Global::statusMessage(tr("Out of memory! Map not loaded."));
      return false;
    }

    //check free memory
    int memFree = HwInfo::instance()->getFreeMemory();

    if( memFree < MINIMUM_FREE_MEMORY ) {
      if ( !unloadDone) {
        unloadMaps();  //try freeing some memory
        memFree = HwInfo::instance()->getFreeMemory();  //re-asses free memory
        if( memFree < MINIMUM_FREE_MEMORY ) {
          memoryFull=true; //set flag to indicate that we need not try loading any more mapfiles now.
          qWarning("Cumulus couldn't load file, low on memory! Memory needed: %d kB, free: %d kB", MINIMUM_FREE_MEMORY, memFree );
          Global::statusMessage(tr("Out of memory! Map not loaded."));
          return false;
        }
      } else {
        memoryFull=true; //set flag to indicate that we need not try loading any more mapfiles now.
        qWarning("Cumulus couldn't load file, low on memory! Memory needed: %d kB, free: %d kB", MINIMUM_FREE_MEMORY, memFree );
        Global::statusMessage(tr("Out of memory! Map not loaded."));
        return false;
      }
    }
    
    QString kflPathName, kfcPathName, pathName;
    QString kflName, kfcName;
    
    kflName.sprintf("%c_%.5d.kfl", fileTypeID, fileSecID);
    kflExists = locateFile(kflName, kflPathName);

    kfcName.sprintf("%c_%.5d.kfc", fileTypeID, fileSecID);
    kfcExists = locateFile(kfcName, kfcPathName);

    if ( ! (kflExists || kfcExists) )
    {
      qWarning( "Cumulus: no map files (%s/%s) found! Please install %s.",
		kflName.latin1(), kfcName.latin1(), kflName.latin1() );
      return false; // file could not be located in any of the possible map directories.
    }

    if ( kflExists )
      {
        if ( kfcExists )
            // kfl file newer than kfc ? Then compile it
        {
	  if ( getDateFromMapFile( kflPathName ) > getDateFromMapFile( kfcPathName ) ) {
	    compiling = true;
	    qDebug("Map file %s has a newer date! Recompiling it from source.",
		   kflPathName.latin1() );
	  }
        }
        else
	  {
            // no kfc file, we compile anyway
            compiling = true;
	  }
      }

    // what file do we read after all ?
    if ( compiling )
      {
	pathName = kflPathName;
	kfcPathName = kflPathName;
	kfcPathName.replace( kfcPathName.length()-1, 1, QString("c") );
      }
    else
      {
        pathName = kfcPathName;
        kflPathName = kfcPathName;
        kflPathName.replace( kflPathName.length()-1, 1, QString("l") );
      }
    
    QFile eingabe(pathName);

    if(!eingabe.open(IO_ReadOnly))
    {
      if( ! compiling && kflExists )
	{
	  qDebug("Cumulus: Can't open map file %s for reading!"
		 " Try to use file %s",
		 pathName.latin1(), kflPathName.latin1());
	  // try to remove unopenable file, not sure if this works.
	  unlink( pathName.latin1() );
	  return __readBinaryFile( fileSecID, fileTypeID );
	}
      
      qWarning("Cumulus: Can't open map file %s for reading! Arborting ...",
	       pathName.latin1() );
      return false;
    }

    emit loadingFile(pathName);

    QDataStream in(&eingabe);
    in.setVersion(2);

    qDebug("reading file %s", pathName.latin1());

    Q_INT8 loadTypeID;
    Q_UINT16 loadSecID, formatID;
    Q_UINT32 magic;
    QDateTime createDateTime;
    ProjectionBase *projectionFromFile = 0;

    in >> magic;

    if( magic != KFLOG_FILE_MAGIC )
    {
      if( ! compiling && kflExists )
	{
	  qWarning("Cumulus: wrong magic key %x read!\n Retry to compile %s.",
		   magic, kflPathName.latin1());
	  eingabe.close();
	  unlink( pathName.latin1() );
	  return __readBinaryFile( fileSecID, fileTypeID );
	}

      qWarning("Cumulus: wrong magic key %x read! Aborting ...", magic);
      eingabe.close();
      return false;
    }

    in >> loadTypeID;

    /** Originally, the binairy files were mend to come in different flavours.
      * Now, they are all of type 'm'. Use that fact to do check for the
      * compiled or the uncompiled version. */

     if (compiling)
      {
	// uncompiled maps have a different format identifier than compiled
	// maps
        if(loadTypeID != FILE_TYPE_MAP)
	  {
	    qWarning("Cumulus: wrong load type identifier %x read! Aborting ...",
		     loadTypeID );
	    eingabe.close();
	    return false;
	  }
      }
    else
      {
	if( loadTypeID != FILE_TYPE_MAP_C) // wrong type
	  {
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning("Cumulus: wrong load type identifier %x read! "
			 "Retry to compile %s",
			 loadTypeID, kflPathName.latin1() );
		unlink( pathName.latin1() );
		return __readBinaryFile( fileSecID, fileTypeID );
	      }

	    qWarning("Cumulus: wrong load type identifier %x read! Aborting ...",
		     loadTypeID );
	    return false;
	  }
      }

    // check the version of the subtype. This can be different for the
    // compiled and the uncompiled version

    in >> formatID;

    if (compiling)
      {
        if( formatID < FILE_VERSION_MAP)
        {
	  // to old ...
	  qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		   "Aborting ...", formatID, FILE_VERSION_MAP );
	  eingabe.close();
	  return false;
        }
        else if(formatID > FILE_VERSION_MAP)
        {
	  // to new ...
	  qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
		   "Aborting ...", formatID, FILE_VERSION_MAP );
	  eingabe.close();
	  return false;
        }
      }
    else
      {
        if( formatID < FILE_VERSION_MAP_C)
        {
	  // to old ...
	  eingabe.close();

	  if( kflExists )
	    {
	      qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		       "Retry to compile %s",
		       formatID, FILE_VERSION_MAP_C, kflPathName.latin1() );
	      unlink( pathName.latin1() );
	      return __readBinaryFile( fileSecID, fileTypeID );
	    }

	  qWarning("Cumulus: File format too old! (version %d, expecting: %d) "
		   "Aborting ...", formatID, FILE_VERSION_MAP_C );
    	  return false;
        }
        else if(formatID > FILE_VERSION_MAP_C)
        {
	  // to new ...
	  eingabe.close();

	  if( kflExists )
	    {
	      qWarning( "Cumulus: File format too new! (version %d, expecting: %d) "
			"Retry to compile %s",
			formatID, FILE_VERSION_MAP_C, kflPathName.latin1() );
	      unlink( pathName.latin1() );
	      return __readBinaryFile( fileSecID, fileTypeID );
	    }

	  qWarning("Cumulus: File format too new! (version %d, expecting: %d) "
		   "Aborting ...", formatID, FILE_VERSION_MAP_C );

	  return false;
        }
    }

    // check if this section really covers the area we want to deal with

    in >> loadSecID;

    if ( loadSecID != fileSecID )
    {
      eingabe.close();

      if( ! compiling && kflExists )
	{
	  qWarning( "Cumulus: %s: wrong section, bogus file name!"
		    "\n Retry to compile %s",
		    pathName.latin1(), kflPathName.latin1() );
	  unlink( pathName.latin1() );
	  return __readBinaryFile( fileSecID, fileTypeID );
	}

      qWarning("Cumulus: %s: wrong section, bogus file name! Aborting ...",
	       pathName.latin1() );
      return false;
    }

    in >> createDateTime;

    if( ! compiling )
      {
        // check projection parameters from file against current used values
        projectionFromFile = LoadProjection(in);
        ProjectionBase *currentProjection = _globalMapMatrix->getProjection();

        if( ! compareProjections( projectionFromFile, currentProjection ) )
	  {
            delete projectionFromFile;
	    eingabe.close();

	    if( kflExists )
	      {
		qWarning( "Cumulus: %s, can't use file, compiled for another projection!"
			  "\n Retry to compile %s",
			  pathName.latin1(), kflPathName.latin1() );
		
		unlink( pathName.latin1() );
		return __readBinaryFile( fileSecID, fileTypeID );
	      }

            qWarning( "Cumulus: %s, can't use file, compiled for another projection!"
		      " Please install %s file and restart.",
		      pathName.latin1(), kflPathName.latin1() );
            return false;
	  }
	else
	  {
            // Must be deleted after use to avoid memory leak
            delete projectionFromFile;
	  }
      }

    QFile ausgabe(kfcPathName);
    QDataStream out(&ausgabe);

    if ( compiling )
    {
        out.setDevice(&ausgabe);
        out.setVersion(2);
        if(!ausgabe.open(IO_WriteOnly))
        {
	  qWarning("Cumulus: Can't open compiled map file %s for writing!"
		   " Arborting ...",
		   kfcPathName.latin1() );
	  eingabe.close();
	  return false;
        }

        qDebug("writing file %s", kfcPathName.latin1());

        out << magic;
        loadTypeID=FILE_TYPE_MAP_C;
        formatID=  FILE_VERSION_MAP_C;
        out << loadTypeID;
        out << formatID;
        out << loadSecID;
        out << createDateTime.addSecs(1);   //set time one second later than the time of the original file;
        SaveProjection(out, _globalMapMatrix->getProjection());
    }

    Q_UINT8 lm_typ;
    Q_INT8 sort, elev;
    Q_INT32 lat_temp, lon_temp;
    Q_UINT32 locLength = 0;
    QString name = "";


    unsigned int gesamt_elemente = 0;
    //  unsigned int river = 0;
    //  unsigned int rivert = 0;

    uint loop = 0;

    while( ! in.atEnd() )
    {
        BaseMapElement::objectType typeIn = BaseMapElement::NotSelected;
        in >> (Q_UINT8&)typeIn;
        if ( compiling )
            out << (Q_UINT8&)typeIn;

        locLength = 0;
        name = "";

        QPointArray tA;
        QPoint single;

        gesamt_elemente++;

        switch (typeIn)
        {
        case BaseMapElement::Highway:
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadHighways()) break;
            highwayList.append(new LineElement("", typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::Road:
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadRoads()) break;
            roadList.append(new LineElement("", typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::Trail:
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadRoads()) break;
            roadList.append(new LineElement("", typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::Railway:
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadRailroads()) break;
            railList.append(new LineElement("", typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::Railway_D:
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadRailroads()) break;
            railList.append(new LineElement("", typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::Aerial_Cable:
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadRailroads()) break;
            railList.append(new LineElement("", typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::Canal:
        case BaseMapElement::River:
        case BaseMapElement::River_T:
            if (typeIn==BaseMapElement::River_T)
            {
              typeIn=BaseMapElement::River;
            }
            if(formatID >= FILE_FORMAT_ID)
            {
                if ( compiling ) {
                    in >> name;
                    ShortSave(out, name);
                } else {
                    ShortLoad(in, name);
                }
            }
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadWaterways()) break;
            hydroList.append(new LineElement(name, typeIn, tA, false, fileSecID));
            break;
        case BaseMapElement::City:
            in >> sort;
            if ( compiling )
                out << sort;
            if(formatID >= FILE_FORMAT_ID)
            {
                if ( compiling ) {
                    in >> name;
                    ShortSave(out, name);
                } else {
                    ShortLoad(in, name);
                }
            }
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadCities()) break;
            cityList.append(new LineElement(name, typeIn, tA, sort, fileSecID));
            break;
        case BaseMapElement::Lake:
        case BaseMapElement::Lake_T:
            if (typeIn==BaseMapElement::Lake_T)
            {
              typeIn=BaseMapElement::Lake;
            }
            in >> sort;
            if ( compiling )
                out << sort;
            if(formatID >= FILE_FORMAT_ID)
            {
                if ( compiling ) {
                    in >> name;
                    ShortSave(out, name);
                } else {
                    ShortLoad(in, name);
                }
            }
            READ_POINT_LIST
            lakeList.append(new LineElement(name, typeIn, tA, sort, fileSecID));
            break;
        case BaseMapElement::Forest:
        case BaseMapElement::Glacier:
        case BaseMapElement::PackIce:
            in >> sort;
            if ( compiling )
                out << sort;
            if(formatID >= FILE_FORMAT_ID)
            {
                if ( compiling ) {
                    in >> name;
                    ShortSave(out, name);
                } else {
                    ShortLoad(in, name);
                }
            }
            READ_POINT_LIST
            if (!_globalMapConfig->getLoadForests()) break;
            topoList.append(new LineElement(name, typeIn, tA, sort, fileSecID));
            break;
        case BaseMapElement::Village:
            if(formatID >= FILE_FORMAT_ID)
            {
                if ( compiling ) {
                    in >> name;
                    ShortSave(out, name);
                } else {
                    ShortLoad(in, name);
                }
            }
            in >> lat_temp;
            in >> lon_temp;
            if (!_globalMapConfig->getLoadCities()) break;
            if ( compiling )
            {
                single = _globalMapMatrix->wgsToMap(lat_temp, lon_temp);
                out << single;
            }
            else
                in >> single;
            villageList.append(new SinglePoint(name, "", typeIn,
                                               WGSPoint(lat_temp, lon_temp), single, 0, fileSecID));
            break;
        case BaseMapElement::Spot:
            if(formatID >= FILE_FORMAT_ID)
            {
                in >> elev;
                if ( compiling )
                    out << elev;
            }
            in >> lat_temp;
            in >> lon_temp;
            if (!_globalMapConfig->getLoadCities()) break;
            if ( compiling )
            {
                single = _globalMapMatrix->wgsToMap(lat_temp, lon_temp);
                out << single;
            }
            else
                in >> single;
            obstacleList.append(new SinglePoint("Spot", "", typeIn,
                                                WGSPoint(lat_temp, lon_temp), single, 0, fileSecID));
            break;
        case BaseMapElement::Landmark:
            if(formatID >= FILE_FORMAT_ID)
            {
                in >> lm_typ;
                if ( compiling ) {
                    in >> name;
                    out << lm_typ;
                    ShortSave(out, name);
                } else {
                    ShortLoad(in, name);
                }
            }
            in >> lat_temp;
            in >> lon_temp;
            if (!_globalMapConfig->getLoadCities()) break;
            if ( compiling )
            {
                single = _globalMapMatrix->wgsToMap(lat_temp, lon_temp);
                out << single;
            }
            else
                in >> single;
            landmarkList.append(new SinglePoint(name, "", typeIn,
                                                WGSPoint(lat_temp, lon_temp), single, 0, fileSecID));
            break;
        default:
            qWarning ("MapContents::__readBinaryFile; type not handled in switch: %d", typeIn);
        }

	// @AP: Performance brake! emit progress calls waitscreen and
	// this steps into main loop
        if ( compiling && (++loop % 100) == 0 )
	  {
            emit progress(2);
	  }
    }

    // qDebug("loop=%d", loop);
    eingabe.close();

    if ( compiling )
    {
        ausgabe.close();
        // kfl file is deleted after 'compilation' to save space. Please handle this "al gusto" ...
        if ( _globalMapConfig->getDeleteMapfileAfterCompile() )
            eingabe.remove();
    }

    ws->slot_SetText2(tr("Loading map ready"));
    return true;
}

/*
int MapContents::searchFlightPoint(QPoint cPos, flightPoint& fP)
{
  Flight *f = (Flight *)getFlight();
  if(f && f->getTypeID() == BaseMapElement::Flight)
      return (f->searchPoint(cPos, fP));

  return -1;
}
*/
//void MapContents::optimzeFlight()
//{
//  if(flightList()->count() &&
//        flightList->current()->optimizeTask())
//    {
//      __redrawMap();
//      showFlightData(_globalMapContents.getFlight());
//    }
//}

BaseFlightElement* MapContents::getFlight()
{
    //  if(flightList.count())  return flightList.current();

    return 0;
}

/*
QList<BaseFlightElement>* MapContents::getFlightList()  {  return &flightList;  }
*/

void MapContents::proofeSection()
{
    // qDebug("MapContents::proofeSection()");

    // @AP: defined a static mutex variable, to prevent the recursive
    // calling of this method

    static bool mutex = false;

    if( mutex )
      {
	// qDebug("MapContents::proofeSection(): is recursive called, returning");
	return; // return immediately, if reentry in method is not possible
      }

    mutex = true;

    extern MapMatrix * _globalMapMatrix;
    extern MapConfig * _globalMapConfig;
    QRect mapBorder;
    // emit majorAction(tr("Loading maps..."));
    ///////////////////////////////////////////////////////////////////////////
    mapDir1 = QDir::homeDirPath()+"/Applications/cumulus/maps"; //subdir of homedir
    mapDir2 = "/mnt/cf/maps";    //compact flash card
    mapDir3 = "/mnt/card/maps";  //secure digital card
    //////////////////////////////////////////////////////////////////////////

    mapBorder = _globalMapMatrix->getViewBorder();

    int westCorner = ( ( mapBorder.left() / 600000 / 2 ) * 2 + 180 ) / 2;
    int eastCorner = ( ( mapBorder.right() / 600000 / 2 ) * 2 + 180 ) / 2;
    int northCorner = ( ( mapBorder.top() / 600000 / 2 ) * 2 - 88 ) / -2;
    int southCorner = ( ( mapBorder.bottom() / 600000 / 2 ) * 2 - 88 ) / -2;

    if(mapBorder.left() < 0)  westCorner -= 1;
    if(mapBorder.right() < 0)  eastCorner -= 1;
    if(mapBorder.top() < 0) northCorner += 1;
    if(mapBorder.bottom() < 0) southCorner += 1;  

    if(isFirst)
    {
        ws->slot_SetText1(tr("Loading maps..."));

        QStringList preselect, airspace;
        QString kflPathName, kfcPathName;

        addDir(preselect, mapDir1 + "/airspace", "*.kfc");
        addDir(preselect, mapDir1 + "/airspace", "*.kfl");
        addDir(preselect, mapDir2 + "/airspace", "*.kfc");
        addDir(preselect, mapDir2 + "/airspace", "*.kfl");
        addDir(preselect, mapDir3 + "/airspace", "*.kfc");
        addDir(preselect, mapDir3 + "/airspace", "*.kfl");

        // Now, we have to test for updated or uncompiled kfls ... Anyone can do it more elegantly?

        if(preselect.count() == 0)
        {
            // No mapfiles found
            emit errorOnMapLoading();
        }
        else
        {
            preselect.sort(); // now kfls follow kfcs in list ...
            while ( ! preselect.isEmpty() )
            {
                if ( preselect.first().contains( QString(".kfl") ) )
                {
                    // there can't be a same name kfc after this kfl
                    airspace.append( preselect.first() );
                    preselect.remove( preselect.begin() );
                }
                else
                {
                    // we have to check if there's a same name kfl after this kfc
                    kfcPathName = preselect.first();
                    preselect.remove( preselect.begin() );
                    kflPathName = kfcPathName;
                    kflPathName.replace( kflPathName.length()-1, 1, QString("l") );
                    if ( kflPathName == preselect.first() )
                    {
                        if ( getDateFromMapFile(kflPathName) > getDateFromMapFile(kfcPathName) )
                        {
                            airspace.append( kflPathName );
                            qDebug("Airspace file %s has a newer date, recompiling", kflPathName.latin1() );
                        }
                        else
                        {
                            airspace.append( kfcPathName );
                            // Here we could probably delete the old kfl to avoid confusion
                        }
                        preselect.remove( preselect.begin() );
                    }
                    else
                    {
                        airspace.append( kfcPathName );
                    }
                }
            }
            // Now only the relevant files are in the list
            for(QStringList::Iterator it = airspace.begin(); it != airspace.end(); it++)
                // we may have files from different countries, read them all
                __readAirspaceFile(*it);
        }

        QStringList airfields;

        addDir(preselect, mapDir1 + "/airfields", "*.kfc");
        addDir(preselect, mapDir1 + "/airfields", "*.kfl");
        addDir(preselect, mapDir2 + "/airfields", "*.kfc");
        addDir(preselect, mapDir2 + "/airfields", "*.kfl");
        addDir(preselect, mapDir3 + "/airfields", "*.kfc");
        addDir(preselect, mapDir3 + "/airfields", "*.kfl");

        // Now, we have to test for updated or uncompiled kfls ... Anyone can do it more elegantly?

        if(preselect.count() == 0)
        {
            // No mapfiles found
            emit errorOnMapLoading();
        }
        else
        {
            preselect.sort(); // now kfls follow kfcs in list ...
            while ( ! preselect.isEmpty() )
            {
                if ( preselect.first().contains( QString(".kfl") ) )
                {
                    // there can't be a same name kfc after this kfl
                    airfields.append( preselect.first() );
                    preselect.remove( preselect.begin() );
                }
                else
                {
                    // we have to check if there's a same name kfl after this kfc
                    kfcPathName = preselect.first();
                    preselect.remove( preselect.begin() );
                    kflPathName = kfcPathName;
                    kflPathName.replace( kflPathName.length()-1, 1, QString("l") );
                    if ( kflPathName == preselect.first() )
                    {
                        if ( getDateFromMapFile(kflPathName) > getDateFromMapFile(kfcPathName) )
                        {
                            airfields.append( kflPathName );
                            qDebug("Airfield file %s has a newer date, recompiling it.", kflPathName.latin1() );
                        }
                        else
                        {
                            airfields.append( kfcPathName );
                            // Here we could probably delete the old kfl to avoid confusion
                        }
                        preselect.remove( preselect.begin() );
                    }
                    else
                    {
                        airfields.append( kfcPathName );
                    }
                }
            }
            // Now only the relevant files are in the list
            for(QStringList::Iterator it = airfields.begin(); it != airfields.end(); it++)
                // we may have files from different countries, read them all
                __readAirfieldFile(*it);
        }
    }

    unloadDone = false;
    memoryFull = false;
    char step, hasstep; //used as small integers
    TilePartMap::Iterator it;

    for(int row = northCorner; row <= southCorner; row++)
    {
        for(int col = westCorner; col <= eastCorner; col++)
        {
            int secID=row + ( col + ( row * 179 ) );

	    if( isFirst )
	      {
		// Animate a little bit during first load. later on in flight,
		// we need the time for gps processing.
		emit progress(3);
	      }

            if(0<=secID & secID <=16200) {
                if( !sectionArray.testBit( secID ) )
                {
                    // Tile is missing
		    if( isFirst )
		      {
			ws->slot_SetText1(tr("Loading maps..."));
		      }
		    else
		      {
			Global::statusMessage("Loading maps...");
			// @AP: remove of all unused maps to get place
			// in heap. That can be disabled here because
			// the loading routines will also check the
			// available memory and call the unloadMaps()
			// method is necessary. But the disadvantage
			// is in that case that the freeing needs a
			// lot of time (several seconds).

			if (_globalMapConfig->getUnloadUnneededMap())
			  {
			    unloadMaps(0);
			    
			  }
		      }

                    qDebug("Going to load section %d", secID);
                    step=0;
                    //check to see if parts of this tile has already been loaded before
                    it=tilePartMap.find(secID);
                    if (it==tilePartMap.end()) { //not found
                        hasstep=0;
                    } else {
                        hasstep=it.data();
                    }

                    //try loading the currently unloaded files
                    if (!(hasstep & 1)) {
                        if (__readTerrainFile(secID, FILE_TYPE_GROUND))
                            step|=1;
			
                    }

                    if (!(hasstep & 2)) {
                        if (__readTerrainFile(secID, FILE_TYPE_TERRAIN))
                            step|=2;
                    }

                    if (!(hasstep & 4)) {
                        if (__readBinaryFile(secID, FILE_TYPE_MAP))
                            step|=4;
                    }

                    //set the correct flags for this maptile
                    if (step==7) {
                        sectionArray.setBit( secID, true );
                        tilePartMap.remove(secID); //make sure we don't leave
                    } else {
                        if (step > 0) {
                           tilePartMap.insert(secID, step);
                        }
                    }
                }
            }
        }
    }

    if( isFirst )
      {
	ws->slot_SetText1(tr("Loading maps done"));
      }
    else
      {
	Global::statusMessage("Loading maps done");
      }

    ws->hide();
    qApp->processEvents();
    isFirst = false;
    mutex = false; // unlock mutex
}

// Distance unit is expected as meters.

void MapContents::unloadMaps(unsigned int distance) {
  // qDebug("MapContents::unloadMaps() is called");

  if (unloadDone) return;  //we only unload mapdata once (per mapredrawing round)

  extern MapMatrix * _globalMapMatrix;
  QRect mapBorder;

  mapBorder = _globalMapMatrix->getViewBorder();

  // scale uses unit meter/pixel
  double scale = _globalMapMatrix->getScale(MapMatrix::CurrentScale);

  int width  = (int) rint(scale * distance);
  int height = width;

//   qDebug("left=%d, right=%d, top=%d, bottom=%d, wight=%d, height=%d, scale=%f",
// 	 mapBorder.left(), mapBorder.right(), mapBorder.top(), mapBorder.bottom(),
// 	 width, height, scale );

  int westCorner = ( ( ( mapBorder.left() - width ) / 600000 / 2 ) * 2 + 180 ) / 2;
  int eastCorner = ( ( ( mapBorder.right() + width ) / 600000 / 2 ) * 2 + 180 ) / 2;
  int northCorner = ( ( ( mapBorder.top() - height ) / 600000 / 2 ) * 2 - 88 ) / -2;
  int southCorner = ( ( ( mapBorder.bottom() + height ) / 600000 / 2 ) * 2 - 88 ) / -2;

  if(mapBorder.left() < 0)  westCorner -= 1;
  if(mapBorder.right() < 0)  eastCorner -= 1;
  if(mapBorder.top() < 0) northCorner += 1;
  if(mapBorder.bottom() < 0) southCorner += 1;

  QBitArray maskArray(MAX_FILE_COUNT);
  maskArray.fill(false);

  for(int row = northCorner; row <= southCorner; row++) {
    for(int col = westCorner; col <= eastCorner; col++) {
      int secID=row + ( col + ( row * 179 ) );
      if(0<=secID && secID <=MAX_FILE_COUNT) {
	// qDebug( "unloadMaps-active-secId=%d",  secID );
        maskArray.setBit( secID, true );
      }
    }
  }

  // @AP: check, if something is to free, otherwise we can return to spare
  // processing time

  bool something2free = false;

  for( uint i=0; i < maskArray.size(); i++ )
    {
      if( sectionArray.testBit(i) != maskArray.testBit(i) )
	{
	  tilePartMap.remove(i); // reset map tile parts
	  something2free = true;
	  continue;
	}
    }

  if( ! something2free )
    {
      return;
    }

  sectionArray &= maskArray;

//  addSitesList.clear();
//  airportList.clear();
//  airspaceList.clear();
//  gliderList.clear();

#define DEBUG_UNLOAD_SUM
// #define DEBUG_UNLOAD

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

  QTime t; uint sum = 0; t.start();

  unloadMapObjects(&cityList);
  sum += t.elapsed();

#ifdef DEBUG_UNLOAD
  qDebug("Unload cityList(%d), elapsed=%d", cityList.count(), t.restart());
#endif  
  unloadMapObjects(&hydroList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload hydroList(%d), elapsed=%d", hydroList.count(), t.restart());
#endif
  unloadMapObjects(&lakeList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload lakeList(%d), elapsed=%d", lakeList.count(), t.restart());
#endif
  unloadMapObjects(&isoList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload isoList(%d), elapsed=%d", isoList.count(), t.restart());
#endif
  unloadMapObjects(&landmarkList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload landmarkList(%d), elapsed=%d", landmarkList.count(), t.restart());
#endif
  unloadMapObjects(&navList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload navList(%d), elapsed=%d", navList.count(), t.restart());
#endif
  unloadMapObjects(&obstacleList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload obstacleList(%d), elapsed=%d", obstacleList.count(), t.restart());
#endif
//  unloadMapObjects(&outList);

  unloadMapObjects(&railList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload railList(%d), elapsed=%d", railList.count(), t.restart());
#endif
  unloadMapObjects(&reportList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload reportList(%d), elapsed=%d", reportList.count(), t.restart());
#endif
  unloadMapObjects(&highwayList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload highwayList(%d), elapsed=%d", highwayList.count(), t.restart());
#endif
  unloadMapObjects(&roadList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload roadList(%d), elapsed=%d", roadList.count(), t.restart());
#endif
  unloadMapObjects(&topoList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload topoList(%d), elapsed=%d", topoList.count(), t.restart());
#endif
  unloadMapObjects(&villageList);
  sum += t.elapsed();
#ifdef DEBUG_UNLOAD
  qDebug("Unload villageList(%d), elapsed=%d", villageList.count(), t.restart());
#endif
  unloadDone=true;

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

  // qDebug("Unloaded unneeded map elements. Elapsed Time=%dms, MemStart=%dKB, MemEnd=%dKB, SEDelta=%dKB", sum, memFreeBegin, memFreeEnd, memFreeBegin-memFreeEnd );
#endif

}

void MapContents::unloadMapObjects(QList<LineElement> * list) {
  for (int i=list->count()-1; i>=0;i--) {
    if (!sectionArray[list->at(i)->getMapSegment()]) {
      list->remove(i);
    }
  }
}

void MapContents::unloadMapObjects(QList<SinglePoint> * list) {
  for (int i=list->count()-1; i>=0;i--) {
    if (!sectionArray[list->at(i)->getMapSegment()]) {
      list->remove(i);
    }
  }
}

void MapContents::unloadMapObjects(QList<RadioPoint> * list) {
  for (int i=list->count()-1; i>=0;i--) {
    if (!sectionArray[list->at(i)->getMapSegment()]) {
      list->remove(i);
    }
  }
}

void MapContents::unloadMapObjects(QList< QList<Isohypse> > * list) {
  for (int i=list->count()-1; i>=0;i--) {
    for (int j=list->at(i)->count()-1; j>=0;j--) {
      if (!sectionArray[list->at(i)->at(j)->getMapSegment()]) {
        list->at(i)->remove(j);
      }
    }
//    if (list->at(i)->count()==0)
//      list->remove(i);
  }
}

unsigned int MapContents::getListLength(int listIndex) const
{
    switch(listIndex)
    {
    case AirportList:
        return airportList.count();
    case GliderList:
        return gliderList.count();
    case OutList:
        return outList.count();
    case NavList:
        return navList.count();
    case AirspaceList:
        return airspaceList.count();
    case ObstacleList:
        return obstacleList.count();
    case ReportList:
        return reportList.count();
    case CityList:
        return cityList.count();
    case VillageList:
        return villageList.count();
    case LandmarkList:
        return landmarkList.count();
    case HighwayList:
        return highwayList.count();
    case RoadList:
        return roadList.count();
    case RailList:
        return railList.count();
        //    case StationList:
        //      return stationList.count();
    case HydroList:
        return hydroList.count();
    case LakeList:
        return lakeList.count();
    case TopoList:
        return topoList.count();
        //    case IsohypseList:
        //      //warning("Anzahl der Hhenlinien: %d", isohypseList.count());
        //      return isohypseList.count();
    default:
        return 0;
    }
}

Airspace* MapContents::getAirspace(unsigned int index)
{
    return (Airspace*)airspaceList.at(index);
}

Airport* MapContents::getAirport(unsigned int index)
{
    return (Airport*)airportList.at(index);
}

GliderSite* MapContents::getGlidersite(unsigned int index)
{
    return (GliderSite*)gliderList.at(index);
}

BaseMapElement* MapContents::getElement(int listIndex, unsigned int index)
{
    switch(listIndex)
    {
    case AirportList:
        return airportList.at(index);
    case GliderList:
        return gliderList.at(index);
    case OutList:
        return outList.at(index);
    case NavList:
        return navList.at(index);
    case AirspaceList:
        return airspaceList.at(index);
    case ObstacleList:
        return obstacleList.at(index);
    case ReportList:
        return reportList.at(index);
    case CityList:
        return cityList.at(index);
    case VillageList:
        return villageList.at(index);
    case LandmarkList:
        return landmarkList.at(index);
    case HighwayList:
        return highwayList.at(index);
    case RoadList:
        return roadList.at(index);
    case RailList:
        return railList.at(index);
        //    case StationList:
        //      return stationList.at(index);
    case HydroList:
        return hydroList.at(index);
    case LakeList:
        return lakeList.at(index);
    case TopoList:
        return topoList.at(index);
    default:
        // Should never happen!
        fatal("Cumulus: trying to access unknown map element list");
        return 0;
    }
}

SinglePoint* MapContents::getSinglePoint(int listIndex, unsigned int index)
{
    switch(listIndex)
    {
    case AirportList:
        return (SinglePoint*)airportList.at(index);
    case GliderList:
        return (SinglePoint*)gliderList.at(index);
    case OutList:
        return (SinglePoint*)outList.at(index);
    case NavList:
        return (SinglePoint*)navList.at(index);
    case ObstacleList:
        return obstacleList.at(index);
    case ReportList:
        return reportList.at(index);
    case VillageList:
        return villageList.at(index);
    case LandmarkList:
        return landmarkList.at(index);
        //      case StationList:
        //        return stationList.at(index);
    default:
        return 0;
    }
}

void MapContents::slotReloadMapData()
{
  // @AP: defined a static mutex variable, to prevent the recursive
  // calling of this method

  static bool mutex = false;

  if( mutex )
    {
      // qDebug("MapContents::slotReloadMapData(): mutex is locked, returning");
      return; // return immediately, if reentry in method is not possible
    }

  mutex = true;

  // qDebug("MapContents::slotReloadMapData() is called");

  addSitesList.clear();
  airportList.clear();
  airspaceList.clear();
  cityList.clear();
  gliderList.clear();
  hydroList.clear();
  lakeList.clear();
  isoList.clear();
  landmarkList.clear();
  navList.clear();
  obstacleList.clear();
  outList.clear();
  railList.clear();
  reportList.clear();
  highwayList.clear();
  roadList.clear();
  //  stationList.clear();
  topoList.clear();
  villageList.clear();

  // Wir nehmen zunaechst 4 Schachtelungstiefen an ...
  for(unsigned int loop = 0; loop < ( ISO_LINE_NUM * 4 ); loop++)
    {
      QList<Isohypse> *list = new QList<Isohypse>;
      list->setAutoDelete(true);
      isoList.append(list);
    }

  sectionArray.fill(false);
  tilePartMap.clear();
  
  isFirst = true;
  
  // @AP: Reload all data, that must be done after a projection value change
  proofeSection();

  emit mapDataReloaded();
  
  mutex = false; // unlock mutex
}

void MapContents::printContents(QPainter* targetPainter, bool isText)
{
    proofeSection();

    for(BaseMapElement* topo = topoList.first(); topo; topo = topoList.next())
        topo->printMapElement(targetPainter, isText);

    for(BaseMapElement* hydro = hydroList.first(); hydro; hydro = hydroList.next())
        hydro->printMapElement(targetPainter, isText);

    for(BaseMapElement* lake = lakeList.first(); lake; lake = lakeList.next())
        lake->printMapElement(targetPainter, isText);

    for(BaseMapElement* rail = railList.first(); rail; rail = railList.next())
        rail->printMapElement(targetPainter, isText);

    for(BaseMapElement* highway = highwayList.first(); highway; highway = highwayList.next())
        highway->printMapElement(targetPainter, isText);

    for(BaseMapElement* road = roadList.first(); road; road = roadList.next())
        road->printMapElement(targetPainter, isText);

    for(BaseMapElement* city = cityList.first(); city; city = cityList.next())
        city->printMapElement(targetPainter, isText);

    //  for(BaseMapElement* village = villageList.first(); village; village = villageList.next())
    //      village->printMapElement(targetPainter, isText);

    for(BaseMapElement* nav = navList.first(); nav; nav = navList.next())
        nav->printMapElement(targetPainter, isText);

    for(BaseMapElement* airspace = airspaceList.first(); airspace; airspace = airspaceList.next())
        airspace->printMapElement(targetPainter, isText);

    for(BaseMapElement* obstacle = obstacleList.first(); obstacle; obstacle = obstacleList.next())
        obstacle->printMapElement(targetPainter, isText);

    for(BaseMapElement* report = reportList.first(); report; report = reportList.next())
        report->printMapElement(targetPainter, isText);

    for(BaseMapElement* landmark = landmarkList.first(); landmark; landmark = landmarkList.next())
        landmark->printMapElement(targetPainter, isText);

    for(BaseMapElement* airport = airportList.first(); airport; airport = airportList.next())
        airport->printMapElement(targetPainter, isText);

    for(BaseMapElement* glider = gliderList.first(); glider; glider = gliderList.next())
        glider->printMapElement(targetPainter, isText);

    for(BaseMapElement* out = outList.first(); out; out = outList.next())
        out->printMapElement(targetPainter, isText);

    //  for(BaseFlightElement* flight = flightList.first(); flight; flight = flightList.next())
    //      flight->printMapElement(targetPainter, isText);
}

void MapContents::drawList(QPainter* targetPainter, QPainter* maskPainter,
                           unsigned int listID)
{
  const char *list="";
  uint len = 0;
  QTime t;

  t.start();

  switch(listID)
    {
    case AirportList:
      list="AirportList";
      len=airportList.count();
        for(BaseMapElement* airport = airportList.first(); airport; airport = airportList.next())
            airport->drawMapElement(targetPainter, maskPainter);
        break;
    case GliderList:
      list="GliderList";
      len=gliderList.count();
        for(BaseMapElement* glider = gliderList.first(); glider; glider = gliderList.next())
            glider->drawMapElement(targetPainter, maskPainter);
        break;
    case OutList:
      list="OutList";
      len=outList.count();
        for(BaseMapElement* out = outList.first(); out; out = outList.next())
            out->drawMapElement(targetPainter, maskPainter);
        break;
    case NavList:
      list="NavList";
      len=navList.count();
        for(BaseMapElement* nav = navList.first(); nav; nav = navList.next())
            nav->drawMapElement(targetPainter, maskPainter);
        break;
    case AirspaceList:
      list="AirspaceList";
      len=airspaceList.count();
        for(BaseMapElement* airspace = airspaceList.first(); airspace; airspace = airspaceList.next())
            airspace->drawMapElement(targetPainter, maskPainter);
        break;
    case ObstacleList:
      list="ObstacleList";
      len=obstacleList.count();
        for(BaseMapElement* obstacle = obstacleList.first(); obstacle; obstacle = obstacleList.next())
            obstacle->drawMapElement(targetPainter, maskPainter);
        break;
    case ReportList:
      list="ReportList";
      len=reportList.count();
        for(BaseMapElement* report = reportList.first(); report; report = reportList.next())
            report->drawMapElement(targetPainter, maskPainter);
        break;
    case CityList:
      list="CityList";
      len=cityList.count();
        for(BaseMapElement* city = cityList.first(); city; city = cityList.next())
            city->drawMapElement(targetPainter, maskPainter);
        break;
    case VillageList:
      list="VillageList";
      for(BaseMapElement* village = villageList.first(); village; village = villageList.next())
          village->drawMapElement(targetPainter, maskPainter);
      break;
    case LandmarkList:
      list="LandmarkList";
      len=landmarkList.count();
        for(BaseMapElement* landmark = landmarkList.first(); landmark; landmark = landmarkList.next())
            landmark->drawMapElement(targetPainter, maskPainter);
        break;
    case HighwayList:
      list="HighwayList";
      len=highwayList.count();
        for(BaseMapElement* highway = highwayList.first(); highway; highway = highwayList.next())
            highway->drawMapElement(targetPainter, maskPainter);
        break;
    case RoadList:
      list="RoadList";
      len=roadList.count();
        for(BaseMapElement* road = roadList.first(); road; road = roadList.next())
            road->drawMapElement(targetPainter, maskPainter);
        break;
    case RailList:
      list="RailList";
      len=railList.count();
        for(BaseMapElement* rail = railList.first(); rail; rail = railList.next())
            rail->drawMapElement(targetPainter, maskPainter);
        break;
    case HydroList:
      list="HydroList";
      len=hydroList.count();
        for(BaseMapElement* hydro = hydroList.first(); hydro; hydro = hydroList.next())
            hydro->drawMapElement(targetPainter, maskPainter);
        break;
    case LakeList:
      list="LakeList";
      len=lakeList.count();
        for(BaseMapElement* lake = lakeList.first(); lake; lake = lakeList.next())
            lake->drawMapElement(targetPainter, maskPainter);
        break;
    case TopoList:
      list="TopoList";
      len=topoList.count();
        for(BaseMapElement* topo = topoList.first(); topo; topo = topoList.next())
            topo->drawMapElement(targetPainter, maskPainter);
        break;
        /*
	  case FlightList:
	  // In some cases, getFlightIndex returns a non-valid index :-(
	  if (flightList.count() > 0 && getFlightIndex() >= 0 &&
	  getFlightIndex() < (int)flightList.count())
	  flightList.at(getFlightIndex())->drawMapElement(targetPainter, maskPainter);
	  break;
        */
    default:
      qWarning("MapContents::drawList(): unknown listID %d", listID);
      return;
    }

  // qDebug( "List=%s, Length=%d, drawTime=%dms", list, len, t.elapsed() ); 
}

void MapContents::drawIsoList(QPainter* targetP, QPainter* maskP)
{
  // qDebug("MapContents::drawIsoList():");

  QTime t; t.start();

    extern MapConfig * _globalMapConfig;
    extern MapMatrix * _globalMapMatrix;
    int height = 0;
    regIsoLines.clear();
    _lastIsoEntry=0;
    _isoLevelReset=true;
    

    /* Determine how fine graded we are going to draw the isolines.
    * If the bigger the scale, the more lines we will skip   */

    int interval=1; //in principle, we draw every line
    int scale = (int)_globalMapMatrix->getScale(MapMatrix::CurrentScale);
    if (_globalMapConfig->getdrawIsoLines())
    {
        if (scale>1500) interval=15; //
        else if (scale>1000) interval=10; //
        else if (scale>725) interval=8; //
        else if (scale>600) interval=6;  //
        else if (scale>500) interval=5;  //
        else if (scale>200) interval=4;  // etc
        else if (scale>150) interval=3;  // We skip more at zoom 151 and above,
        else if (scale>100) interval=2;  // But, if the scale is > 100, we skip every other line.
        else interval=1;
    }
    else
    {
        interval=9999;              // Make sure we are only drawing _one_ level.
    }

   /* switched off for now
    //try to find the highest isoline that covers the entire map
    bool foundCovering;
    bool everFoundCovering=false;
    int listIndex=-1;
    Isohypse * lastFound;
    for(QList<Isohypse>* iso = isoList.at(2); iso; iso = isoList.next()) {
     // qDebug("checking level %d: items %d", isoList.at(), iso->count());
      if (isoList.at() > ISO_LINE_NUM) break;

      foundCovering=false;
      for(Isohypse* iso2 = iso->first(); iso2; iso2 = iso->next()) {
        if (iso2->coversMap()) {
          foundCovering=true;
          everFoundCovering=true;
          lastFound=iso2;
          listIndex=isoList.at();
//          qDebug("found covering!");
          break;
        }
      }
      //if (!foundCovering)
        //break;
    }
    if (everFoundCovering)
    	qDebug("last covering elevation level: %d (level %d)", lastFound->getElevation(), listIndex);
    else
       qDebug("No covering found!");

    //targetP->fillRect(_globalMapMatrix->getMapBorder(),QBrush(_globalMapConfig->getIsoColor(height), QBrush::SolidPattern));
    //return;
    isoListEntry* entry=new isoListEntry(new QRegion(_globalMapMatrix->getMapBorder()), lastFound->getElevation());
    regIsoLines.append(entry);
     */
    int lastHeight=9999;
    int group=9999;
    bool groupDrawn=false;

    // qDebug("group: %d, interval: %d, scale: %d, heigth: %d, count: %d",group,interval,scale,height,iso->count());
    bool isolines = false;
    targetP->setPen(QPen(Qt::black, 1, Qt::NoPen));
    if (_globalMapConfig->getShowIsolineBorders()) {
      if( scale < 160 ) { // Draw Lines at higher scales
        targetP->setPen(QPen(Qt::black, 1, Qt::DotLine));
        isolines = true;
      } 
    }

    interval=1;
    for(QList<Isohypse>* iso = isoList.first(); iso; iso = isoList.next())
//    for(QList<Isohypse>* iso = isoList.at(listIndex); iso; iso = isoList.next())
    {
     //if (isoList.at()>1) break;
        if(iso->count() == 0) continue;
        Isohypse* first = iso->getFirst();

        for(unsigned int pos = 0; pos < ISO_LINE_NUM; pos++)
        {
            if(isoLines[pos] == first->getElevation())
            {
                if(first->isValley())
                    height = pos + 1;
                else
                    height = pos + 2;

                break;
            }
        }

        groupDrawn=true;
//        if (interval==1 || height <= 1 || isoList.at()==listIndex)
        if (interval==1 || height <= 1)
        {
            groupDrawn=false; //skip the rest, result is less devisions->faster!
        }
        else
        {
            if (height<lastHeight)
            {
                lastHeight=height;
                group=height/interval;
                groupDrawn=false;
            }
            if ((height/interval)!=group)
            {
                group=height/interval;
                groupDrawn=false;
            }
        }

        targetP->setBrush(QBrush(_globalMapConfig->getIsoColor(height), QBrush::SolidPattern));

        for(Isohypse* iso2 = iso->first(); iso2; iso2 = iso->next())
        {
            QRegion * reg = iso2->drawRegion(targetP, maskP, !groupDrawn, isolines  );
//            QRegion * reg = iso2->drawRegion(targetP, maskP, true, true, 1  );
            if (reg)
            {
                isoListEntry* entry=new isoListEntry(reg, iso2->getElevation());
                //entry=new isoListEntry(reg, iso2->getElevation());
                regIsoLines.append(entry);
                //qDebug("  added Iso: %04x, %d", (int)reg, iso2->getElevation() );
            }
        }
    }
    regIsoLines.sort();
    _isoLevelReset=false;

    qDebug( "IsoListe, Length=%d, drawTime=%dms", isoList.count(), t.elapsed() ); 
}


#ifndef t_cumulus //no task loading at this point
/** read a task file and append all tasks to flight list
switch to first task in file */
bool MapContents::loadTask(QFile path)
{
    QFileInfo fInfo(path);

    extern const MapMatrix _globalMapMatrix;

    if(!fInfo.exists()) {
        KMessageBox::error(0,  tr("The selected file<BR><B>%1</B><BR>does not exist!").arg(path.name()));
        return false;
    }

    if(!fInfo.size()) {
        KMessageBox::sorry(0, tr("The selected file<BR><B>%1</B><BR>is empty!").arg(path.name()));
        return false;
    }

    if(!path.open(IO_ReadOnly)) {
        KMessageBox::error(0, tr("You don't have permission to access file<BR><B>%1</B>").arg(path.name()));
        return false;
    }

    QDomDocument doc;
    QList<wayPoint> wpList;
    FlightTask *f, *firstTask = 0;

    doc.setContent(&path);

    if (doc.doctype().name() == "KFLogTask") {
        QDomNodeList nl = doc.elementsByTagName("Task");

        for (uint i = 0; i < nl.count(); i++)
        {
            QDomNodeList childNodes = nl.item(i).childNodes();
            wpList.clear();
            for (uint childIdx = 0; childIdx < childNodes.count(); childIdx++)
            {
                QDomNamedNodeMap nm =  childNodes.item(childIdx).attributes();

                wayPoint *w = new wayPoint;

                w->name = nm.namedItem("Name").toAttr().value().left(6).upper();
                w->description = nm.namedItem("Description").toAttr().value();
                w->icao = nm.namedItem("ICAO").toAttr().value().upper();
                w->origP.setLat(nm.namedItem("Latitude").toAttr().value().toInt());
                w->origP.setLon(nm.namedItem("Longitude").toAttr().value().toInt());
                w->projP = _globalMapMatrix.wgsToMap(w->origP);
                w->elevation = nm.namedItem("Elevation").toAttr().value().toInt();
                w->frequency = nm.namedItem("Frequency").toAttr().value().toDouble();
                w->isLandable = nm.namedItem("Landable").toAttr().value().toInt();
                w->runway = nm.namedItem("Runway").toAttr().value().toInt();
                w->length = nm.namedItem("Length").toAttr().value().toInt();
                w->surface = nm.namedItem("Surface").toAttr().value().toInt();
                w->comment = nm.namedItem("Comment").toAttr().value();

                wpList.append(w);
            }
            f = new FlightTask(wpList, false, genTaskName());
            // remember first task in file
            if (firstTask == 0)
            {
                firstTask = f;
            }
            flightList.append(f);
            emit newTaskAdded(f);
        }

        if (firstTask)
        {
            slotSetFlight(firstTask);
        }
        return true;
    }
    else
    {
        KMessageBox::error(0, tr("wrong doctype ") + doc.doctype().name(), tr("Error occurred!"));
        return false;
    }
}
#endif //not t_cumulus: no tastloading


QString MapContents::genTaskName()
{
    static int tCount = 1;
    QString tmp;

    tmp.sprintf("TASK%03d", tCount++);
    return tmp;
}

/** This function checks all possible map directories for the map file. If
    found, it returns true and returns the complete path in pathName. */
bool MapContents::locateFile(const QString& fileName, QString& pathName)
{
    QFile test;
    test.setName(mapDir1 + "/" + fileName);
    if (test.exists())
    {
        pathName=test.name();
        return true;
    }

    test.setName(mapDir2 + "/" + fileName);
    if (test.exists()) {
        pathName=test.name();
        return true;
    }

    test.setName(mapDir3 + "/" + fileName);
    if (test.exists()) {
        pathName=test.name();
        return true;
    }
    
    //lower case tests
    test.setName(mapDir1 + "/" + fileName.lower());
    if (test.exists())
    {
        pathName=test.name();
        return true;
    }

    test.setName(mapDir2 + "/" + fileName.lower());
    if (test.exists()) {
        pathName=test.name();
        return true;
    }

    test.setName(mapDir3 + "/" + fileName.lower());
    if (test.exists()) {
        pathName=test.name();
        return true;
    }
    
    //so, let's try uppercase
    test.setName(mapDir1 + "/" + fileName.upper());
    if (test.exists())
    {
        pathName=test.name();
        return true;
    }

    test.setName(mapDir2 + "/" + fileName.upper());
    if (test.exists()) {
        pathName=test.name();
        return true;
    }

    test.setName(mapDir3 + "/" + fileName.upper());
    if (test.exists()) {
        pathName=test.name();
        return true;
    }

    return false;
}

void MapContents::addDir (QStringList& list, const QString& _path, const QString& filter)
{
    //  qDebug ("addDir (%s, %s)", _path.latin1(), filter.latin1());
    QDir path (_path, filter);

    //JD was a bit annoyed by many notifications about nonexisting dirs
    if ( ! path.exists() )
        return;

    QStringList entries (path.entryList());
    for (QStringList::Iterator it = entries.begin(); it != entries.end(); ++it )
    {
        bool found = false;
        // look for other entries with same filename
        for (QStringList::Iterator it2 =  list.begin(); it2 != list.end(); ++it2)
        {
            QFileInfo path2 (*it2);
            if (path2.fileName() == *it)
                found = true;
        }
        if (!found)
            list += path.absFilePath (*it);
    }
    //  qDebug ("entries: %s", list.join(";").latin1());
}

/** Read property of FlightTask * currentTask. */
const FlightTask * MapContents::getCurrentTask(){
    return currentTask;
}

/** Write property of FlightTask * currentTask. */
void MapContents::setCurrentTask( FlightTask * _newVal){
    currentTask = _newVal;
}

/** Returns true if the coordinates of the waypoint in the argument matches one of the waypoints in the list. */
bool MapContents::getIsInWaypointList(const wayPoint * wp)
{
    int i,n;
    n =  wpList.count();
    wayPoint * wpi;

    for (i=0; i < n; i++)
    {
        wpi=(wayPoint*)wpList.at(i);
        if (wp->origP==wpi->origP)
	  return true;
    }

    return false;
}

QDateTime MapContents::getDateFromMapFile( const QString& path )
{
    QDateTime createDateTime;
    QFile mapFile( path );
    if(!mapFile.open(IO_ReadOnly))
    {
        qWarning("Cumulus: can't open map file %s for reading date", path.latin1() );
        createDateTime.setDate( QDate(1900,1,1) );
        return createDateTime;
    }

    QDataStream in(&mapFile);
    in.setVersion(2);

    mapFile.at( 9 );
    in >> createDateTime;
    mapFile.close();
    //qDebug("Map file %s created %s", path.latin1(), createDateTime.toString().latin1() );
    return createDateTime;
}

/** Add a point to a rectangle, so the rectangle will be the bounding box
  * of all points added to it. If the point allready lies within the borders
  * of the QRect, the QRect is unchanged. If the point is outside the
  * defined QRect, the QRox will be modified so the point lies inside the
  * new QRect. If the QRect is empty, the QRect will be set to a rect of
  * size (1,1) at the location of the point. */
void MapContents::AddPointToRect(QRect& rect, const QPoint& point){
    if (rect.isValid()) {
        rect.setCoords(
            MIN(rect.left(), point.x()),
            MIN(rect.top(), point.y()),
            MAX(rect.right(), point.x()),
            MAX(rect.bottom(), point.y()));
    } else {
        rect.setCoords(point.x(),point.y(),point.x(),point.y());
    }
}



/** @AP: Compares two projection objects for equality.
    Returns true if equal; otherwise false */

bool MapContents::compareProjections(ProjectionBase* p1, ProjectionBase* p2)
{
    if( p1->projectionType() != p2->projectionType() )
    {
        return false;
    }

    if( p1->projectionType() == ProjectionBase::Lambert )
    {
        ProjectionLambert* l1 = (ProjectionLambert *) p1;
        ProjectionLambert* l2 = (ProjectionLambert *) p2;

        if( l1->getStandardParallel1() != l2->getStandardParallel1() ||
                l1->getStandardParallel2() != l2->getStandardParallel2() ||
                l1->getOrigin() != l2->getOrigin() )
        {
            return false;
        }

        return true;
    }

    if( p1->projectionType() == ProjectionBase::Cylindric )
    {
        ProjectionCylindric* c1 = (ProjectionCylindric*) p1;
        ProjectionCylindric* c2 = (ProjectionCylindric*) p2;

        if( c1->getStandardParallel() != c2->getStandardParallel() )
        {
            return false;
        }

        return true;
    }

    // What's that? Det kennen wir noch nicht :( Rejection!

    return false;

}

/***********************************************
 * isoListEntry
 ***********************************************/

isoListEntry::isoListEntry(QRegion* region, int height) {
    this->region=region;
    this->height=height;
}

isoListEntry::~isoListEntry() {
    if(region) delete region;
}

extern MapContents * _globalMapContents;
extern MapMatrix * _globalMapMatrix;

int MapContents::findElevation(const QPoint& coordP, Distance * errorDist){
    isoListEntry* entry;
    int height=0;
    QPoint coordP1 = _globalMapMatrix->wgsToMap(coordP.x(), coordP.y());
    QPoint coord = _globalMapMatrix->map(coordP1);
    QList<isoListEntry>* list=_globalMapContents->getIsohypseRegions();
    // qDebug("list->count() %d", list->count() );
    double error=0.0;
    //qDebug("==searching for elevation==");
    //qDebug("_lastIsoLevel %d, _nextIsoLevel %d",_lastIsoLevel, _nextIsoLevel);
    if (_isoLevelReset)
    {
      qDebug("findElevation: Bussy rebuilding the isomap. Returning last known result...");
      return _lastIsoLevel;
    }
    for(unsigned int i=0; i<list->count();i++) {
//    for(int i=list->count()-1; i>=0;i--) {
        entry=list->at(i);
        // qDebug("i: %d entry->height %d contains %d",i,entry->height, entry->region->contains(coord) );
        // qDebug("Point x:%d y:%d", coord.x(), coord.y() );
        // qDebug("boundingRect l:%d r:%d t:%d b:%d", entry->region->boundingRect().left(),
        //                                 entry->region->boundingRect().right(),
        //                                 entry->region->boundingRect().top(),
        //                                 entry->region->boundingRect().bottom() );
        if (entry->height>height && /*there is no reason to search a lower level if we allready have a hit on a higher one*/
            entry->height <= _nextIsoLevel) /* since the odds of skipping a level between two fixes are not too high, we can ignore higher levels, making searching more efficient.*/
        {
          if (entry->height==_lastIsoLevel && _lastIsoEntry!=0) 
          {
            //qDebug("Trying previous entry...");
            if (_lastIsoEntry->region->contains(coord))
            {
                height=MAX(height,entry->height);
                //qDebug("Found on height %d",entry->height);
                break;
            }
          }
          if (entry == _lastIsoEntry)
          {
            continue; //we allready tried this one, and it wasn't it.
          }
          //qDebug("Probing on height %d...", entry->height);
          if (entry->region->contains(coord))
          {
              height=MAX(height,entry->height);
              //qDebug("Found on height %d",entry->height);
              _lastIsoEntry=entry;
              break;
          }
        }
    }
    _lastIsoLevel=height;
    // The real altitude is between the current and the next
    // isolevel, therefore reduce error by taking the middle
    if( height <100 ) {
        _nextIsoLevel=height+25;
        height += 12;
        error=12.5;
    }
    else if( (height >=100) && (height < 500) ) {
        _nextIsoLevel=height+50;
        height += 25;
        error=25.0;
    }
    else if( (height >=500) && (height < 1000) ) {
        _nextIsoLevel=height+100;
        height += 50;
        error=50.0;
    }
    else { 
        _nextIsoLevel=height+250;
        height += 125;
        error=125.0;
    }

    //if errorDist is set, set the correct error margin
    if (errorDist)
      errorDist->setMeters(error);
    return height;
}


