// ********************************************************************
//                  mainwindow.cpp
//                  --------------
//
//      Date:       15. 5. 2013
//      Authors:    Funakova Marie, Karochova Simona, Vojtechovsky Tomas
//      Web:        http://geo.fsv.cvut.cz/gwiki/153PIN2
//
//      Semestral project in course 153PIN2 (Projekt Informatika 2)
//      at Czech Technical University, Faculty of Civil Engineering.
//
//      The main function is to create cartogram from loaded shapefile.
//      It is possible to set the field cartogram is created from, number
//      of classes, color palette, labeling and edit breakpoints.
//
//      The application is written in C + + using the QGIS libraries.
//
// *********************************************************************


// Qt includes
// -----------
#include <QApplication>

#include "mainwindow.h"

// #include <iostream>
#include <QFileDialog>
#include <QFileInfo>

#include <QStringList> // Because of list of classification symbols
#include <QVariant> // Because of Min and Max of column atr. table

#include <QMessageBox>
#include <QMouseEvent>


// QGIS Includes
// -------------

#include <qgsapplication.h>

#include <qgsvectorlayer.h>
#include <qgsmapcanvas.h>

#include <qgsmaplayerregistry.h>
#include <qgsvectordataprovider.h>

#include <qgssinglesymbolrenderer.h>
#include <qgsgraduatedsymbolrenderer.h>

#include <qgssymbol.h>

// QGIS Map tools

#include "qgsmaptoolpan.h"
#include "qgsmaptoolzoom.h"

// Labelling related

#include <qgslabel.h>
#include <qgslabelattributes.h>

using namespace std;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    //mpTileScaleWidget( NULL )
    ui(new Ui::MainWindow)
{
    //setupUi( this );
    ui->setupUi(this);

    // Create the Map Canvas
    mMapCanvas= new QgsMapCanvas(0, 0);
    mMapCanvas->enableAntiAliasing(true);
    mMapCanvas->useImageToRender(false);
    mMapCanvas->setCanvasColor(QColor(255, 255, 255));
    mMapCanvas->freeze(false);
    mMapCanvas->setVisible(true);
    mMapCanvas->refresh();
    mMapCanvas->show();

    // Lay our widgets out in the main window
    mLayout = new QVBoxLayout(ui->frameMap);
    mLayout->addWidget(mMapCanvas);

    // set application's caption
    caption = "Choropleth Creator";
    setWindowTitle( caption );

    // Create tools, menus, etc.
    createCanvasTools();
}

MainWindow::~MainWindow()
{
    delete mZoomInTool;
    delete mZoomOutTool;
    delete mZoomFullTool;
    delete mPanTool;
    delete mMapToolBar;
    delete mMapCanvas;
    delete mLayout;
    delete mScaleEdit;
    delete mScaleLabel;
}

// -----------------------------------------------------
//          Create widgets
// -----------------------------------------------------

void MainWindow::createCanvasTools()
{

    // Set up the mode combo
    // ------------------------------

    ui->cboMode->addItem( tr( "Equal Interval" ) );
    ui->cboMode->addItem( tr( "Editable Breakpoints" ) ); //SK pridano

    ui->chkEnableLabeling->setEnabled(false);
    ui->cboFieldName->setEnabled(false);

    // Set min / max of number classify class
    ui->spinBoxNumberOfClasses->setMinimum(1);
    ui->spinBoxNumberOfClasses->setMaximum(12);

    // Add color ramps
    ui->comboBoxColor->addItem(tr("white -> blue"));
    ui->comboBoxColor->addItem(tr("white -> cyan"));
    ui->comboBoxColor->addItem(tr("white -> green"));
    ui->comboBoxColor->addItem(tr("white -> yellow"));
    ui->comboBoxColor->addItem(tr("white -> red"));
    ui->comboBoxColor->addItem(tr("white -> magenta"));
    ui->comboBoxColor->addItem(tr("greyscale"));
    ui->comboBoxColor->addItem(tr("rainbow"));
    ui->comboBoxColor->addItem(tr("orange -> blue"));

    // Set rows and colums of table
    int classes = ui->spinBoxNumberOfClasses->value();

    ui->tableWidgetValues->setRowCount(classes);

    int rows = ui->tableWidgetValues->rowCount();

    for (int i= 0; i < rows; i++)
    {
        ui->tableWidgetValues->setRowHeight(i,18);
    }

    ui->tableWidgetValues->setColumnWidth(0,25);
    ui->tableWidgetValues->setColumnWidth(1,70);
    ui->tableWidgetValues->setColumnWidth(2,70);

    // Set disabled of widgets
    ui->groupBoxChoroplethSettings->setDisabled(true);
    ui->cboColumn->setDisabled(true);
    ui->chkEnableLabeling->setDisabled(true);
    ui->ApplyPushButton->setDisabled(true);
    ui->cboMode->setDisabled(true);
    ui->comboBoxColor->setDisabled(true);
    ui->checkBoxInverseColor->setDisabled(true);
    ui->spinBoxNumberOfClasses->setDisabled(true);
    ui->tableWidgetValues->setDisabled(true);







    // --------------------------------

    // Set wheel action to canvas

    QSettings mySettings;
    int action = mySettings.value( "/qgis/wheel_action", 2 ).toInt();
    double zoomFactor = mySettings.value( "/qgis/zoom_factor", 2 ).toDouble();
    mMapCanvas->setWheelAction(( QgsMapCanvas::WheelAction ) action, zoomFactor );

    // -----------------------------------

    // Add combobox with scale

    QList<QString> ScaleList;
    ScaleList << "100"
              << "500"
              << "1000"
              << "2500"
              << "5000"
              << "10000"
              << "25000"
              << "50000"
              << "100000"
              << "250000"
              << "500000"
              << "1000000"
              << "2500000"
              << "5000000"
              << "100000000" ;

    ScaleComboBox = new QComboBox;
    ScaleComboBox->setEditable(true);
    ScaleComboBox->setMinimumWidth(130);
    ScaleComboBox->setMinimumHeight(20);

    // Add items to combobox by QList
    for (int i = 0; i < ScaleList.size() - 1; i++)
    {
        ScaleComboBox->addItem("1 : " + ScaleList[i]);
    }

    // show scale in combobox and zoom map by item in combobox
    connect(ScaleComboBox, SIGNAL(editTextChanged(QString)), this, SLOT( userScale() ) );
    connect( mMapCanvas, SIGNAL (scaleChanged( double ) ), this, SLOT( showScale() ) );


    // -----------------------------------

    // Create menus

    //create the action behaviours
    connect(ui->mActionPan, SIGNAL(triggered()), this, SLOT(panMode()));
    connect(ui->mActionZoomIn, SIGNAL(triggered()), this, SLOT(zoomInMode()));
    connect(ui->mActionZoomOut, SIGNAL(triggered()), this, SLOT(zoomOutMode()));
    connect(ui->mActionAddShapefile, SIGNAL(triggered()), this, SLOT(addShapefile()));
    connect(ui->mActionZoomFull, SIGNAL(triggered()), this, SLOT(zoomFull()));
    connect(ui->mActionSave_Map_As, SIGNAL(triggered()), this, SLOT(saveMapAsImage()));
    connect(ui->mActionQuit, SIGNAL( triggered() ), this, SLOT( CloseApp() ) );

    //create a little toolbar
    mMapToolBar = addToolBar(tr("Map Navigation"));
    mMapToolBar->addAction(ui->mActionAddShapefile);
    mMapToolBar->addAction(ui->mActionZoomIn);
    mMapToolBar->addAction(ui->mActionZoomOut);
    mMapToolBar->addAction(ui->mActionPan);
    mMapToolBar->addAction(ui->mActionZoomFull);

    //create a little toolbar with scale
    mScaleToolBar = addToolBar(tr("Scale"));
    mScaleToolBar->addWidget(ScaleComboBox);

    //create the maptools
    mPanTool = new QgsMapToolPan(mMapCanvas);
    mPanTool->setAction(ui->mActionPan);

    mZoomInTool = new QgsMapToolZoom(mMapCanvas, FALSE); // false = in
    mZoomInTool->setAction(ui->mActionZoomIn);

    mZoomOutTool = new QgsMapToolZoom(mMapCanvas, TRUE ); //true = out
    mZoomOutTool->setAction(ui->mActionZoomOut);
}

// -----------------------------------------------------
//          SCALE, EXTENT
// -----------------------------------------------------

// Show actual scale to scalecombobox
void MainWindow::showScale()
{
    QString scString;

    if (mLayer)
    {
        // read scale from canvas
        double sc = (int)mMapCanvas->scale();
        if (sc <= 500000000 && sc >= 0)
        {
            scString = "1 : " + QString::number(sc, 'f', 0);
        }
        else
        {
            scString = "< 1 : 500000000";
        }
    }
    else
    {
        scString = "";
    }

    ScaleComboBox->lineEdit()->setText(scString);
}
// Set scale to canvas
void MainWindow::userScale()
{
    if ( mOldScale == ScaleComboBox->currentText() )
    {
        return;
    }

    QStringList parts = ScaleComboBox->currentText().split( ':' );
    if ( parts.size() == 2 )
    {
        bool leftOk, rightOk;
        double leftSide = parts.at( 0 ).toDouble( &leftOk );
        double rightSide = parts.at( 1 ).toDouble( &rightOk );
        if ( leftSide > 0.0 && leftOk && rightOk )
        {
            mMapCanvas->zoomScale( rightSide / leftSide );
        }
    }
    else
    {
        bool rightOk;
        double rightSide = parts.at( 0 ).toDouble( &rightOk );
        if ( rightOk )
        {
            mMapCanvas->zoomScale( rightSide );
        }
    }
}
// Zoom In
void MainWindow::zoomInMode()
{
    mMapCanvas->setMapTool(mZoomInTool);
}
// Zoom Out
void MainWindow::zoomOutMode()
{
    mMapCanvas->setMapTool(mZoomOutTool);
}
// Zoom foll extent, 5% margin
void MainWindow::zoomFull()
{
    mMapCanvas->zoomToFullExtent();
}
// Pan
void MainWindow::panMode()
{
    mMapCanvas->setMapTool(mPanTool);
}


// -----------------------------------------------------
//          CLASSIFICATION, COLOR, SYMBOLS
// -----------------------------------------------------

// Classification and symbols
void MainWindow::choroplethSettings()
{
    ui->tableWidgetValues->clearContents();

    QString NameAttribute = ui->cboColumn->currentText();

    // get minimum and maximum of values column atributte table
    int index = mLayer->fieldNameIndex(NameAttribute);

    double minimum = mLayer->minimumValue(index).toDouble();
    double maximum = mLayer->maximumValue(index).toDouble();

    mValues.clear();

    QGis::GeometryType m_type = mLayer->geometryType();

    //todo: setup a data structure which holds the symbols
    std::list<QgsSymbol*> symbolList;
    for ( int i = 0; i < ui->spinBoxNumberOfClasses->value(); ++i )
    {
        QgsSymbol* symbol = new QgsSymbol( m_type );
        symbol->setLabel( "" );
        QPen pen;
        QBrush brush;

        // todo: These color ramps should come from a dropdown list
        QString ramp;
        ramp = ui->comboBoxColor->currentText(); //"red_to_green";
        if ( m_type == QGis::Line )
        {
            pen.setColor( getColorFromRamp( ramp, i, ui->spinBoxNumberOfClasses->value() ) );
        }
        else //point or polygon
        {
            brush.setColor( getColorFromRamp( ramp, i, ui->spinBoxNumberOfClasses->value() ) );
            pen.setColor( Qt::black );
        }

        pen.setWidthF( symbol->lineWidth() );
        brush.setStyle( Qt::SolidPattern );
        symbol->setPen( pen );
        symbol->setBrush( brush );
        symbolList.push_back( symbol );

        double lower = minimum + ( maximum - minimum ) / ui->spinBoxNumberOfClasses->value() * i;

        QString lowerString = QString::number( lower, 'f', 3 );

        mValues.insert(lowerString,symbol);

    }

    QString lowerString, upperString;

    std::list<QgsSymbol*>::const_iterator symbol_it = symbolList.begin();

    for ( int i = 0; i < ui->spinBoxNumberOfClasses->value(); ++i )
    {
        //switch if attribute is int or double
        double lower = minimum + ( maximum - minimum ) / ui->spinBoxNumberOfClasses->value() * i;
        double upper = minimum + ( maximum - minimum ) / ui->spinBoxNumberOfClasses->value() * ( i + 1 );
        lowerString = QString::number( lower, 'f', 3 );
        upperString = QString::number( upper, 'f', 3 );
        ( *symbol_it )->setLowerValue( lowerString );
        ( *symbol_it )->setUpperValue( upperString );

        //items to be set into the table
        QTableWidgetItem * mypItem = new QTableWidgetItem( lowerString );
        QTableWidgetItem *itemUp = new QTableWidgetItem( upperString );
        QTableWidgetItem *sym = new QTableWidgetItem( "" );

        //icon (color) in the first column
        updateEntryIcon( *symbol_it, sym);

        // set the item into the table
        ui->tableWidgetValues->setItem(i,0,sym);
        ui->tableWidgetValues->setItem(i,1,mypItem);
        ui->tableWidgetValues->setItem(i,2,itemUp);

        mEntries.insert( std::make_pair( upperString, *symbol_it ) );
        ++symbol_it;

        // set the items in table widget not editable
        itemUp->setFlags(itemUp->flags() &~Qt::ItemIsEditable );
        sym->setFlags(sym->flags() &~Qt::ItemIsEditable );
        mypItem->setFlags(mypItem->flags() &~Qt::ItemIsEditable );

    }
}
// Classification and symboms when table is editabling
void MainWindow::choroplethSet_ForEditable()
{
    mValues.clear();

    // name of selected column for create choropleth
    QString NameAttribute = ui->cboColumn->currentText();

    int index = mLayer->fieldNameIndex(NameAttribute);

    // shape type of layer
    QGis::GeometryType m_type = mLayer->geometryType();

    std::list<double> upperList; // list of upper values

    for ( int i = 0; i < (ui->spinBoxNumberOfClasses->value()); ++i )
    {
        QString upperString = ui->tableWidgetValues->item(i, 2)->text();
        double upperDouble = upperString.toDouble();
        upperList.push_back(upperDouble); //add upper value to the list
    }

    upperList.sort(); //sort the list of  upper values

    for ( int i = 0; i < ui->spinBoxNumberOfClasses->value(); ++i )
    {
        int n = 3; // number of decimal places

        QgsSymbol* symbol = new QgsSymbol( m_type );
        symbol->setLabel( "" );
        QPen pen;
        QBrush brush;

        // todo: These color ramps should come from a dropdown list
        QString ramp;
        ramp = ui->comboBoxColor->currentText();
        if ( m_type == QGis::Line )
        {
            pen.setColor( getColorFromRamp( ramp, i, ui->spinBoxNumberOfClasses->value() ) );
        }
        else //point or polygon
        {
            brush.setColor( getColorFromRamp( ramp, i, ui->spinBoxNumberOfClasses->value() ) );
            pen.setColor( Qt::black );
        }

        pen.setWidthF( symbol->lineWidth() );
        brush.setStyle( Qt::SolidPattern );
        symbol->setPen( pen );
        symbol->setBrush( brush );

        // getting lower strings
        QString lowerString;
        if (i==0)   // for first class
        {
            double lower = mLayer->minimumValue(index).toDouble(); // find minimum value
            lowerString = QString::number( lower, 'f', n );
        }
        else        // for the other classes
        {
            lowerString = ui->tableWidgetValues->item(i-1, 2)->text(); //set the lower string from previous upper string
        }

        double upperDouble = upperList.front(); // get upper string from the list
        QString upperString = QString::number( upperDouble, 'f', n );

        // delete first item of the list
        upperList.pop_front();

        symbol->setLowerValue(lowerString);
        symbol->setUpperValue(upperString);

        //create items
        QTableWidgetItem * mypItem = new QTableWidgetItem( lowerString );
        QTableWidgetItem *itemUp = new QTableWidgetItem( upperString );
        QTableWidgetItem *sym = new QTableWidgetItem( "" );

        mValues.insert(lowerString,symbol);

        updateEntryIcon( symbol,sym);

        //set items to the table
        ui->tableWidgetValues->setItem(i,0,sym);
        ui->tableWidgetValues->setItem(i,1,mypItem);
        ui->tableWidgetValues->setItem(i,2,itemUp);

        mEntries.insert( std::make_pair( upperString, symbol ) );
    }
}

// Set color ramp from colorcombobox
QColor MainWindow::getColorFromRamp( QString ramp, int step, int totalSteps )
{
    Q_UNUSED( ramp );
    QColor color;

    if (ui->checkBoxInverseColor->isChecked())
    {
        step = (totalSteps - 1) - step;
    }

    if (totalSteps<2)
    {
        return color = QColor(255,255,255);
    }
    else{

        if (ramp == "orange -> blue")
            color = QColor( 255 - (( 255 / (totalSteps-1) ) * step ), 127, (( 255 / (totalSteps-1) ) * step ) );
        if (ramp == "white -> yellow")
            color = QColor( 255, 255, 255 - (( 255 / (totalSteps-1) ) * step ));
        if (ramp == "white -> red")
            color = QColor( 255, 255 - (( 255 / (totalSteps-1) ) * step ), 255 - (( 255 / (totalSteps-1) ) * step ));
        if (ramp == "white -> magenta")
            color = QColor( 255, 255- (( 255 / (totalSteps-1) ) * step ), 255 );
        if (ramp == "white -> blue")
            color = QColor( 255 - (( 255 / (totalSteps-1) ) * step ), 255 - (( 255 / (totalSteps-1) ) * step ), 255 );
        if (ramp == "white -> cyan")
            color = QColor( 255 - (( 255 / (totalSteps-1) ) * step ), 255, 255);
        if (ramp == "white -> green")
            color = QColor( 255 - (( 255 / (totalSteps-1) ) * step ), 255, 255 - (( 255 / (totalSteps-1) ) * step ));
        if (ramp == "greyscale")
            color = QColor( 255 - (( 255 / (totalSteps-1) ) * step ), 255 - (( 255 / (totalSteps-1) ) * step ), 255 - (( 255 / (totalSteps-1) ) * step ));

        if (ramp == "rainbow")
        {
            double pomer=double(step)/double(totalSteps-1);
            double hranice= 0.2;

            if (pomer < hranice)
            {
                double pozice= pomer/hranice;
                int nahoru = 255.0*pozice;
                color = QColor( 255 , nahoru , 0 );
            }
            if (pomer >=hranice && pomer <(2.0*hranice))
            {
                double pozice = (pomer-hranice)/hranice;
                int dolu = 255.0*(1-pozice);
                color = QColor( dolu , 255 , 0 );
            }
            if (pomer >=(2.0*hranice) && pomer <(3.0*hranice))
            {
                double pozice = (pomer-(2.0*hranice))/hranice;
                int nahoru = 255.0*pozice;
                color = QColor( 0 , 255 , nahoru );
            }
            if (pomer >=(3.0*hranice) && pomer <(4.0*hranice))
            {
                double pozice = (pomer-(3.0*hranice))/hranice;
                int dolu = 255.0*(1.0-pozice);
                color = QColor( 0 , dolu , 255 );
            }
            if (pomer >=(4.0*hranice))
            {
                double pozice = (pomer-(4.0*hranice))/hranice;
                int nahoru = 255.0*pozice;
                color = QColor( nahoru , 0 , 255 );
            }
        }
    }
    return color;
}

// Set symbol
void MainWindow::updateEntryIcon( QgsSymbol * thepSymbol, QTableWidgetItem * thepItem )
{
    QGis::GeometryType myType = mLayer->geometryType();
    switch ( myType )
    {
    case QGis::Point:
    {
        int myWidthScale = 4; //magick no to try to make vector props dialog preview look same as legend
        thepItem->setIcon( QIcon( QPixmap::fromImage( thepSymbol->getPointSymbolAsImage( myWidthScale ) ) ) );
    }
        break;
    case QGis::Line:
        thepItem->setIcon( QIcon( QPixmap::fromImage( thepSymbol->getLineSymbolAsImage() ) ) );
        break;
    case QGis::Polygon:
        thepItem->setIcon( QIcon( QPixmap::fromImage( thepSymbol->getPolygonSymbolAsImage() ) ) );
        break;
    default: //unknown
        //do nothing
        ;
    }
}

// Create renderer and set provider
void MainWindow::CreateRender()
{
    // create new graduated symbol renderer
    QgsGraduatedSymbolRenderer *renderer = new QgsGraduatedSymbolRenderer( mLayer->geometryType() );

    //go through mValues and add the entries to the renderer
    for ( QMap<QString, QgsSymbol*>::iterator it = mValues.begin(); it != mValues.end(); ++it )
    {
        QgsSymbol* symbol = it.value();
        QgsSymbol* newsymbol = new QgsSymbol( mLayer->geometryType(), symbol->lowerValue(), symbol->upperValue(), symbol->label() );
        newsymbol->setPen( symbol->pen() );
        newsymbol->setCustomTexture( symbol->customTexture() );
        newsymbol->setBrush( symbol->brush() );
        newsymbol->setNamedPointSymbol( symbol->pointSymbolName() );
        newsymbol->setPointSize( symbol->pointSize() );
        newsymbol->setPointSizeUnits( symbol->pointSizeUnits() );
        newsymbol->setScaleClassificationField( symbol->scaleClassificationField() );
        newsymbol->setRotationClassificationField( symbol->rotationClassificationField() );
        renderer->addSymbol(newsymbol);
    }

    renderer->updateSymbolAttributes();

    QgsVectorDataProvider *provider = dynamic_cast<QgsVectorDataProvider *>( mLayer->dataProvider() );
    if ( provider )
    {
        QString NameAttribute = ui->cboColumn->currentText();   // name of selected item of combobox
        int fieldIndex = mLayer->fieldNameIndex(NameAttribute); // index of this item
        if ( fieldIndex != -1 )
        {
            renderer->setClassificationField( fieldIndex );
            mLayer->setRenderer( renderer );
            return;
        }
    }

    delete renderer; //something went wrong
}


// -----------------------------------------------------
//          CONTROL OF WIDGETS, SLOTS
// -----------------------------------------------------

// when the number of classes is changed
void MainWindow::on_spinBoxNumberOfClasses_valueChanged(int arg1)
{
    ui->tableWidgetValues->setRowCount(arg1);
    for (int i= 0; i<arg1; i++)
    {
        ui->tableWidgetValues->setRowHeight(i,18);
    }
    if (ui->cboMode->currentText()=="Editable Breakpoints"){
        // change mode to equal interval
        ui->cboMode->setCurrentIndex(0);

    }

    //if number of classes in not between 1 and 12

    if (ui->spinBoxNumberOfClasses->value()<1)
    {
        ui->spinBoxNumberOfClasses->setValue(1);
        choroplethSettings();
    }

    else if(ui->spinBoxNumberOfClasses->value()>12)
    {
        ui->spinBoxNumberOfClasses->setValue(12);
        choroplethSettings();
    }

    else
    {
        choroplethSettings();
    }

    QString number =  QString::number(arg1, 'f', 0);

    ui->statusbar->showMessage("Number of classes: "+ number);
}
//when the field is changed
void MainWindow::on_cboColumn_currentIndexChanged()
{
    QString NameAttribute = ui->cboColumn->currentText();

    if (ui->cboMode->currentText()=="Editable Breakpoints"){
        // change mode to equal interval
        ui->cboMode->setCurrentIndex(0);
    }\

    choroplethSettings();

    addStatistics(NameAttribute);

    ui->statusbar->showMessage("Clasification field changed: "+NameAttribute);
}
// when color is changed
void MainWindow::on_comboBoxColor_activated(const QString &arg1)
{
    if (ui->cboMode->currentText()=="Editable Breakpoints")
    {
        choroplethSet_ForEditable();
    }

    else
    {
        choroplethSettings();
    }

    QString color = ui->comboBoxColor->currentText();

    ui->statusbar->showMessage("Palette changed: "+color);
}
// when inverse is changed
void MainWindow::on_checkBoxInverseColor_clicked()
{
    QString color = ui->comboBoxColor->currentText();

    if (ui->checkBoxInverseColor->isChecked())
    {
        ui->statusbar->showMessage("Palette changed: inverse"+color);
    }
    else
    {
        ui->statusbar->showMessage("Palette changed: "+color);
    }

    if (ui->cboMode->currentText()=="Editable Breakpoints")
    {
        choroplethSet_ForEditable();
    }

    else
    {
        choroplethSettings();
    }

}
// when mode of classification changed
void MainWindow::on_cboMode_activated(int index)
{
    int num = ui->spinBoxNumberOfClasses->value();

    if (index==0) //equal -- not editable
    {
        for (int i = 0; i < num; ++i)
        {
            choroplethSettings();
        }
        ui->statusbar->showMessage("Mode: Equal Interval.");
    }
    else { //editable

        for (int i = 0; i < num; ++i)
        {
            QTableWidgetItem *item = ui->tableWidgetValues->item(i,2);
            item->setFlags(item->flags()  |Qt::ItemIsEditable);
            ui->statusbar->showMessage("Mode: Editable breakpoints");
        }
    }
}
// create choropleth, update changed table
void MainWindow::on_ApplyPushButton_clicked()
{
    if (ui->cboMode->currentText()=="Editable Breakpoints") //if mode is editable
    {
        choroplethSet_ForEditable();
    }

    CreateRender();

    // ----------
    if ( mLayer )
    {
        const QgsGraduatedSymbolRenderer* renderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( mLayer->renderer() );

        if ( renderer )
        {
            QString field = mLayer->attributeDisplayName( renderer->classificationField() );
            mOldClassificationAttribute = field;

            const QList<QgsSymbol*> list = renderer->symbols();
            //fill the items of the renderer into mValues
            for ( QList<QgsSymbol*>::const_iterator iter = list.begin(); iter != list.end(); ++iter )
            {

                QgsSymbol* symbol = *iter;
                QString symbolvalue = symbol->lowerValue();
                QgsSymbol* sym = new QgsSymbol( mLayer->geometryType(), symbol->lowerValue(), symbol->upperValue(), symbol->label() );
                sym->setPen( symbol->pen() );

                sym->setCustomTexture( symbol->customTexture() );

                sym->setBrush( symbol->brush() );

                sym->setNamedPointSymbol( symbol->pointSymbolName() );
                sym->setPointSize( symbol->pointSize() );
                sym->setPointSizeUnits( symbol->pointSizeUnits() );
                sym->setScaleClassificationField( symbol->scaleClassificationField() );
                sym->setRotationClassificationField( symbol->rotationClassificationField() );
                mValues.insert( symbolvalue, sym );

            }

        }
    }

    QString color = ui->comboBoxColor->currentText();
    QString number =QString::number(ui->spinBoxNumberOfClasses->value(),'f', 0 );

    if (ui->checkBoxInverseColor->isChecked())
    {
        ui->statusbar->showMessage("Cartogram rendered. Palette: inverse "+color+ ". Number of classes: "+number);
    }
    else
    {
        ui->statusbar->showMessage("Cartogram rendered. Palette: "+color+ ". Number of classes: "+number);
    }



    mMapCanvas->refresh();


}

// labels

// show / hide label
void MainWindow::on_chkEnableLabeling_clicked()
{
    if (ui->chkEnableLabeling->checkState() == Qt::Checked) {
        mLayer->enableLabels(true);

        ui->cboFieldName->setEnabled(true);

        // Start labeling
        on_cboFieldName_activated(ui->cboFieldName->currentIndex());

        QString colu = ui->cboFieldName->currentText();

        ui->statusbar->showMessage("Labels on: "+colu);
    }
    else
    {
        mLayer->enableLabels(false);
        ui->cboFieldName->setEnabled(false);
        ui->statusbar->showMessage("Labels off.");
    }

    // refresh canvas


    mMapCanvas->refresh();
}
// fill labelcombobox by column of atr. table
void MainWindow::populateFieldNames()
{
    ui->cboFieldName->clear();

    const QgsFieldMap& fields = mLayer->pendingFields();
    for ( int idx = 0; idx < fields.count(); ++idx )
    {
        ui->cboFieldName->addItem( fields[idx].name() );
    }
}
// when item in labelcombobox changed
void MainWindow::on_cboFieldName_activated(int index)
{
    //get the label instance associated with the layer
    QgsLabel * mypLabel;
    mypLabel = mLayer->label();
    //and the label attributes associated with the label
    QgsLabelAttributes * mypLabelAttributes;
    mypLabelAttributes = mypLabel->layerAttributes();

    mypLabel->setLabelField( QgsLabel::Text, index);

    //set the colour of the label text
    //mypLabelAttributes->setColor(Qt::black);

    //create a 'halo' effect around each label so it
    //can still be read on dark backgrounds
    mypLabelAttributes->setBufferEnabled(true);
    mypLabelAttributes->setBufferColor(Qt::white);
    int myType = QgsLabelAttributes::PointUnits;
    mypLabelAttributes->setBufferSize(1,myType);

    QString colu = ui->cboFieldName->currentText();

    ui->statusbar->showMessage("Labels on: "+colu);

    mMapCanvas->refresh();
}

// table

// when there is a click on an item
void MainWindow::on_tableWidgetValues_itemClicked(QTableWidgetItem *item)
{
    ui->tableWidgetValues->editItem(item);  //start editing
}
// when item is changed
void MainWindow::on_tableWidgetValues_itemChanged(QTableWidgetItem *item)
{
    int n = 3; // number of decimal places

    QString NameAttribute = ui->cboColumn->currentText();
    int index = mLayer->fieldNameIndex(NameAttribute);

    double mini = mLayer->minimumValue(index).toDouble();
    double maxi = mLayer->maximumValue(index).toDouble();

    QString minString = QString::number( mini, 'f', n );
    QString maxString = QString::number( maxi, 'f', n );

    // inserted value
    double inval = item->text().toDouble();

    // compare inserted value to minimum and maximum
    if (inval > maxi)   // if inserted value is higher than maximum
    {
        item->setText(maxString); // set maximum as inserted value
        ui->statusbar->showMessage("Inserted value out of range. Value changed to the highest value "+maxString+".");
    }

    if (inval < mini)   // if inserted value is lower than minimum
    {
        item->setText(minString);  // set minimum as inserted value
        ui->statusbar->showMessage("Inserted value out of range. Value changed to the lowest value "+minString+".");
    }
}

// another

// Load Shapefile
void MainWindow::addShapefile()
{
    ui->chkEnableLabeling->setChecked(false);

    QString layerPath = QFileDialog::getOpenFileName(this, "Load Shapefile", ".", "Shapefiles (*.shp)");
    QString myProviderName = "ogr";
    QString fileName = QFileInfo(layerPath).fileName();

    // create new vector layer from loaded shapefile
    mLayer = new QgsVectorLayer(layerPath, fileName, myProviderName);

    // check if layer is valid
    if (!mLayer->isValid())
        return;

    // Test polygon layer
    // If loaded file is not polygon layer, new dialog with choice of new select of layer.
    if (mLayer->geometryType() != QGis::Polygon)
    {
        switch( QMessageBox::warning( this, "No polygon layer",
                                      "Shape type of this file is not polygon.\nPlease load polygon layer.",
                                      QMessageBox::Ok | QMessageBox::Default,
                                      QMessageBox::Cancel | QMessageBox::Escape ))
        {
        case QMessageBox::Ok: // Ok or Enter
            addShapefile();
            break;
        case QMessageBox::Cancel: // Cancel or Cancel
            // Set disabled of widgets
            ui->groupBoxChoroplethSettings->setDisabled(true);
            ui->cboColumn->setDisabled(true);
            ui->chkEnableLabeling->setDisabled(true);
            ui->ApplyPushButton->setDisabled(true);
            ui->cboMode->setDisabled(true);
            ui->comboBoxColor->setDisabled(true);
            ui->checkBoxInverseColor->setDisabled(true);
            ui->spinBoxNumberOfClasses->setDisabled(true);
            ui->tableWidgetValues->setDisabled(true);

            return;

            break;
        }

        return;
    }

    // Select only column with numeric values
    int pocet = addNumericFields();

    if (pocet == 0){
        switch( QMessageBox::warning( this, "No numeric column",
                                      "Loaded shapefile have any column with numeric values.\nPlease reload shapefile whose attributte table contains at least one column with numeric values.",
                                      QMessageBox::Ok | QMessageBox::Default,
                                      QMessageBox::Cancel | QMessageBox::Escape ))
        {
        case QMessageBox::Ok: // Ok or Enter
            addShapefile();

            break;
        case QMessageBox::Cancel: // Cancel or Cancel
            // Set disabled of widgets
            ui->groupBoxChoroplethSettings->setDisabled(true);
            ui->cboColumn->setDisabled(true);
            ui->chkEnableLabeling->setDisabled(true);
            ui->ApplyPushButton->setDisabled(true);
            ui->cboMode->setDisabled(true);
            ui->comboBoxColor->setDisabled(true);
            ui->checkBoxInverseColor->setDisabled(true);
            ui->spinBoxNumberOfClasses->setDisabled(true);
            ui->tableWidgetValues->setDisabled(true);

            return;

            break;
        }

        return;

    }
    else
    {
        ui->groupBoxChoroplethSettings->setEnabled(true);
        ui->cboColumn->setEnabled(true);
        ui->chkEnableLabeling->setEnabled(true);
        ui->ApplyPushButton->setEnabled(true);
        ui->cboMode->setEnabled(true);
        ui->comboBoxColor->setEnabled(true);
        ui->checkBoxInverseColor->setEnabled(true);
        ui->spinBoxNumberOfClasses->setEnabled(true);
        ui->tableWidgetValues->setEnabled(true);

        //set up a renderer for the layer
        QgsSingleSymbolRenderer *mypRenderer = new QgsSingleSymbolRenderer(mLayer->geometryType());
        QList<QgsMapCanvasLayer> mLayerSet;
        mLayer->setRenderer(mypRenderer);
        QgsMapLayerRegistry::instance()->addMapLayer(mLayer, TRUE);

        // Add the Layer to the Layer Set
        mLayerSet.append(QgsMapCanvasLayer( mLayer ) );

        // set teh canvas to the extent of our layer
        mMapCanvas->setExtent(mLayer->extent());

        // Set the Map Canvas Layer Set
        mMapCanvas->setLayerSet(mLayerSet);

        // Zoom full extent // 5% margin
        mMapCanvas->zoomToFullExtent();

        // set classification and symbols
        choroplethSettings();

        // Fill label combobox
        populateFieldNames();
        mMapCanvas->updateScale();

        // Update window title
        QString NameLayer= mLayer->name();
        setWindowTitle(NameLayer + " - " + caption);

        //======= INFORMATIONS ABOUT ATTRIBUTE TABLE ======//

        mLayer->enableLabels(false);
        QString jmeno = mLayer->name();

        ui->statusbar->showMessage("Loaded shapefile: "+jmeno);
    }

}

// Close, About, Help

void MainWindow::CloseApp()
{
    switch( QMessageBox::warning( this, "Exit?",
                                  "Are you sure you want to quit?",
                                  QMessageBox::Ok | QMessageBox::Default,
                                  QMessageBox::Cancel | QMessageBox::Escape ))
    {
    case QMessageBox::Ok: // Ok or Enter
        qApp->quit();
        break;
    case QMessageBox::Cancel: // Cancel or Cancel
        return;
        break;
    }

    return;

}

void MainWindow::on_mActionAbout_triggered()
{
    QMessageBox::about( this, "About", "This application was created as a semestral project in course 153PIN2 (Projekt Informatika 2) at Czech Technical University, Faculty of Civil Engineering in 2013. "
                        "The authors are: Marie Funakova, Simona Karochova and Tomas Vojtechovsky."
                        "\n\n"
                        "The main function is to create cartogram from loaded shapefile. It is possible to set the field cartogram is created from, number of classes, color palette, labeling and edit breakpoints."
                        "\n\n"
                        "The application is written in C + + using the QGIS libraries.");
}

void MainWindow::on_actionHelp_triggered()
{
    QMessageBox::about( this, "Help", "Only *.shp files  with at least one numeric column can be loaded. Number od classification field is from 1 to 12. User can choose from two methods of breakpoints: Equal Interval and Editable Breakpoints which allows entering own breakpoints by editing \"to\" field."
                        "\n"
                        "The color palette can be chosen from the list. Choropleth according to entered parameters is plotted by pushing the Apply pushbutton.");
}


// Save map as image
void MainWindow::saveMapAsImage() // export with dialog window
{
    QPair< QString, QString> myFileNameAndFilter = getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
    if ( myFileNameAndFilter.first != "" )
    {
        //save the mapview to the selected file
        mMapCanvas->saveAsImage( myFileNameAndFilter.first, NULL, myFileNameAndFilter.second );
        statusBar()->showMessage( tr( "Saved map image to %1" ).arg( myFileNameAndFilter.first ) );
    }

}

QPair<QString, QString> GUI_EXPORT MainWindow::getSaveAsImageName( QWidget *theParent, QString theMessage )
{
    // get a list of supported output image types
    QMap<QString, QString> filterMap;
    foreach( QByteArray format, QImageWriter::supportedImageFormats() )
    {
        //svg doesnt work so skip it
        if ( format ==  "svg" )
            continue;

        filterMap.insert( createFileFilter_( format + " format", "*." + format ), format );
    }

#ifdef QGISDEBUG
    QgsDebugMsg( "Available Filters Map: " );
    for ( QMap<QString, QString>::iterator it = filterMap.begin(); it != filterMap.end(); ++it )
    {
        QgsDebugMsg( it.key() + "  :  " + it.value() );
    }
#endif

    //find out the last used filter
    QSettings settings;  // where we keep last used filter in persistent state
    QString lastUsedFilter = settings.value( "/UI/lastSaveAsImageFilter" ).toString();
    QString lastUsedDir = settings.value( "/UI/lastSaveAsImageDir", "." ).toString();

    QString outputFileName;
    QString selectedFilter = lastUsedFilter;
    QString ext;

#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
    outputFileName = QFileDialog::getSaveFileName( theParent, theMessage, lastUsedDir, QStringList( filterMap.keys() ).join( ";;" ), &selectedFilter );

    if ( !outputFileName.isNull() )
    {
        ext = filterMap.value( selectedFilter, QString::null );
        if ( !ext.isNull() )
            settings.setValue( "/UI/lastSaveAsImageFilter", selectedFilter );
        settings.setValue( "/UI/lastSaveAsImageDir", QFileInfo( outputFileName ).absolutePath() );
    }
#else
    //create a file dialog using the the filter list generated above
    std::auto_ptr<QFileDialog> fileDialog( new QFileDialog( theParent, theMessage, lastUsedDir, QStringList( filterMap.keys() ).join( ";;" ) ) );

    // allow for selection of more than one file
    fileDialog->setFileMode( QFileDialog::AnyFile );
    fileDialog->setAcceptMode( QFileDialog::AcceptSave );
    fileDialog->setConfirmOverwrite( true );

    if ( !lastUsedFilter.isEmpty() )     // set the filter to the last one used
    {
        fileDialog->selectFilter( lastUsedFilter );
    }

    //prompt the user for a fileName
    if ( fileDialog->exec() == QDialog::Accepted )
    {
        outputFileName = fileDialog->selectedFiles().first();
    }

    selectedFilter = fileDialog->selectedFilter();
    QgsDebugMsg( "Selected filter: " + selectedFilter );
    ext = filterMap.value( selectedFilter, QString::null );

    if ( !ext.isNull() )
        settings.setValue( "/UI/lastSaveAsImageFilter", selectedFilter );

    settings.setValue( "/UI/lastSaveAsImageDir", fileDialog->directory().absolutePath() );
#endif

    // Add the file type suffix to the fileName if required
    if ( !ext.isNull() && !outputFileName.endsWith( "." + ext ) )
    {
        outputFileName += "." + ext;
    }

    return qMakePair<QString, QString>( outputFileName, ext );
}

QString MainWindow::createFileFilter_( QString const &longName, QString const &glob )
{
    return longName + " (" + glob.toLower() + " " + glob.toUpper() + ")";
}


// return number of numeric column in layer
int MainWindow::addNumericFields()
{
    // clear cboColumn
    ui->cboColumn->clear();

    // find out the numerical fields of mVectorLayer
    const QgsFieldMap & fields = mLayer->pendingFields();
    QString displayName;

    int CountNumericColumn = 0;

    for ( int idx = 0; idx < fields.count(); ++idx )
    {
        QVariant::Type type = fields[idx].type();
        if ( type == QVariant::Int || type == QVariant::Double || type == QVariant::LongLong )
        {
            displayName = mLayer->attributeDisplayName( idx );
            ui->cboColumn->addItem( displayName );
            mFieldMap.insert( std::make_pair( displayName, idx ) );   // CO TO JE???
            CountNumericColumn++;
        }
    }

    return CountNumericColumn;
}

// set minimum, maximum and mean to label
void MainWindow::addStatistics(QString NameAttribute)
{
    // get minimum and maximum of values column atributte table
    int index = mLayer->fieldNameIndex(NameAttribute);

    double minimum = mLayer->minimumValue(index).toDouble();
    double maximum = mLayer->maximumValue(index).toDouble();

    // Get value of selected column
    QList< QVariant > fieldList;
    mLayer->uniqueValues(index,fieldList);

    double sum = 0;

    for(int i = 1; i < fieldList.size(); i++){
        sum = sum + fieldList[i].toDouble();
    }

    double mean = sum / fieldList.size();

    //  formaty: 'f', 'F', 'e', 'E', 'g' or 'G'
    QString minimumString = QString::number(minimum, 'f', 3);
    QString maximumString = QString::number(maximum, 'f', 3);
    QString meanString = QString::number(mean, 'f', 3);

    ui->labelMinVal->setText(minimumString);
    ui->labelMaxVal->setText(maximumString);
    ui->labelMeanVal->setText(meanString);

}



