/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file gpl-2.0.txt included in the
** packaging of this file.
**
** This program 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
** This copyright notice MUST APPEAR in all copies of the script!
**
**********************************************************************/

#include "rs_actionpolylineequidistant.h"

#include <QAction>
#include "rs_dialogfactory.h"
#include "rs_graphicview.h"
#include "rs_polyline.h"
#include "rs_information.h"

RS_ActionPolylineEquidistant::RS_ActionPolylineEquidistant(RS_EntityContainer& container,
                RS_GraphicView& graphicView)
                :RS_PreviewActionInterface("Create Equidistant Polylines",
                                                   container, graphicView) {
        dist = 1.0;
        number = 1;
}


QAction* RS_ActionPolylineEquidistant::createGUIAction(RS2::ActionType /*type*/, QObject* /*parent*/) {
        QAction* action = new QAction(tr("Create &Equidistant Polylines"), NULL);
        action->setShortcut(QKeySequence());
        action->setIcon(QIcon(":/extui/polylineequidstant.png"));
        action->setStatusTip(tr("Create Equidistant Polylines"));
        return action;
}

void RS_ActionPolylineEquidistant::init(int status) {
        RS_PreviewActionInterface::init(status);
        originalEntity = NULL;
        targetPoint = RS_Vector(false);
        bRightSide = false;
}

/**
 * Helper function for makeContour
 * Modify newEntity to parellel of orgEntity at distance dist
 * If dist is positive the offset is in left else in right
 * Bot newEntity and orgEntity are the same type of entity
 * if not return NULL pointer
 *
 * @retval RS_Entity* of parellel entity
 *
 * @author Rallaz
 */
RS_Entity* RS_ActionPolylineEquidistant::calculateOffset(RS_Entity* newEntity,RS_Entity* orgEntity, double dist) {
    if (orgEntity->rtti()==RS2::EntityArc && newEntity->rtti()==RS2::EntityArc) {
        RS_Arc* arc = (RS_Arc*)newEntity;
        double r0 = ((RS_Arc*)orgEntity)->getRadius();
        double r;
        if ( ((RS_Arc*)orgEntity)->isReversed())
            r = r0 + dist;
        else
            r = r0 - dist;
        if(r < 0)
            return NULL;
        arc->setData(((RS_Arc*)orgEntity)->getData());
        arc->setRadius(r);
        arc->calculateEndpoints();
        return newEntity;
    } else if (orgEntity->rtti()==RS2::EntityLine && newEntity->rtti()==RS2::EntityLine) {
        RS_Line* line0 = (RS_Line*)orgEntity;
        RS_Line* line1 = (RS_Line*)newEntity;
        RS_Vector v0 = line0->getStartpoint();
        RS_Vector v1(v0.x,v0.y+dist);
        RS_Vector v2(v0.x+line0->getLength(),v0.y+dist);
        line1->setStartpoint(v1);
        line1->setEndpoint(v2);
        line1->rotate(v0, line0->getAngle1());
        return newEntity;
    }
    return NULL;
}

/**
 * Helper function for makeContour
 * Calculate the intersection point of first and last entities
 * The first vertex is not added and the last is returned instead of added
 *
 * @retval RS_Vector nearest to startpoint of last and endpoint of first or RS_Vector(false) if not
 *
 * @author Rallaz
 */
RS_Vector RS_ActionPolylineEquidistant::calculateIntersection(RS_Entity* first,RS_Entity* last) {
    RS_VectorSolutions vsol;
    RS_Vector v(false);
    vsol = RS_Information::getIntersection(first, last, false);
    if (vsol.getNumber()==0) {
        //Parallel entities
        return RS_Vector(false);
    } else if (vsol.getNumber()>1 &&
               vsol.get(0).distanceTo(last->getStartpoint()) > vsol.get(1).distanceTo(last->getStartpoint())) {
        return vsol.get(1);
    }
    return vsol.get(0);
}

bool RS_ActionPolylineEquidistant::makeContour() {
    if (container==NULL) {
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_ActionPolylineEquidistant::makeContour: no valid container");
        return false;
    }

    RS_Polyline* originalPolyline = (RS_Polyline*)originalEntity;
//create a list of entities to offset without length = 0
    QList<RS_Entity*> entities;
    for (RS_Entity* en=originalPolyline->firstEntity(); en!=NULL; en=originalPolyline->nextEntity()) {
        if (en->getLength() > 1.0e-12)
            entities.append(en);
    }
    if (entities.isEmpty()) {
        return false;
    }
    if (document!=NULL) {
        document->startUndoCycle();
    }
    double neg = 1.0;
    if(bRightSide)
        neg = -1.0;

    // Create new helper entities
    RS_Line line1(NULL, RS_LineData(RS_Vector(true), RS_Vector(true)));//current line
    RS_Line lineFirst(NULL, RS_LineData(RS_Vector(true), RS_Vector(true)));//previous line
    RS_Arc arc1(NULL, RS_ArcData(RS_Vector(true), 0,0,0,false));//current arc
    RS_Arc arcFirst(NULL, RS_ArcData(RS_Vector(true), 0,0,0,false));//previous arc

    for (int num=1; num<=number || (number==0 && num<=1); num++) {
        RS_Polyline* newPolyline = new RS_Polyline(container);
        newPolyline->setLayer(((RS_Polyline*)originalEntity)->getLayer());
        newPolyline->setPen(((RS_Polyline*)originalEntity)->getPen());

        bool first = true;
        bool closed = originalPolyline->isClosed();
        double bulge = 0.0;
        RS_Entity* en;
        RS_Entity* prevEntity = entities.last();
        RS_Entity* currEntity=NULL;
        for (int i = 0; i < entities.size(); ++i) {
            en = entities.at(i);
            RS_Vector v(false);
            if (en->rtti()==RS2::EntityArc) {
                currEntity = &arc1;
                calculateOffset(currEntity, en, dist*num*neg);
                bulge = arc1.getBulge();
            } else {
                currEntity = &line1;
                bulge = 0.0;
                calculateOffset(currEntity, en, dist*num*neg);
            }
            if (first) {
                if (closed){
                    if (prevEntity->rtti()==RS2::EntityArc) {
                        prevEntity = calculateOffset(&arcFirst, prevEntity, dist*num*neg);
                    } else {
                        prevEntity = calculateOffset(&lineFirst, prevEntity, dist*num*neg);
                    }
                    v = calculateIntersection(prevEntity, currEntity);
                }
                if (!v.valid) {
                    v = currEntity->getStartpoint();
                    closed = false;
                } else if (currEntity->rtti()==RS2::EntityArc) {
                    //update bulge
                    arc1.setAngle1(arc1.getCenter().angleTo(v));
                    arc1.calculateEndpoints();
                    bulge = arc1.getBulge();
                }
                first = false;
                if (!prevEntity) break; //prevent crash if not exist offset for prevEntity
            }else{
                v = calculateIntersection(prevEntity, currEntity);
                if (!v.valid) {
                    v= prevEntity->getEndpoint();
                    double dess = currEntity->getStartpoint().distanceTo(prevEntity->getEndpoint());
                    if (dess > 1.0e-12) {
                        newPolyline->addVertex(v, bulge);
                        prevEntity = NULL;
                        break;
                    }
                }
                double startAngle = prevEntity->getStartpoint().angleTo(prevEntity->getEndpoint());
                if (prevEntity->rtti()==RS2::EntityArc) {
                    arcFirst.setAngle2(arcFirst.getCenter().angleTo(v));
                    arcFirst.calculateEndpoints();
                     newPolyline->setNextBulge(arcFirst.getBulge());
                }
                //check if the entity are reverted
                if (abs (prevEntity->getStartpoint().angleTo(prevEntity->getEndpoint())- startAngle) > 0.785){
                    prevEntity = newPolyline->lastEntity();
                    RS_Vector v0 = calculateIntersection(prevEntity, currEntity);
                    if (prevEntity->rtti()==RS2::EntityArc) {
                        ((RS_Arc*)prevEntity)->setAngle2(arcFirst.getCenter().angleTo(v0));
                        ((RS_Arc*)prevEntity)->calculateEndpoints();
                        newPolyline->setNextBulge( ((RS_Arc*)prevEntity)->getBulge() );
                    } else {
                        ((RS_Line*)prevEntity)->setEndpoint(v0);
                        newPolyline->setNextBulge( 0.0 );
                    }
                    newPolyline->setEndpoint(v0);
                }
                if (currEntity->rtti()==RS2::EntityArc) {
                    arc1.setAngle1(arc1.getCenter().angleTo(v));
                    arc1.calculateEndpoints();
                    bulge = arc1.getBulge();
                } else
                    bulge = 0.0;
            }
            if (prevEntity) {
                newPolyline->addVertex(v, bulge, false);
                if (currEntity->rtti()==RS2::EntityArc){
                    arcFirst.setData(arc1.getData());
                    arcFirst.calculateEndpoints();
                    prevEntity = &arcFirst;
                } else {
                    lineFirst.setStartpoint(line1.getStartpoint());
                    lineFirst.setEndpoint(line1.getEndpoint());
                    prevEntity = &lineFirst;
                }
            }
        }
        //properly terminated, check closed
        if (prevEntity) {
            if (closed){
                if (currEntity->rtti()==RS2::EntityArc) {
                    arc1.setAngle2(arc1.getCenter().angleTo(newPolyline->getStartpoint()));
                    arc1.calculateEndpoints();
                    newPolyline->setNextBulge(arc1.getBulge());
                    bulge = arc1.getBulge();
                }
                newPolyline->setClosed(true, bulge);
            } else {
                newPolyline->addVertex(currEntity->getEndpoint(), bulge);
            }
        }
        if (!newPolyline->isEmpty()) {
            container->addEntity(newPolyline);
            document->addUndoable(newPolyline);
        }
    }
    if (document!=NULL) {
        document->endUndoCycle();
    }

    if (graphicView!=NULL) {
        graphicView->redraw();
    }

    return true;
}

void RS_ActionPolylineEquidistant::trigger() {

        RS_DEBUG->print("RS_ActionPolylineEquidistant::trigger()");

        if (originalEntity!=NULL && targetPoint.valid ) {

                originalEntity->setHighlighted(false);
                graphicView->drawEntity(originalEntity);

                makeContour();

                originalEntity = NULL;
                targetPoint = RS_Vector(false);
                bRightSide = false;
                setStatus(ChooseEntity);

                RS_DIALOGFACTORY->updateSelectionWidget(container->countSelected(),container->totalSelectedLength());
        }
////////////////////////////////////////2006/06/15
                graphicView->redraw();
////////////////////////////////////////
}



//void RS_ActionPolylineEquidistant::mouseMoveEvent(QMouseEvent* e) {
//        RS_DEBUG->print("RS_ActionPolylineEquidistant::mouseMoveEvent begin");

//        switch (getStatus()) {
//        case ChooseEntity:
////                snapPoint(e);
//                break;
//        default:
//                break;
//        }

//        RS_DEBUG->print("RS_ActionPolylineEquidistant::mouseMoveEvent end");
//}



void RS_ActionPolylineEquidistant::mouseReleaseEvent(QMouseEvent* e) {
        if (e->button()==Qt::LeftButton) {
                switch (getStatus()) {
                case ChooseEntity:
                        originalEntity = catchEntity(e);
                        if (originalEntity==NULL) {
                                RS_DIALOGFACTORY->commandMessage(tr("No Entity found."));
                        } else if (originalEntity->rtti()!=RS2::EntityPolyline) {

                                RS_DIALOGFACTORY->commandMessage(
                                        tr("Entity must be a polyline."));
                        } else {
                                targetPoint = snapFree(e);
                                originalEntity->setHighlighted(true);
                                graphicView->drawEntity(originalEntity);
                                double d = graphicView->toGraphDX(snapRange)*0.9;
                                RS_Entity* Segment =  ((RS_Polyline*)originalEntity)->getNearestEntity( targetPoint, &d, RS2::ResolveNone);
                                if (Segment->rtti() == RS2::EntityLine) {
                                double ang = ((RS_Line*)Segment)->getAngle1();
                                double ang1 = ((RS_Line*)Segment)->getStartpoint().angleTo(RS_Vector(targetPoint));
                                if( ang > ang1 || ang + M_PI < ang1 )
                                        bRightSide = true;
                                } else {
                                    RS_Vector cen = ((RS_Arc*)Segment)->getCenter();
                                    if (cen.distanceTo(targetPoint) > ((RS_Arc*)Segment)->getRadius() && ((RS_Arc*)Segment)->getBulge() > 0 )
                                        bRightSide = true;
                                }
////////////////////////////////////////2006/06/15
                graphicView->redraw();
////////////////////////////////////////
                                trigger();
                        }
                        break;
                default:
                        break;
                }
        } else if (e->button()==Qt::RightButton) {
                deleteSnapper();
                if (originalEntity!=NULL) {
                        originalEntity->setHighlighted(false);
                        graphicView->drawEntity(originalEntity);
////////////////////////////////////////2006/06/15
                graphicView->redraw();
////////////////////////////////////////
                }
                init(getStatus()-1);
        }
}

void RS_ActionPolylineEquidistant::showOptions() {
        RS_ActionInterface::showOptions();

        RS_DIALOGFACTORY->requestOptions(this, true);
}



void RS_ActionPolylineEquidistant::hideOptions() {
        RS_ActionInterface::hideOptions();

        RS_DIALOGFACTORY->requestOptions(this, false);
}

void RS_ActionPolylineEquidistant::updateMouseCursor() {
        graphicView->setMouseCursor(RS2::CadCursor);
}

void RS_ActionPolylineEquidistant::updateMouseButtonHints() {
        switch (getStatus()) {
        case ChooseEntity:
                RS_DIALOGFACTORY->updateMouseWidget(tr("Choose the original polyline"),
                                                                                        tr("Cancel"));
                break;
        default:
                RS_DIALOGFACTORY->updateMouseWidget("", "");
                break;
        }
}

//void RS_ActionPolylineEquidistant::updateToolBar() {
//    if (RS_DIALOGFACTORY!=NULL) {
//        if (isFinished()) {
//            RS_DIALOGFACTORY->resetToolBar();
//        }
//    }
//}
// EOF
