//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2006-2009 Torsten Rahn <tackat@kde.org>
// Copyright 2007      Inge Wallin  <ingwa@kde.org>
// Copyright 2008      Carlos Licea <carlos.licea@kdemail.net>
// Copyright 2009      Jens-Michael Hoffmann <jensmh@gmx.de>
// Copyright 2010-2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
//


// Own
#include "MarbleMap.h"

// Posix
#include <cmath>

// Qt
#include <QtCore/QAbstractItemModel>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtGui/QItemSelectionModel>
#include <QtGui/QSizePolicy>
#include <QtGui/QRegion>

#ifdef MARBLE_DBUS
#include <QtDBus/QDBusConnection>
#endif

// Marble
#include "layers/AtmosphereLayer.h"
#include "layers/FogLayer.h"
#include "layers/FpsLayer.h"
#include "layers/GeometryLayer.h"
#include "layers/MarbleSplashLayer.h"
#include "layers/MeasureTool.h"
#include "layers/PlacemarkLayout.h"
#include "layers/TextureLayer.h"
#include "layers/VectorMapBaseLayer.h"
#include "layers/VectorMapLayer.h"
#include "AbstractFloatItem.h"
#include "GeoDataTreeModel.h"
#include "GeoPainter.h"
#include "GeoSceneDocument.h"
#include "GeoSceneFilter.h"
#include "GeoSceneHead.h"
#include "GeoSceneMap.h"
#include "GeoScenePalette.h"
#include "GeoSceneSettings.h"
#include "GeoSceneVector.h"
#include "GeoSceneZoom.h"
#include "LayerManager.h"
#include "MapThemeManager.h"
#include "MarbleDebug.h"
#include "MarbleDirs.h"
#include "MarbleModel.h"
#include "RenderPlugin.h"
#include "SunLocator.h"
#include "TextureColorizer.h"
#include "TileCoordsPyramid.h"
#include "TileCreator.h"
#include "TileCreatorDialog.h"
#include "TileLoader.h"
#include "VectorComposer.h"
#include "ViewParams.h"
#include "ViewportParams.h"

namespace Marble
{


class MarbleMap::CustomPaintLayer : public LayerInterface
{
 public:
    CustomPaintLayer( MarbleMap *map )
        : m_map( map )
    {
    }

    virtual QStringList renderPosition() const { return QStringList() << "USER_TOOLS"; }

    virtual bool render( GeoPainter *painter, ViewportParams *viewport,
                         const QString &renderPos, GeoSceneLayer *layer )
    {
        Q_UNUSED( viewport );
        Q_UNUSED( renderPos );
        Q_UNUSED( layer );

        m_map->customPaint( painter );

        return true;
    }

    virtual qreal zValue() const { return 1.0e6; }

 private:
    MarbleMap *const m_map;
};


class MarbleMapPrivate
{
    friend class MarbleWidget;

 public:
    explicit MarbleMapPrivate( MarbleMap *parent, MarbleModel *model );

    void updateMapTheme();

    void updateProperty( const QString &, bool );

    MarbleMap *const q;

    // The model we are showing.
    MarbleModel     *const m_model;
    bool             m_modelIsOwned;

    // Parameters for the maps appearance.
    ViewParams       m_viewParams;
    ViewportParams   m_viewport;
    bool             m_showFrameRate;

    VectorComposer   m_veccomposer;
    TextureColorizer *m_texcolorizer;

    LayerManager     m_layerManager;
    MarbleSplashLayer m_marbleSplashLayer;
    MarbleMap::CustomPaintLayer m_customPaintLayer;
    GeometryLayer            m_geometryLayer;
    AtmosphereLayer          m_atmosphereLayer;
    FogLayer                 m_fogLayer;
    VectorMapBaseLayer       m_vectorMapBaseLayer;
    VectorMapLayer   m_vectorMapLayer;
    TextureLayer     m_textureLayer;
    PlacemarkLayout  m_placemarkLayout;
    MeasureTool      m_measureTool;
};

MarbleMapPrivate::MarbleMapPrivate( MarbleMap *parent, MarbleModel *model )
        : q( parent ),
          m_model( model ),
          m_viewParams(),
          m_showFrameRate( false ),
          m_veccomposer(),
          m_texcolorizer( 0 ),
          m_layerManager( model, parent ),
          m_customPaintLayer( parent ),
          m_geometryLayer( model->treeModel() ),
          m_vectorMapBaseLayer( &m_veccomposer ),
          m_vectorMapLayer( &m_veccomposer ),
          m_textureLayer( model->downloadManager(), model->sunLocator() ),
          m_placemarkLayout( model->placemarkModel(), model->placemarkSelectionModel(), model->clock(), parent ),
          m_measureTool( model )
{
    m_layerManager.addLayer( &m_fogLayer );
    m_layerManager.addLayer( &m_measureTool );
    m_layerManager.addLayer( &m_geometryLayer );
    m_layerManager.addLayer( &m_placemarkLayout );
    m_layerManager.addLayer( &m_customPaintLayer );

    QObject::connect( m_model, SIGNAL( themeChanged( QString ) ),
                      parent, SLOT( updateMapTheme() ) );

    QObject::connect( &m_veccomposer, SIGNAL( datasetLoaded() ),
                      parent, SIGNAL( repaintNeeded() ));

    QObject::connect( model->treeModel(), SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
                      &m_placemarkLayout, SLOT( setCacheData() ) );
    QObject::connect( model->treeModel(), SIGNAL( layoutChanged() ),
                      &m_placemarkLayout, SLOT( setCacheData() ) );
    QObject::connect( model->treeModel(), SIGNAL( modelReset() ),
                      &m_placemarkLayout, SLOT( setCacheData() ) );
    QObject::connect( model->treeModel(), SIGNAL( treeChanged() ),
                      &m_placemarkLayout, SLOT( setCacheData() ) );

    QObject::connect( &m_placemarkLayout, SIGNAL( repaintNeeded()),
                      parent, SIGNAL( repaintNeeded() ));

    QObject::connect ( &m_layerManager, SIGNAL( pluginSettingsChanged() ),
                       parent,        SIGNAL( pluginSettingsChanged() ) );
    QObject::connect ( &m_layerManager, SIGNAL( repaintNeeded( QRegion ) ),
                       parent,        SIGNAL( repaintNeeded( QRegion ) ) );
    QObject::connect ( &m_layerManager, SIGNAL( renderPluginInitialized( RenderPlugin * ) ),
                       parent,        SIGNAL( renderPluginInitialized( RenderPlugin * ) ) );

    QObject::connect( model->treeModel(), SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
                      &m_geometryLayer, SLOT( invalidateScene() ) );
    QObject::connect( model->treeModel(), SIGNAL( layoutChanged() ),
                      &m_geometryLayer, SLOT( invalidateScene() ) );
    QObject::connect( model->treeModel(), SIGNAL( modelReset() ),
                      &m_geometryLayer, SLOT( invalidateScene() ) );
    QObject::connect( model->treeModel(), SIGNAL( treeChanged() ),
                      &m_geometryLayer, SLOT( invalidateScene() ) );

    QObject::connect( &m_geometryLayer, SIGNAL( repaintNeeded()),
                      parent, SIGNAL( repaintNeeded() ));

    QObject::connect( &m_textureLayer, SIGNAL( tileLevelChanged( int ) ),
                      parent, SIGNAL( tileLevelChanged( int ) ) );
    QObject::connect( &m_textureLayer, SIGNAL( repaintNeeded() ),
                      parent, SIGNAL( repaintNeeded() ) );
}

void MarbleMapPrivate::updateProperty( const QString &name, bool show )
{
    // earth
    if ( name == "places" ) {
        m_placemarkLayout.setShowPlaces( show );
    } else if ( name == "cities" ) {
        m_placemarkLayout.setShowCities( show );
    } else if ( name == "terrain" ) {
        m_placemarkLayout.setShowTerrain( show );
    } else if ( name == "otherplaces" ) {
        m_placemarkLayout.setShowOtherPlaces( show );
    }

    // other planets
    else if ( name == "landingsites" ) {
        m_placemarkLayout.setShowLandingSites( show );
    } else if ( name == "craters" ) {
        m_placemarkLayout.setShowCraters( show );
    } else if ( name == "maria" ) {
        m_placemarkLayout.setShowMaria( show );
    }

    else if ( name == "waterbodies" ) {
        m_veccomposer.setShowWaterBodies( show );
    } else if ( name == "lakes" ) {
        m_veccomposer.setShowLakes( show );
    } else if ( name == "ice" ) {
        m_veccomposer.setShowIce( show );
    } else if ( name == "coastlines" ) {
        m_veccomposer.setShowCoastLines( show );
    } else if ( name == "rivers" ) {
        m_veccomposer.setShowRivers( show );
    } else if ( name == "borders" ) {
        m_veccomposer.setShowBorders( show );
    }

    else if ( name == "relief" ) {
        if ( m_texcolorizer ) {
            m_texcolorizer->setShowRelief( show );
        }
    }
}

// ----------------------------------------------------------------


MarbleMap::MarbleMap()
    : d( new MarbleMapPrivate( this, new MarbleModel( this ) ) )
{
#ifdef MARBLE_DBUS
    QDBusConnection::sessionBus().registerObject( "/MarbleMap", this, 
                                                  QDBusConnection::ExportAllSlots
                                                  | QDBusConnection::ExportAllSignals
                                                  | QDBusConnection::ExportAllProperties );
#endif
}

MarbleMap::MarbleMap(MarbleModel *model)
    : d( new MarbleMapPrivate( this, model ) )
{
#ifdef MARBLE_DBUS
    QDBusConnection::sessionBus().registerObject( "/MarbleMap", this,
                                                  QDBusConnection::ExportAllSlots
                                                  | QDBusConnection::ExportAllSignals
                                                  | QDBusConnection::ExportAllProperties );
#endif

    d->m_modelIsOwned = false;
}

MarbleMap::~MarbleMap()
{
    MarbleModel *model = d->m_modelIsOwned ? d->m_model : 0;

    d->m_layerManager.removeLayer( &d->m_customPaintLayer );
    d->m_layerManager.removeLayer( &d->m_geometryLayer );
    d->m_layerManager.removeLayer( &d->m_measureTool );
    d->m_layerManager.removeLayer( &d->m_fogLayer );
    d->m_layerManager.removeLayer( &d->m_placemarkLayout );
    d->m_layerManager.removeLayer( &d->m_textureLayer );
    d->m_layerManager.removeLayer( &d->m_atmosphereLayer );
    d->m_layerManager.removeLayer( &d->m_vectorMapLayer );
    d->m_layerManager.removeLayer( &d->m_vectorMapBaseLayer );
    delete d;

    delete model;  // delete the model after private data
}

MarbleModel *MarbleMap::model() const
{
    return d->m_model;
}

ViewportParams *MarbleMap::viewport()
{
    return &d->m_viewport;
}

const ViewportParams *MarbleMap::viewport() const
{
    return &d->m_viewport;
}


void MarbleMap::setMapQualityForViewContext( MapQuality quality, ViewContext viewContext )
{
    d->m_viewParams.setMapQualityForViewContext( quality, viewContext );

    // Update texture map during the repaint that follows:
    d->m_textureLayer.setNeedsUpdate();
}

MapQuality MarbleMap::mapQuality( ViewContext viewContext ) const
{
    return d->m_viewParams.mapQuality( viewContext );
}

MapQuality MarbleMap::mapQuality() const
{
    return d->m_viewParams.mapQuality();
}

void MarbleMap::setViewContext( ViewContext viewContext )
{
    d->m_viewParams.setViewContext( viewContext );

    // Update texture map during the repaint that follows:
    d->m_textureLayer.setNeedsUpdate();
}

ViewContext MarbleMap::viewContext() const
{
    return d->m_viewParams.viewContext();
}


void MarbleMap::setSize( int width, int height )
{
    setSize( QSize( width, height ) );
}

void MarbleMap::setSize( const QSize& size )
{
    d->m_viewport.setSize( size );
    d->m_textureLayer.setNeedsUpdate();

    emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() );
}

QSize MarbleMap::size() const
{
    return QSize( d->m_viewport.width(), d->m_viewport.height() );
}

int  MarbleMap::width() const
{
    return d->m_viewport.width();
}

int  MarbleMap::height() const
{
    return d->m_viewport.height();
}

int MarbleMap::radius() const
{
    return d->m_viewport.radius();
}

void MarbleMap::setRadius( int radius )
{
    const int oldRadius = d->m_viewport.radius();

    d->m_viewport.setRadius( radius );

    d->m_textureLayer.setNeedsUpdate();

    if ( oldRadius != d->m_viewport.radius() ) {
        emit radiusChanged( radius );
    }
}


int MarbleMap::preferredRadiusCeil( int radius )
{
    if ( d->m_textureLayer.tileZoomLevel() < 0 )
        return radius;

    return d->m_textureLayer.preferredRadiusCeil( radius );
}


int MarbleMap::preferredRadiusFloor( int radius )
{
    if ( d->m_textureLayer.tileZoomLevel() < 0 )
        return radius;

    return d->m_textureLayer.preferredRadiusFloor( radius );
}


int MarbleMap::tileZoomLevel() const
{
    return d->m_textureLayer.tileZoomLevel();
}


qreal MarbleMap::centerLatitude() const
{
    // Calculate translation of center point
    const qreal centerLat = d->m_viewport.centerLatitude();

    return centerLat * RAD2DEG;
}

qreal MarbleMap::centerLongitude() const
{
    // Calculate translation of center point
    const qreal centerLon = d->m_viewport.centerLongitude();

    return centerLon * RAD2DEG;
}

int  MarbleMap::minimumZoom() const
{
    if ( d->m_model->mapTheme() )
        return d->m_model->mapTheme()->head()->zoom()->minimum();

    return 950;
}

int  MarbleMap::maximumZoom() const
{
    if ( d->m_model->mapTheme() )
        return d->m_model->mapTheme()->head()->zoom()->maximum();

    return 2100;
}

QVector<const GeoDataPlacemark*> MarbleMap::whichFeatureAt( const QPoint& curpos ) const
{
    return d->m_placemarkLayout.whichPlacemarkAt( curpos );
}

void MarbleMap::reload() const
{
    d->m_textureLayer.reload();
}

void MarbleMap::downloadRegion( const QString& sourceDir, QVector<TileCoordsPyramid> const & pyramid )
{
    Q_ASSERT( textureLayer() );
    Q_ASSERT( !pyramid.isEmpty() );
    QTime t;
    t.start();

    // When downloading a region (the author of these lines thinks) most users probably expect
    // the download to begin with the low resolution tiles and then procede level-wise to
    // higher resolution tiles. In order to achieve this, we start requesting downloads of
    // high resolution tiles and request the low resolution tiles at the end because
    // DownloadQueueSet (silly name) is implemented as stack.


    int const first = 0;
    int tilesCount = 0;

     for ( int level = pyramid[first].bottomLevel(); level >= pyramid[first].topLevel(); --level ) {
         QSet<TileId> tileIdSet;
          for( int i = 0; i < pyramid.size(); ++i ) {
            QRect const coords = pyramid[i].coords( level );
            mDebug() << "MarbleMap::downloadRegion level:" << level << "tile coords:" << coords;
            int x1, y1, x2, y2;
            coords.getCoords( &x1, &y1, &x2, &y2 );
            for ( int x = x1; x <= x2; ++x ) {
                for ( int y = y1; y <= y2; ++y ) {
                    TileId const tileId( sourceDir, level, x, y );
                    tileIdSet.insert( tileId );
                    // FIXME: use lazy evaluation to not generate up to 100k tiles in one go
                    // this can take considerable time even on very fast systems
                    // in contrast generating the TileIds on the fly when they are needed
                    // does not seem to affect download speed.
                }
            }
         }
         QSetIterator<TileId> i( tileIdSet );
         while( i.hasNext() ) {
              textureLayer()->downloadTile( i.next() );
         }
         tilesCount += tileIdSet.count();
     }
    // Needed for downloading unique tiles only. Much faster than if tiles for each level is downloaded separately

    int const elapsedMs = t.elapsed();
    mDebug() << "MarbleMap::downloadRegion:" << tilesCount << "tiles, " << elapsedMs << "ms";
}

bool MarbleMap::propertyValue( const QString& name ) const
{
    bool value;
    if ( d->m_model->mapTheme() ) {
        d->m_model->mapTheme()->settings()->propertyValue( name, value );
    }
    else {
        value = false;
        mDebug() << "WARNING: Failed to access a map theme! Property: " << name;
    }
    return value;
}

bool MarbleMap::showOverviewMap() const
{
    return propertyValue( "overviewmap" );
}

bool MarbleMap::showScaleBar() const
{
    return propertyValue( "scalebar" );
}

bool MarbleMap::showCompass() const
{
    return propertyValue( "compass" );
}

bool MarbleMap::showGrid() const
{
    return propertyValue( "coordinate-grid" );
}

bool MarbleMap::showClouds() const
{
    return d->m_viewParams.showClouds();
}

bool MarbleMap::showSunShading() const
{
    return d->m_textureLayer.showSunShading();
}

bool MarbleMap::showCityLights() const
{
    return d->m_textureLayer.showCityLights();
}

bool MarbleMap::showSunInZenith() const
{
    bool visible = false;

    QList<RenderPlugin *> pluginList = renderPlugins();
    QList<RenderPlugin *>::const_iterator i = pluginList.constBegin();
    QList<RenderPlugin *>::const_iterator const end = pluginList.constEnd();
    for (; i != end; ++i ) {
        if ( (*i)->nameId() == "sun" ) {
            visible = (*i)->visible();
        }
    }

    return visible;
}

bool MarbleMap::showAtmosphere() const
{
    return d->m_viewParams.showAtmosphere();
}

bool MarbleMap::showCrosshairs() const
{
    bool visible = false;

    QList<RenderPlugin *> pluginList = renderPlugins();
    QList<RenderPlugin *>::const_iterator i = pluginList.constBegin();
    QList<RenderPlugin *>::const_iterator const end = pluginList.constEnd();
    for (; i != end; ++i ) {
        if ( (*i)->nameId() == "crosshairs" ) {
            visible = (*i)->visible();
        }
    }

    return visible;
}

bool MarbleMap::showPlaces() const
{
    return propertyValue( "places" );
}

bool MarbleMap::showCities() const
{
    return propertyValue( "cities" );
}

bool MarbleMap::showTerrain() const
{
    return propertyValue( "terrain" );
}

bool MarbleMap::showOtherPlaces() const
{
    return propertyValue( "otherplaces" );
}

bool MarbleMap::showRelief() const
{
    return propertyValue( "relief" );
}

bool MarbleMap::showIceLayer() const
{
    return propertyValue( "ice" );
}

bool MarbleMap::showBorders() const
{
    return propertyValue( "borders" );
}

bool MarbleMap::showRivers() const
{
    return propertyValue( "rivers" );
}

bool MarbleMap::showLakes() const
{
    return propertyValue( "lakes" );
}

bool MarbleMap::showFrameRate() const
{
    return d->m_showFrameRate;
}

bool MarbleMap::showBackground() const
{
    return d->m_layerManager.showBackground();
}

quint64 MarbleMap::volatileTileCacheLimit() const
{
    return d->m_textureLayer.volatileCacheLimit();
}


void MarbleMap::rotateBy( const qreal& deltaLon, const qreal& deltaLat )
{
    centerOn( d->m_viewport.centerLongitude() * RAD2DEG + deltaLon,
              d->m_viewport.centerLatitude()  * RAD2DEG + deltaLat );
}


void MarbleMap::centerOn( const qreal lon, const qreal lat )
{
    d->m_viewport.centerOn( lon * DEG2RAD, lat * DEG2RAD );
    d->m_textureLayer.setNeedsUpdate();

    emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() );
}

void MarbleMap::setCenterLatitude( qreal lat )
{
    centerOn( centerLongitude(), lat );
}

void MarbleMap::setCenterLongitude( qreal lon )
{
    centerOn( lon, centerLatitude() );
}

Projection MarbleMap::projection() const
{
    return d->m_viewport.projection();
}

void MarbleMap::setProjection( Projection projection )
{
    emit projectionChanged( projection );

    d->m_viewport.setProjection( projection );

    d->m_textureLayer.setupTextureMapper( projection );

    emit visibleLatLonAltBoxChanged( d->m_viewport.viewLatLonAltBox() );
}


bool MarbleMap::screenCoordinates( qreal lon, qreal lat,
                                   qreal& x, qreal& y ) const
{
    return d->m_viewport.screenCoordinates( lon * DEG2RAD, lat * DEG2RAD, x, y );
}

bool MarbleMap::geoCoordinates( int x, int y,
                                qreal& lon, qreal& lat,
                                GeoDataCoordinates::Unit unit ) const
{
    return d->m_viewport.geoCoordinates( x, y, lon, lat, unit );
}

// Used to be paintEvent()
void MarbleMap::paint( GeoPainter &painter, const QRect &dirtyRect )
{
    Q_UNUSED( dirtyRect );

    if ( !d->m_model->mapTheme() ) {
        mDebug() << "No theme yet!";
        d->m_marbleSplashLayer.render( &painter, &d->m_viewport );
        return;
    }

    QTime t;
    t.start();

    d->m_layerManager.renderLayers( &painter, &d->m_viewport );

    if ( d->m_showFrameRate ) {
        FpsLayer fpsLayer( &t );
        fpsLayer.render( &painter, &d->m_viewport );
    }

    const qreal fps = 1000.0 / (qreal)( t.elapsed() );
    emit framesPerSecond( fps );
}

void MarbleMap::customPaint( GeoPainter *painter )
{
    Q_UNUSED( painter );
}

QString MarbleMap::mapThemeId() const
{
    return d->m_model->mapThemeId();
}

void MarbleMap::setMapThemeId( const QString& mapThemeId )
{
    d->m_model->setMapThemeId( mapThemeId );
}

void MarbleMapPrivate::updateMapTheme()
{
    m_layerManager.removeLayer( &m_textureLayer );
    m_layerManager.removeLayer( &m_vectorMapLayer );
    m_layerManager.removeLayer( &m_vectorMapBaseLayer );

    m_textureLayer.setTextureColorizer( 0 );
    delete m_texcolorizer;
    m_texcolorizer = 0;

    QObject::connect( m_model->mapTheme()->settings(), SIGNAL( valueChanged( const QString &, bool ) ),
                      q, SLOT( updateProperty( const QString &, bool ) ) );

    q->setPropertyValue( "clouds_data", m_viewParams.showClouds() );

    // NOTE due to frequent regressions: 
    // Do NOT take it for granted that there is any TEXTURE or VECTOR data AVAILABLE
    // at this point. Some themes do NOT have either vector or texture data!
    
    // Check whether there is a vector layer available:
    if ( m_model->mapTheme()->map()->hasVectorLayers() ) {
        m_veccomposer.setShowWaterBodies( q->propertyValue( "waterbodies" ) );
        m_veccomposer.setShowLakes( q->propertyValue( "lakes" ) );
        m_veccomposer.setShowIce( q->propertyValue( "ice" ) );
        m_veccomposer.setShowCoastLines( q->propertyValue( "coastlines" ) );
        m_veccomposer.setShowRivers( q->propertyValue( "rivers" ) );
        m_veccomposer.setShowBorders( q->propertyValue( "borders" ) );

	// Set all the colors for the vector layers
        m_veccomposer.setOceanColor( m_model->mapTheme()->map()->backgroundColor() );

        // Just as with textures, this is a workaround for DGML2 to
        // emulate the old behaviour.

        GeoSceneLayer *layer = m_model->mapTheme()->map()->layer( "mwdbii" );
        if ( layer ) {
            GeoSceneVector *vector = 0;

            vector = static_cast<GeoSceneVector*>( layer->dataset("pdiffborder") );
            if ( vector )
                m_veccomposer.setCountryBorderColor( vector->pen().color() );

            vector = static_cast<GeoSceneVector*>( layer->dataset("rivers") );
            if ( vector )
                m_veccomposer.setRiverColor( vector->pen().color() );

            vector = static_cast<GeoSceneVector*>( layer->dataset("pusa48") );
            if ( vector )
                m_veccomposer.setStateBorderColor( vector->pen().color() );

            vector = static_cast<GeoSceneVector*>( layer->dataset("plake") );
            if ( vector )
                m_veccomposer.setLakeColor( vector->pen().color() );

            vector = static_cast<GeoSceneVector*>( layer->dataset("pcoast") );
            if ( vector )
            {
                m_veccomposer.setLandColor( vector->brush().color() );
                m_veccomposer.setCoastColor( vector->pen().color() );
            }
        }
    }

    if ( m_model->mapTheme()->map()->hasVectorLayers() ) {
        if ( !m_model->mapTheme()->map()->hasTextureLayers() ) {
            m_layerManager.addLayer( &m_vectorMapBaseLayer );
        }

        m_layerManager.addLayer( &m_vectorMapLayer );
    }

    // NOTE due to frequent regressions: 
    // Do NOT take it for granted that there is any TEXTURE or VECTOR data AVAILABLE
    // at this point.

    // Check whether there is a texture layer available:
    if ( m_model->mapTheme()->map()->hasTextureLayers() ) {
        GeoSceneSettings *const settings = m_model->mapTheme()->settings();
        GeoSceneGroup *const textureLayerSettings = settings ? settings->group( "Texture Layers" ) : 0;

        const GeoSceneHead *const head = m_model->mapTheme()->head();
        const GeoSceneMap *const map = m_model->mapTheme()->map();
        const GeoSceneLayer *const sceneLayer = ( head && map ) ? map->layer( head->theme() ) : 0;

        QVector<const GeoSceneTexture *> textures;
        if ( sceneLayer ) {
            foreach ( const GeoSceneAbstractDataset *pos, sceneLayer->datasets() ) {
                const GeoSceneTexture *const texture = dynamic_cast<GeoSceneTexture const *>( pos );
                if ( !texture )
                    continue;

                const QString sourceDir = texture->sourceDir();
                const QString installMap = texture->installMap();
                const QString role = sceneLayer->role();

                // If the tiles aren't already there, put up a progress dialog
                // while creating them.
                if ( !TileLoader::baseTilesAvailable( *texture )
                    && !installMap.isEmpty() )
                {
                    mDebug() << "Base tiles not available. Creating Tiles ... \n"
                             << "SourceDir: " << sourceDir << "InstallMap:" << installMap;

                    TileCreator *tileCreator = new TileCreator(
                                             sourceDir,
                                             installMap,
                                             (role == "dem") ? "true" : "false" );

                    QPointer<TileCreatorDialog> tileCreatorDlg = new TileCreatorDialog( tileCreator, 0 );
                    tileCreatorDlg->setSummary( m_model->mapTheme()->head()->name(),
                                                m_model->mapTheme()->head()->description() );
                    tileCreatorDlg->exec();
                    if ( TileLoader::baseTilesAvailable( *texture ) ) {
                        qDebug() << "Base tiles for" << sourceDir << "successfully created.";
                    } else {
                        qDebug() << "Some or all base tiles for" << sourceDir << "could not be created.";
                    }

                    delete tileCreatorDlg;
                }

                if ( TileLoader::baseTilesAvailable( *texture ) ) {
                    textures.append( texture );
                } else {
                    qWarning() << "Base tiles for" << sourceDir << "not available. Skipping.";
                }
            }
        }

        m_textureLayer.setMapTheme( textures, textureLayerSettings );

        m_textureLayer.setupTextureMapper( m_viewport.projection() );

        if( !m_model->mapTheme()->map()->filters().isEmpty() ) {
            GeoSceneFilter *filter= m_model->mapTheme()->map()->filters().first();

            if( filter->type() == "colorize" ) {
                 //no need to look up with MarbleDirs twice so they are left null for now
                QString seafile, landfile;
                QList<GeoScenePalette*> palette = filter->palette();
                foreach ( GeoScenePalette *curPalette, palette ) {
                    if( curPalette->type() == "sea" ) {
                        seafile = MarbleDirs::path( curPalette->file() );
                    } else if( curPalette->type() == "land" ) {
                        landfile = MarbleDirs::path( curPalette->file() );
                    }
                }
                //look up locations if they are empty
                if( seafile.isEmpty() )
                    seafile = MarbleDirs::path( "seacolors.leg" );
                if( landfile.isEmpty() )
                    landfile = MarbleDirs::path( "landcolors.leg" );

                m_texcolorizer = new TextureColorizer( seafile, landfile, &m_veccomposer, q );
                m_texcolorizer->setShowRelief( q->showRelief() );

                m_textureLayer.setTextureColorizer( m_texcolorizer );
            }
        }

        m_layerManager.addLayer( &m_textureLayer );
    }

    // NOTE due to frequent regressions: 
    // Do NOT take it for granted that there is any TEXTURE or VECTOR data AVAILABLE
    // at this point!

    // earth
    bool value;
    if ( m_model->mapTheme()->settings()->propertyValue( "places", value ) ) {
        m_placemarkLayout.setShowPlaces( value );
    }

    m_placemarkLayout.setShowCities( q->showCities() );
    m_placemarkLayout.setShowTerrain( q->showTerrain() );
    m_placemarkLayout.setShowOtherPlaces( q->showOtherPlaces() );
    m_placemarkLayout.setShowLandingSites( q->propertyValue("landingsites") );
    m_placemarkLayout.setShowCraters( q->propertyValue("craters") );
    m_placemarkLayout.setShowMaria( q->propertyValue("maria") );

    m_placemarkLayout.setDefaultLabelColor( m_model->mapTheme()->map()->labelColor() );
    m_placemarkLayout.requestStyleReset();

    m_layerManager.syncViewParamsAndPlugins( m_model->mapTheme() );

    emit q->themeChanged( m_model->mapTheme()->head()->mapThemeId() );
}

void MarbleMap::setPropertyValue( const QString& name, bool value )
{
    mDebug() << "In MarbleMap the property " << name << "was set to " << value;
    if ( d->m_model->mapTheme() ) {
        d->m_model->mapTheme()->settings()->setPropertyValue( name, value );
    }
    else {
        mDebug() << "WARNING: Failed to access a map theme! Property: " << name;
    }
    d->m_textureLayer.setNeedsUpdate();
}

void MarbleMap::setShowOverviewMap( bool visible )
{
    setPropertyValue( "overviewmap", visible );
}

void MarbleMap::setShowScaleBar( bool visible )
{
    setPropertyValue( "scalebar", visible );
}

void MarbleMap::setShowCompass( bool visible )
{
    setPropertyValue( "compass", visible );
}

void MarbleMap::setShowAtmosphere( bool visible )
{
    d->m_layerManager.removeLayer( &d->m_atmosphereLayer );
    if ( visible ) {
        d->m_layerManager.addLayer( &d->m_atmosphereLayer );
    }

    d->m_viewParams.setShowAtmosphere( visible );
}

void MarbleMap::setShowCrosshairs( bool visible )
{
    QList<RenderPlugin *> pluginList = renderPlugins();
    QList<RenderPlugin *>::const_iterator i = pluginList.constBegin();
    QList<RenderPlugin *>::const_iterator const end = pluginList.constEnd();
    for (; i != end; ++i ) {
        if ( (*i)->nameId() == "crosshairs" ) {
            (*i)->setVisible( visible );
        }
    }
}

void MarbleMap::setShowClouds( bool visible )
{
    d->m_viewParams.setShowClouds( visible );
    d->m_textureLayer.setNeedsUpdate();

    setPropertyValue( "clouds_data", visible );
}

void MarbleMap::setShowSunShading( bool visible )
{
    d->m_textureLayer.setShowSunShading( visible );
}

void MarbleMap::setShowCityLights( bool visible )
{
    d->m_textureLayer.setShowCityLights( visible );
    setPropertyValue( "citylights", visible );
}

void MarbleMap::setShowSunInZenith( bool visible )
{
    disconnect( d->m_model->sunLocator(), SIGNAL( positionChanged( qreal, qreal ) ),
                this,                     SLOT( centerOn( qreal, qreal ) ) );

    QList<RenderPlugin *> pluginList = renderPlugins();
    QList<RenderPlugin *>::const_iterator i = pluginList.constBegin();
    QList<RenderPlugin *>::const_iterator const end = pluginList.constEnd();
    for (; i != end; ++i ) {
        if ( (*i)->nameId() == "sun" ) {
            (*i)->setVisible( visible );
        }
    }

    if ( showSunInZenith() ) {
        connect( d->m_model->sunLocator(), SIGNAL( positionChanged( qreal, qreal ) ),
                 this,                     SLOT( centerOn( qreal, qreal ) ) );

        centerOn( d->m_model->sunLocator()->getLon(), d->m_model->sunLocator()->getLat() );
    } else if ( visible ) {
        mDebug() << "Ignoring centering on sun, since the sun plugin is not loaded.";
    }
}

void MarbleMap::setShowTileId( bool visible )
{
    d->m_textureLayer.setShowTileId( visible );
}

void MarbleMap::setShowGrid( bool visible )
{
    setPropertyValue( "coordinate-grid", visible );
}

void MarbleMap::setShowPlaces( bool visible )
{
    setPropertyValue( "places", visible );
}

void MarbleMap::setShowCities( bool visible )
{
    setPropertyValue( "cities", visible );
}

void MarbleMap::setShowTerrain( bool visible )
{
    setPropertyValue( "terrain", visible );
}

void MarbleMap::setShowOtherPlaces( bool visible )
{
    setPropertyValue( "otherplaces", visible );
}

void MarbleMap::setShowRelief( bool visible )
{
    setPropertyValue( "relief", visible );
    // Update texture map during the repaint that follows:
    d->m_textureLayer.setNeedsUpdate();
}

void MarbleMap::setShowIceLayer( bool visible )
{
    setPropertyValue( "ice", visible );
    // Update texture map during the repaint that follows:
    d->m_textureLayer.setNeedsUpdate();
}

void MarbleMap::setShowBorders( bool visible )
{
    setPropertyValue( "borders", visible );
}

void MarbleMap::setShowRivers( bool visible )
{
    setPropertyValue( "rivers", visible );
}

void MarbleMap::setShowLakes( bool visible )
{
    setPropertyValue( "lakes", visible );
    // Update texture map during the repaint that follows:
    d->m_textureLayer.setNeedsUpdate();
}

void MarbleMap::setShowFrameRate( bool visible )
{
    d->m_showFrameRate = visible;
}

void MarbleMap::setShowBackground( bool visible )
{
    d->m_layerManager.setShowBackground( visible );
}

void MarbleMap::notifyMouseClick( int x, int y )
{
    qreal  lon   = 0;
    qreal  lat   = 0;

    const bool valid = geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian );

    if ( valid ) {
        emit mouseClickGeoPosition( lon, lat, GeoDataCoordinates::Radian );
    }
}

void MarbleMap::clearVolatileTileCache()
{
    d->m_textureLayer.update();
    mDebug() << "Cleared Volatile Cache!";
}

void MarbleMap::setVolatileTileCacheLimit( quint64 kilobytes )
{
    mDebug() << "kiloBytes" << kilobytes;
    d->m_textureLayer.setVolatileCacheLimit( kilobytes );
}


bool MarbleMap::mapCoversViewport()
{
    return d->m_viewport.mapCoversViewport();
}

AngleUnit MarbleMap::defaultAngleUnit() const
{
    if ( GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::Decimal ) {
        return DecimalDegree;
    }

    return DMSDegree;
}

void MarbleMap::setDefaultAngleUnit( AngleUnit angleUnit )
{
    if ( angleUnit == DecimalDegree ) {
        GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::Decimal );
        return;
    }

    GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::DMS );
}

QFont MarbleMap::defaultFont() const
{
    return GeoDataFeature::defaultFont();
}

void MarbleMap::setDefaultFont( const QFont& font )
{
    GeoDataFeature::setDefaultFont( font );
    d->m_placemarkLayout.requestStyleReset();
}

QList<RenderPlugin *> MarbleMap::renderPlugins() const
{
    return d->m_layerManager.renderPlugins();
}

QList<AbstractFloatItem *> MarbleMap::floatItems() const
{
    return d->m_layerManager.floatItems();
}

AbstractFloatItem * MarbleMap::floatItem( const QString &nameId ) const
{
    foreach ( AbstractFloatItem * floatItem, floatItems() ) {
        if ( floatItem && floatItem->nameId() == nameId ) {
            return floatItem;
        }
    }

    return 0; // No item found
}

QList<AbstractDataPlugin *> MarbleMap::dataPlugins()  const
{
    return d->m_layerManager.dataPlugins();
}

QList<AbstractDataPluginItem *> MarbleMap::whichItemAt( const QPoint& curpos ) const
{
    return d->m_layerManager.whichItemAt( curpos );
}

void MarbleMap::addLayer( LayerInterface *layer )
{
    d->m_layerManager.addLayer(layer);
}

void MarbleMap::removeLayer( LayerInterface *layer )
{
    d->m_layerManager.removeLayer(layer);
}

MeasureTool *MarbleMap::measureTool()
{
    return &d->m_measureTool;
}

// this method will only temporarily "pollute" the MarbleModel class
TextureLayer* MarbleMap::textureLayer()
{
    return &d->m_textureLayer;
}

}

#include "MarbleMap.moc"
