/***************************************************************************
 *   Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# include <climits>
# include <sstream>
# include <BRep_Tool.hxx>
# include <TopoDS.hxx>
# include <TopoDS_Edge.hxx>
# include <TopoDS_Shape.hxx>
# include <TopExp.hxx>
# include <TopExp_Explorer.hxx>
# include <TopTools_ListOfShape.hxx>
# include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
# include <TopTools_IndexedMapOfShape.hxx>
# include <QItemDelegate>
# include <QLocale>
# include <QHeaderView>
# include <QMessageBox>
# include <QVBoxLayout>
# include <QItemSelection>
# include <QItemSelectionModel>
# include <boost/signal.hpp>
# include <boost/bind.hpp>
# include <Inventor/actions/SoSearchAction.h>
# include <Inventor/details/SoLineDetail.h>
#endif

#include "DlgFilletEdges.h"
#include "ui_DlgFilletEdges.h"
#include "SoBrepFaceSet.h"
#include "SoBrepEdgeSet.h"
#include "SoBrepPointSet.h"


#include "../App/PartFeature.h"
#include "../App/FeatureFillet.h"
#include "../App/FeatureChamfer.h"
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Command.h>
#include <Gui/WaitCursor.h>
#include <Gui/Selection.h>
#include <Gui/SelectionFilter.h>
#include <Gui/SoFCUnifiedSelection.h>
#include <Gui/ViewProvider.h>
#include <Gui/Window.h>

using namespace PartGui;

FilletRadiusDelegate::FilletRadiusDelegate(QObject *parent) : QItemDelegate(parent)
{
}

QWidget *FilletRadiusDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */,
                                            const QModelIndex & index) const
{
    if (index.column() < 1)
        return 0;

    QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
    editor->setMinimum(0.0);
    editor->setMaximum(INT_MAX);
    editor->setSingleStep(0.1);

    return editor;
}

void FilletRadiusDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    double value = index.model()->data(index, Qt::EditRole).toDouble();

    QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
    spinBox->setValue(value);
}

void FilletRadiusDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                        const QModelIndex &index) const
{
    QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
    spinBox->interpretText();
    //double value = spinBox->value();
    //QString value = QString::fromAscii("%1").arg(spinBox->value(),0,'f',2);
    QString value = QLocale::system().toString(spinBox->value(),'f',2);

    model->setData(index, value, Qt::EditRole);
}

void FilletRadiusDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                                                const QModelIndex &/* index */) const
{
    editor->setGeometry(option.rect);
}

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

FilletRadiusModel::FilletRadiusModel(QObject * parent) : QStandardItemModel(parent)
{
}

Qt::ItemFlags FilletRadiusModel::flags (const QModelIndex & index) const
{
    Qt::ItemFlags fl = QStandardItemModel::flags(index);
    if (index.column() == 0)
        fl = fl | Qt::ItemIsUserCheckable;
    return fl;
}

bool FilletRadiusModel::setData (const QModelIndex & index, const QVariant & value, int role)
{
    bool ok = QStandardItemModel::setData(index, value, role);
    if (role == Qt::CheckStateRole) {
        toggleCheckState(index);
    }
    return ok;
}

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

namespace PartGui {
    class EdgeFaceSelection : public Gui::SelectionFilterGate
    {
        bool allowEdge;
        App::DocumentObject*& object;
    public:
        EdgeFaceSelection(App::DocumentObject*& obj)
            : Gui::SelectionFilterGate((Gui::SelectionFilter*)0), allowEdge(true), object(obj)
        {
        }
        void selectEdges()
        {
            allowEdge = true;
        }
        void selectFaces()
        {
            allowEdge = false;
        }
        bool allow(App::Document*pDoc, App::DocumentObject*pObj, const char*sSubName)
        {
            if (pObj != this->object)
                return false;
            if (!sSubName || sSubName[0] == '\0')
                return false;
            std::string element(sSubName);
            if (allowEdge)
                return element.substr(0,4) == "Edge";
            else
                return element.substr(0,4) == "Face";
        }
    };
    class DlgFilletEdges::Private
    {
    public:
        App::DocumentObject* object;
        EdgeFaceSelection* selection;
        Part::FilletBase* fillet;
        FilletType filletType;
        std::vector<int> edge_ids;
        TopTools_IndexedMapOfShape all_edges;
        TopTools_IndexedMapOfShape all_faces;
        typedef boost::signals::connection Connection;
        Connection connectApplicationDeletedObject;
        Connection connectApplicationDeletedDocument;
    };
};

/* TRANSLATOR PartGui::DlgFilletEdges */

DlgFilletEdges::DlgFilletEdges(FilletType type, Part::FilletBase* fillet, QWidget* parent, Qt::WFlags fl)
  : QWidget(parent, fl), ui(new Ui_DlgFilletEdges()), d(new Private())
{
    ui->setupUi(this);
    ui->filletStartRadius->setMaximum(INT_MAX);
    ui->filletEndRadius->setMaximum(INT_MAX);

    d->object = 0;
    d->selection = new EdgeFaceSelection(d->object);
    Gui::Selection().addSelectionGate(d->selection);

    d->fillet = fillet;
    d->connectApplicationDeletedObject = App::GetApplication().signalDeletedObject
        .connect(boost::bind(&DlgFilletEdges::onDeleteObject, this, _1));
    d->connectApplicationDeletedDocument = App::GetApplication().signalDeleteDocument
        .connect(boost::bind(&DlgFilletEdges::onDeleteDocument, this, _1));
    // set tree view with three columns
    QStandardItemModel* model = new FilletRadiusModel(this);
    connect(model, SIGNAL(toggleCheckState(const QModelIndex&)),
            this, SLOT(toggleCheckState(const QModelIndex&)));
    model->insertColumns(0,3);

    d->filletType = type;
    if (d->filletType == DlgFilletEdges::CHAMFER) {
        ui->labelRadius->setText(tr("Length:"));
        ui->filletType->setItemText(0, tr("Constant Length"));
        ui->filletType->setItemText(1, tr("Variable Length"));

        model->setHeaderData(0, Qt::Horizontal, tr("Edges to chamfer"), Qt::DisplayRole);
        model->setHeaderData(1, Qt::Horizontal, tr("Start length"), Qt::DisplayRole);
        model->setHeaderData(2, Qt::Horizontal, tr("End length"), Qt::DisplayRole);
    }
    else {
        model->setHeaderData(0, Qt::Horizontal, tr("Edges to fillet"), Qt::DisplayRole);
        model->setHeaderData(1, Qt::Horizontal, tr("Start radius"), Qt::DisplayRole);
        model->setHeaderData(2, Qt::Horizontal, tr("End radius"), Qt::DisplayRole);
    }
    ui->treeView->setRootIsDecorated(false);
    ui->treeView->setItemDelegate(new FilletRadiusDelegate(this));
    ui->treeView->setModel(model);

    QHeaderView* header = ui->treeView->header();
    header->setResizeMode(0, QHeaderView::Stretch);
    header->setDefaultAlignment(Qt::AlignLeft);
    header->setMovable(false);
    on_filletType_activated(0);
    findShapes();
}

/*  
 *  Destroys the object and frees any allocated resources
 */
DlgFilletEdges::~DlgFilletEdges()
{
    // no need to delete child widgets, Qt does it all for us
    d->connectApplicationDeletedDocument.disconnect();
    d->connectApplicationDeletedObject.disconnect();
    Gui::Selection().rmvSelectionGate();
}

void DlgFilletEdges::onSelectionChanged(const Gui::SelectionChanges& msg)
{
    // no object selected in the combobox or no sub-element was selected
    if (!d->object || !msg.pSubName)
        return;
    if (msg.Type == Gui::SelectionChanges::AddSelection ||
        msg.Type == Gui::SelectionChanges::RmvSelection) {
        // when adding a sub-element to the selection check
        // whether this is the currently handled object
        App::Document* doc = d->object->getDocument();
        std::string docname = doc->getName();
        std::string objname = d->object->getNameInDocument();
        if (docname==msg.pDocName && objname==msg.pObjectName) {
            QString subelement = QString::fromAscii(msg.pSubName);
            if (subelement.startsWith(QLatin1String("Edge"))) {
                onSelectEdge(subelement, msg.Type);
            }
            else if (subelement.startsWith(QLatin1String("Face"))) {
                d->selection->selectEdges();
                onSelectEdgesOfFace(subelement, msg.Type);
                d->selection->selectFaces();
            }
        }
    }

    if (msg.Type != Gui::SelectionChanges::SetPreselect &&
        msg.Type != Gui::SelectionChanges::RmvPreselect)
        QTimer::singleShot(20, this, SLOT(onHighlightEdges()));
}

void DlgFilletEdges::onHighlightEdges()
{
    Gui::ViewProvider* view = Gui::Application::Instance->getViewProvider(d->object);
    if (view) {
        // deselect all faces
        {
            SoSearchAction searchAction;
            searchAction.setType(PartGui::SoBrepFaceSet::getClassTypeId());
            searchAction.setInterest(SoSearchAction::FIRST);
            searchAction.apply(view->getRoot());
            SoPath* selectionPath = searchAction.getPath();
            if (selectionPath) {
                Gui::SoSelectionElementAction action(Gui::SoSelectionElementAction::None);
                action.apply(selectionPath);
            }
        }
        // deselect all points
        {
            SoSearchAction searchAction;
            searchAction.setType(PartGui::SoBrepPointSet::getClassTypeId());
            searchAction.setInterest(SoSearchAction::FIRST);
            searchAction.apply(view->getRoot());
            SoPath* selectionPath = searchAction.getPath();
            if (selectionPath) {
                Gui::SoSelectionElementAction action(Gui::SoSelectionElementAction::None);
                action.apply(selectionPath);
            }
        }
        // select the edges
        {
            SoSearchAction searchAction;
            searchAction.setType(PartGui::SoBrepEdgeSet::getClassTypeId());
            searchAction.setInterest(SoSearchAction::FIRST);
            searchAction.apply(view->getRoot());
            SoPath* selectionPath = searchAction.getPath();
            if (selectionPath) {
                ParameterGrp::handle hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("View");
                SbColor selectionColor(0.1f, 0.8f, 0.1f);
                unsigned long selection = (unsigned long)(selectionColor.getPackedValue());
                selection = hGrp->GetUnsigned("SelectionColor", selection);
                float transparency;
                selectionColor.setPackedValue((uint32_t)selection, transparency);

                // clear the selection first
                Gui::SoSelectionElementAction clear(Gui::SoSelectionElementAction::None);
                clear.apply(selectionPath);

                Gui::SoSelectionElementAction action(Gui::SoSelectionElementAction::Append);
                action.setColor(selectionColor);
                action.apply(selectionPath);

                QAbstractItemModel* model = ui->treeView->model();
                SoLineDetail detail;
                action.setElement(&detail);
                for (int i=0; i<model->rowCount(); ++i) {
                    QVariant value = model->index(i,0).data(Qt::CheckStateRole);
                    Qt::CheckState checkState = static_cast<Qt::CheckState>(value.toInt());

                    // is item checked
                    if (checkState & Qt::Checked) {
                        // the index value of the edge
                        int id = model->index(i,0).data(Qt::UserRole).toInt();
                        detail.setLineIndex(id-1);
                        action.apply(selectionPath);
                    }
                }
            }
        }
    }
}

void DlgFilletEdges::onSelectEdge(const QString& subelement, int type)
{
    Gui::SelectionChanges::MsgType msgType = Gui::SelectionChanges::MsgType(type);
    QAbstractItemModel* model = ui->treeView->model();
    for (int i=0; i<model->rowCount(); ++i) {
        int id = model->data(model->index(i,0), Qt::UserRole).toInt();
        QString name = QString::fromAscii("Edge%1").arg(id);
        if (name == subelement) {
            // ok, check the selected sub-element
            Qt::CheckState checkState =
                (msgType == Gui::SelectionChanges::AddSelection
                ? Qt::Checked : Qt::Unchecked);
            QVariant value(static_cast<int>(checkState));
            QModelIndex index = model->index(i,0);
            model->setData(index, value, Qt::CheckStateRole);
            // select the item
            ui->treeView->selectionModel()->setCurrentIndex(index,QItemSelectionModel::NoUpdate);
            QItemSelection selection(index, model->index(i,1));
            ui->treeView->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
            ui->treeView->update();
            break;
        }
    }
}

void DlgFilletEdges::onSelectEdgesOfFace(const QString& subelement, int type)
{
    bool ok;
    int index = subelement.mid(4).toInt(&ok);
    if (ok) {
        try {
            const TopoDS_Shape& face = d->all_faces.FindKey(index);
            TopTools_IndexedMapOfShape mapOfEdges;
            TopExp::MapShapes(face, TopAbs_EDGE, mapOfEdges);

            for(int j = 1; j <= mapOfEdges.Extent(); ++j) {
                TopoDS_Edge edge = TopoDS::Edge(mapOfEdges.FindKey(j));
                int id = d->all_edges.FindIndex(edge);
                QString name = QString::fromAscii("Edge%1").arg(id);
                onSelectEdge(name, type);
                Gui::SelectionChanges::MsgType msgType = Gui::SelectionChanges::MsgType(type);
                if (msgType == Gui::SelectionChanges::AddSelection) {
                    Gui::Selection().addSelection(d->object->getDocument()->getName(),
                        d->object->getNameInDocument(), (const char*)name.toAscii());
                }
            }
        }
        catch (Standard_Failure) {
        }
    }
}

void DlgFilletEdges::onDeleteObject(const App::DocumentObject& obj)
{
    if (d->fillet == &obj) {
        d->fillet = 0;
    }
    else if (d->fillet && d->fillet->Base.getValue() == &obj) {
        d->fillet = 0;
        d->object = 0;
        ui->shapeObject->setCurrentIndex(0);
        on_shapeObject_activated(0);
    }
    else if (d->object == &obj) {
        d->object = 0;
        ui->shapeObject->removeItem(ui->shapeObject->currentIndex());
        ui->shapeObject->setCurrentIndex(0);
        on_shapeObject_activated(0);
    }
    else {
        QString shape = QString::fromAscii(obj.getNameInDocument());
        // start from the second item
        for (int i=1; i<ui->shapeObject->count(); i++) {
            if (ui->shapeObject->itemData(i).toString() == shape) {
                ui->shapeObject->removeItem(i);
                break;
            }
        }
    }
}

void DlgFilletEdges::onDeleteDocument(const App::Document& doc)
{
    if (d->object) {
        if (d->object->getDocument() == &doc) {
            ui->shapeObject->setCurrentIndex(0);
            on_shapeObject_activated(0);
            setEnabled(false);
        }
    }
    else if (App::GetApplication().getActiveDocument() == &doc) {
        ui->shapeObject->setCurrentIndex(0);
        on_shapeObject_activated(0);
        setEnabled(false);
    }
}

void DlgFilletEdges::toggleCheckState(const QModelIndex& index)
{
    if (!d->object)
        return;
    QVariant check = index.data(Qt::CheckStateRole);
    int id = index.data(Qt::UserRole).toInt();
    QString name = QString::fromAscii("Edge%1").arg(id);
    Qt::CheckState checkState = static_cast<Qt::CheckState>(check.toInt());

    bool block = this->blockConnection(false);

    // is item checked
    if (checkState & Qt::Checked) {
        App::Document* doc = d->object->getDocument();
        Gui::Selection().addSelection(doc->getName(),
            d->object->getNameInDocument(),
            (const char*)name.toAscii());
    }
    else {
        App::Document* doc = d->object->getDocument();
        Gui::Selection().rmvSelection(doc->getName(),
            d->object->getNameInDocument(),
            (const char*)name.toAscii());
    }

    this->blockConnection(block);
}

void DlgFilletEdges::findShapes()
{
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
    if (!activeDoc) return;

    std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType
        (Part::Feature::getClassTypeId());
    int index = 1;
    int current_index = 0;
    for (std::vector<App::DocumentObject*>::iterator it = objs.begin(); it!=objs.end(); ++it, ++index) {
        ui->shapeObject->addItem(QString::fromUtf8((*it)->Label.getValue()));
        ui->shapeObject->setItemData(index, QString::fromAscii((*it)->getNameInDocument()));
        if (current_index == 0) {
            if (Gui::Selection().isSelected(*it)) {
                current_index = index;
            }
        }
    }

    // if only one object is in the document then simply use that
    if (objs.size() == 1)
        current_index = 1;

    if (current_index > 0) {
        ui->shapeObject->setCurrentIndex(current_index);
        on_shapeObject_activated(current_index);
    }

    // if an existing fillet object is given start the edit mode
    if (d->fillet) {
        setupFillet(objs);
    }
}

void DlgFilletEdges::setupFillet(const std::vector<App::DocumentObject*>& objs)
{
    App::DocumentObject* base = d->fillet->Base.getValue();
    const std::vector<Part::FilletElement>& e = d->fillet->Edges.getValues();
    std::vector<App::DocumentObject*>::const_iterator it = std::find(objs.begin(), objs.end(), base);
    if (it != objs.end()) {
        // toggle visibility
        Gui::ViewProvider* vp;
        vp = Gui::Application::Instance->getViewProvider(d->fillet);
        if (vp) vp->hide();
        vp = Gui::Application::Instance->getViewProvider(base);
        if (vp) vp->show();

        int current_index = (it - objs.begin()) + 1;
        ui->shapeObject->setCurrentIndex(current_index);
        on_shapeObject_activated(current_index);
        ui->shapeObject->setEnabled(false);
        QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->treeView->model());
        for (std::vector<Part::FilletElement>::const_iterator et = e.begin(); et != e.end(); ++et) {
            std::vector<int>::iterator it = std::find(d->edge_ids.begin(), d->edge_ids.end(), et->edgeid);
            if (it != d->edge_ids.end()) {
                int index = it - d->edge_ids.begin();
                model->setData(model->index(index, 0), Qt::Checked, Qt::CheckStateRole);
                model->setData(model->index(index, 1), QVariant(QLocale::system().toString(et->radius1,'f',2)));
                model->setData(model->index(index, 2), QVariant(QLocale::system().toString(et->radius2,'f',2)));
            }
        }
    }
}

/**
 * Sets the strings of the subwidgets using the current language.
 */
void DlgFilletEdges::changeEvent(QEvent *e)
{
    if (e->type() == QEvent::LanguageChange) {
        int index = ui->shapeObject->currentIndex();
        // only get the items from index 1 on since the first one will be added automatically
        int count = ui->shapeObject->count() - 1;
        QStringList text;
        QList<QVariant> data;
        for (int i=0; i<count; i++) {
            text << ui->shapeObject->itemText(i+1);
            data << ui->shapeObject->itemData(i+1);
        }

        ui->retranslateUi(this);
        for (int i=0; i<count; i++) {
            ui->shapeObject->addItem(text.at(i));
            ui->shapeObject->setItemData(i+1, data.at(i));
        }

        ui->shapeObject->setCurrentIndex(index);
        QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->treeView->model());
        count = model->rowCount();
        for (int i=0; i<count; i++) {
            int id = model->data(model->index(i, 0), Qt::UserRole).toInt();
            model->setData(model->index(i, 0), QVariant(tr("Edge%1").arg(id)));
        }
    }
    else {
        QWidget::changeEvent(e);
    }
}

void DlgFilletEdges::on_shapeObject_activated(int index)
{
    d->object = 0;
    QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->treeView->model());
    model->removeRows(0, model->rowCount());

    QByteArray name = ui->shapeObject->itemData(index).toByteArray();
    App::Document* doc = App::GetApplication().getActiveDocument();
    if (!doc)
        return;
    App::DocumentObject* part = doc->getObject((const char*)name);
    if (part && part->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
        d->object = part;
        TopoDS_Shape myShape = static_cast<Part::Feature*>(part)->Shape.getValue();

        d->all_edges.Clear();
        TopExp::MapShapes(myShape, TopAbs_EDGE, d->all_edges);

        d->all_faces.Clear();
        TopExp::MapShapes(myShape, TopAbs_FACE, d->all_faces);

        // build up map edge->face
        TopTools_IndexedDataMapOfShapeListOfShape edge2Face;
        TopExp::MapShapesAndAncestors(myShape, TopAbs_EDGE, TopAbs_FACE, edge2Face);
        TopTools_IndexedMapOfShape mapOfShape;
        TopExp::MapShapes(myShape, TopAbs_EDGE, mapOfShape);

        // populate the model
        d->edge_ids.clear();
        for (int i=1; i<= edge2Face.Extent(); ++i) {
            // set the index value as user data to use it in accept()
            const TopTools_ListOfShape& los = edge2Face.FindFromIndex(i);
            if (los.Extent() == 2) {
                // set the index value as user data to use it in accept()
                const TopoDS_Shape& edge = edge2Face.FindKey(i);
                // Now check also the continuity to only allow C0-continious
                // faces
                const TopoDS_Shape& face1 = los.First();
                const TopoDS_Shape& face2 = los.Last();
                GeomAbs_Shape cont = BRep_Tool::Continuity(TopoDS::Edge(edge),
                                                           TopoDS::Face(face1),
                                                           TopoDS::Face(face2));
                if (cont == GeomAbs_C0) {
                    int id = mapOfShape.FindIndex(edge);
                    d->edge_ids.push_back(id);
                }
            }
        }

        model->insertRows(0, d->edge_ids.size());
        int index = 0;
        for (std::vector<int>::iterator it = d->edge_ids.begin(); it != d->edge_ids.end(); ++it) {
            model->setData(model->index(index, 0), QVariant(tr("Edge%1").arg(*it)));
            model->setData(model->index(index, 0), QVariant(*it), Qt::UserRole);
            model->setData(model->index(index, 1), QVariant(QLocale::system().toString(1.0,'f',2)));
            model->setData(model->index(index, 2), QVariant(QLocale::system().toString(1.0,'f',2)));
            std::stringstream element;
            element << "Edge" << *it;
            if (Gui::Selection().isSelected(part, element.str().c_str()))
                model->setData(model->index(index, 0), Qt::Checked, Qt::CheckStateRole);
            else
                model->setData(model->index(index, 0), Qt::Unchecked, Qt::CheckStateRole);
            index++;
        }
    }
}

void DlgFilletEdges::on_selectEdges_toggled(bool on)
{
    if (on) d->selection->selectEdges();
}

void DlgFilletEdges::on_selectFaces_toggled(bool on)
{
    if (on) d->selection->selectFaces();
}

void DlgFilletEdges::on_selectAllButton_clicked()
{
    QAbstractItemModel* model = ui->treeView->model();
    for (int i=0; i<model->rowCount(); ++i) {
        Qt::CheckState checkState = Qt::Checked;
        QVariant value(static_cast<int>(checkState));
        model->setData(model->index(i,0), value, Qt::CheckStateRole);
    }
}

void DlgFilletEdges::on_selectNoneButton_clicked()
{
    QAbstractItemModel* model = ui->treeView->model();
    for (int i=0; i<model->rowCount(); ++i) {
        Qt::CheckState checkState = Qt::Unchecked;
        QVariant value(static_cast<int>(checkState));
        model->setData(model->index(i,0), value, Qt::CheckStateRole);
    }
}

void DlgFilletEdges::on_filletType_activated(int index)
{
    QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->treeView->model());
    if (index == 0) {
        if (d->filletType == DlgFilletEdges::CHAMFER)
            model->setHeaderData(1, Qt::Horizontal, tr("Length"), Qt::DisplayRole);
        else
            model->setHeaderData(1, Qt::Horizontal, tr("Radius"), Qt::DisplayRole);
        ui->treeView->hideColumn(2);
        ui->filletEndRadius->hide();
    }
    else {
        if (d->filletType == DlgFilletEdges::CHAMFER)
            model->setHeaderData(1, Qt::Horizontal, tr("Start length"), Qt::DisplayRole);
        else
            model->setHeaderData(1, Qt::Horizontal, tr("Start radius"), Qt::DisplayRole);
        ui->treeView->showColumn(2);
        ui->filletEndRadius->show();
    }

    ui->treeView->resizeColumnToContents(0);
    ui->treeView->resizeColumnToContents(1);
    ui->treeView->resizeColumnToContents(2);
}

void DlgFilletEdges::on_filletStartRadius_valueChanged(double radius)
{
    QAbstractItemModel* model = ui->treeView->model();
    QString text = QLocale::system().toString(radius,'f',2);
    for (int i=0; i<model->rowCount(); ++i) {
        QVariant value = model->index(i,0).data(Qt::CheckStateRole);
        Qt::CheckState checkState = static_cast<Qt::CheckState>(value.toInt());

        // is item checked
        if (checkState & Qt::Checked) {
            model->setData(model->index(i, 1), QVariant(text));
        }
    }
}

void DlgFilletEdges::on_filletEndRadius_valueChanged(double radius)
{
    QAbstractItemModel* model = ui->treeView->model();
    QString text = QLocale::system().toString(radius,'f',2);
    for (int i=0; i<model->rowCount(); ++i) {
        QVariant value = model->index(i,0).data(Qt::CheckStateRole);
        Qt::CheckState checkState = static_cast<Qt::CheckState>(value.toInt());

        // is item checked
        if (checkState & Qt::Checked) {
            model->setData(model->index(i, 2), QVariant(text));
        }
    }
}

const char* DlgFilletEdges::getFilletType() const
{
    return "Fillet";
}

bool DlgFilletEdges::accept()
{
    if (!d->object) {
        QMessageBox::warning(this, tr("No shape selected"),
            tr("No valid shape is selected.\n"
               "Please select a valid shape in the drop-down box first."));
        return false;
    }
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
    QAbstractItemModel* model = ui->treeView->model();
    bool end_radius = !ui->treeView->isColumnHidden(2);
    bool todo = false;

    QString shape, type, name;
    std::string fillet = getFilletType();
    int index = ui->shapeObject->currentIndex();
    shape = ui->shapeObject->itemData(index).toString();
    type = QString::fromAscii("Part::%1").arg(QString::fromAscii(fillet.c_str()));

    if (d->fillet)
        name = QString::fromAscii(d->fillet->getNameInDocument());
    else
        name = QString::fromAscii(activeDoc->getUniqueObjectName(fillet.c_str()).c_str());

    activeDoc->openTransaction(fillet.c_str());
    QString code;
    if (!d->fillet) {
        code = QString::fromAscii(
        "FreeCAD.ActiveDocument.addObject(\"%1\",\"%2\")\n"
        "FreeCAD.ActiveDocument.%2.Base = FreeCAD.ActiveDocument.%3\n")
        .arg(type).arg(name).arg(shape);
    }
    code += QString::fromAscii("__fillets__ = []\n");
    for (int i=0; i<model->rowCount(); ++i) {
        QVariant value = model->index(i,0).data(Qt::CheckStateRole);
        Qt::CheckState checkState = static_cast<Qt::CheckState>(value.toInt());

        // is item checked
        if (checkState & Qt::Checked) {
            // the index value of the edge
            int id = model->index(i,0).data(Qt::UserRole).toInt();
            double r1 = model->index(i,1).data().toDouble();
            double r2 = r1;
            if (end_radius)
                r2 = model->index(i,2).data().toDouble();
            code += QString::fromAscii(
                "__fillets__.append((%1,%2,%3))\n")
                .arg(id).arg(r1,0,'f',2).arg(r2,0,'f',2);
            todo = true;
        }
    }

    if (!todo) {
        QMessageBox::warning(this, tr("No edge selected"),
            tr("No edge entity is checked to fillet.\n"
               "Please check one or more edge entities first."));
        return false;
    }

    Gui::WaitCursor wc;
    code += QString::fromAscii(
        "FreeCAD.ActiveDocument.%1.Edges = __fillets__\n"
        "del __fillets__\n"
        "FreeCADGui.ActiveDocument.%2.Visibility = False\n")
        .arg(name).arg(shape);
    Gui::Application::Instance->runPythonCode((const char*)code.toAscii());
    activeDoc->commitTransaction();
    activeDoc->recompute();
    if (d->fillet) {
        Gui::ViewProvider* vp;
        vp = Gui::Application::Instance->getViewProvider(d->fillet);
        if (vp) vp->show();
    }

    QByteArray to = name.toAscii();
    QByteArray from = shape.toAscii();
    Gui::Command::copyVisual(to, "LineColor", from);
    Gui::Command::copyVisual(to, "PointColor", from);
    return true;
}

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

FilletEdgesDialog::FilletEdgesDialog(DlgFilletEdges::FilletType type, Part::FilletBase* fillet, QWidget* parent, Qt::WFlags fl)
  : QDialog(parent, fl)
{
    widget = new DlgFilletEdges(type, fillet, this);
    this->setWindowTitle(widget->windowTitle());

    QVBoxLayout* hboxLayout = new QVBoxLayout(this);
    QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

    QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    hboxLayout->addWidget(widget);
    hboxLayout->addWidget(buttonBox);
}

FilletEdgesDialog::~FilletEdgesDialog()
{
}

void FilletEdgesDialog::accept()
{
    if (widget->accept())
        QDialog::accept();
}

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

TaskFilletEdges::TaskFilletEdges(Part::Fillet* fillet)
{
    widget = new DlgFilletEdges(DlgFilletEdges::FILLET, fillet);
    taskbox = new Gui::TaskView::TaskBox(
        Gui::BitmapFactory().pixmap("Part_Fillet"),
        widget->windowTitle(), true, 0);
    taskbox->groupLayout()->addWidget(widget);
    Content.push_back(taskbox);
}

TaskFilletEdges::~TaskFilletEdges()
{
    // automatically deleted in the sub-class
}

void TaskFilletEdges::open()
{
}

void TaskFilletEdges::clicked(int)
{
}

bool TaskFilletEdges::accept()
{
    bool ok = widget->accept();
    if (ok)
        Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()");
    return ok;
}

bool TaskFilletEdges::reject()
{
    Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()");
    return true;
}

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

/* TRANSLATOR PartGui::DlgChamferEdges */

DlgChamferEdges::DlgChamferEdges(Part::FilletBase* chamfer, QWidget* parent, Qt::WFlags fl)
  : DlgFilletEdges(DlgFilletEdges::CHAMFER, chamfer, parent, fl)
{
    this->setWindowTitle(tr("Chamfer Edges"));
}

/*  
 *  Destroys the object and frees any allocated resources
 */
DlgChamferEdges::~DlgChamferEdges()
{
}

const char* DlgChamferEdges::getFilletType() const
{
    return "Chamfer";
}

TaskChamferEdges::TaskChamferEdges(Part::Chamfer* chamfer)
{
    widget = new DlgChamferEdges(chamfer);
    taskbox = new Gui::TaskView::TaskBox(
        Gui::BitmapFactory().pixmap("Part_Chamfer"),
        widget->windowTitle(), true, 0);
    taskbox->groupLayout()->addWidget(widget);
    Content.push_back(taskbox);
}

TaskChamferEdges::~TaskChamferEdges()
{
    // automatically deleted in the sub-class
}

void TaskChamferEdges::open()
{
}

void TaskChamferEdges::clicked(int)
{
}

bool TaskChamferEdges::accept()
{
    bool ok = widget->accept();
    if (ok)
        Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()");
    return ok;
}

bool TaskChamferEdges::reject()
{
    Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()");
    return true;
}

#include "moc_DlgFilletEdges.cpp"
