/***************************************************************************
 *   Copyright (c) Konstantinos Poulios      (logari81@gmail.com) 2011     *
 *                                                                         *
 *   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                                *
 *                                                                         *
 ***************************************************************************/

#ifndef PLANEGCS_GEO_H
#define PLANEGCS_GEO_H

#include <cmath>
#include "Util.h"

namespace GCS
{
    class Point
    {
    public:
        Point(){x = 0; y = 0;}
        double *x;
        double *y;
    };

    ///Class DeriVector2 holds a vector value and its derivative on the
    ///parameter that the derivatives are being calculated for now. x,y is the
    ///actual vector (v). dx,dy is a derivative of the vector by a parameter
    ///(dv/dp). The easiest way to fill the vector in is by passing a point and
    ///a derivative parameter pointer to its constructor. x,y are read from the
    ///pointers in Point, and dx,dy are set to either 0 or 1 depending on what
    ///pointers of Point match the supplied pointer. The derivatives can be set
    ///manually as well. The class also provides a bunch of methods to do math
    ///on it (and derivatives are calculated implicitly).
    ///
    class DeriVector2
    {
    public:
        DeriVector2(){x=0; y=0; dx=0; dy=0;}
        DeriVector2(double x, double y) {this->x = x; this->y = y; this->dx = 0; this->dy = 0;}
        DeriVector2(double x, double y, double dx, double dy) {this->x = x; this->y = y; this->dx = dx; this->dy = dy;}
        DeriVector2(const Point &p, double* derivparam);
        double x, dx;
        double y, dy;

        double length() const {return sqrt(x*x + y*y);}
        double length(double &dlength) const; //returns length and writes length deriv into the dlength argument.


        //unlike other vectors in FreeCAD, this normalization creates a new vector instead of modifying existing one.
        DeriVector2 getNormalized() const; //returns zero vector if the original is zero.
        double scalarProd(const DeriVector2 &v2, double* dprd=0) const;//calculates scalar product of two vectors and returns the result. The derivative of the result is written into argument dprd.
        DeriVector2 sum(const DeriVector2 &v2) const {//adds two vectors and returns result
            return DeriVector2(x + v2.x, y + v2.y,
                               dx + v2.dx, dy + v2.dy);}
        DeriVector2 subtr(const DeriVector2 &v2) const {//subtracts two vectors and returns result
            return DeriVector2(x - v2.x, y - v2.y,
                               dx - v2.dx, dy - v2.dy);}
        DeriVector2 mult(double val) const {
            return DeriVector2(x*val, y*val, dx*val, dy*val);}//multiplies the vector by a number. Derivatives are scaled.
        DeriVector2 multD(double val, double dval) const {//multiply vector by a variable with a derivative.
            return DeriVector2(x*val, y*val, dx*val+x*dval, dy*val+y*dval);}
        DeriVector2 divD(double val, double dval) const;//divide vector by a variable with a derivative
        DeriVector2 rotate90ccw() const {return DeriVector2(-y,x,-dy,dx);}
        DeriVector2 rotate90cw() const {return DeriVector2(y,-x,dy,-dx);}
        DeriVector2 linCombi(double m1, const DeriVector2 &v2, double m2) const {//linear combination of two vectors
            return DeriVector2(x*m1 + v2.x*m2, y*m1 + v2.y*m2,
                               dx*m1 + v2.dx*m2, dy*m1 + v2.dy*m2);}

    };

    ///////////////////////////////////////
    // Geometries
    ///////////////////////////////////////

    class Curve //a base class for all curve-based objects (line, circle/arc, ellipse/arc)
    {
    public:

        //returns normal vector. The vector should point to the left when one
        // walks along the curve from start to end. Ellipses and circles are
        // assumed to be walked counterclockwise, so the vector should point
        // into the shape.
        //derivparam is a pointer to a curve parameter (or point coordinate) to
        // compute the derivative for. The derivative is returned through dx,dy
        // fields of DeriVector2.
        virtual DeriVector2 CalculateNormal(Point &p, double* derivparam = 0) = 0;

        //adds curve's parameters to pvec (used by constraints)
        virtual int PushOwnParams(VEC_pD &pvec) = 0;
        //recunstruct curve's parameters reading them from pvec starting from index cnt.
        //cnt will be incremented by the same value as returned by PushOwnParams()
        virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt) = 0;
        virtual Curve* Copy() = 0; //DeepSOIC: I haven't found a way to simply copy a curve object provided pointer to a curve object.
    };

    class Line: public Curve
    {
    public:
        Line(){}
        Point p1;
        Point p2;
        DeriVector2 CalculateNormal(Point &p, double* derivparam = 0);
        virtual int PushOwnParams(VEC_pD &pvec);
        virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
        virtual Line* Copy();
    };

    class Circle: public Curve
    {
    public:
        Circle(){rad = 0;}
        Point center;
        double *rad;
        DeriVector2 CalculateNormal(Point &p, double* derivparam = 0);
        virtual int PushOwnParams(VEC_pD &pvec);
        virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
        virtual Circle* Copy();
    };

    class Arc: public Circle
    {
    public:
        Arc(){startAngle=0;endAngle=0;rad=0;}
        double *startAngle;
        double *endAngle;
        //double *rad; //inherited
        Point start;
        Point end;
        //Point center; //inherited
        virtual int PushOwnParams(VEC_pD &pvec);
        virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
        virtual Arc* Copy();
    };
    
    class Ellipse: public Curve
    {
    public:
        Ellipse(){ radmin = 0;}
        Point center; 
        Point focus1;
        double *radmin;
        double getRadMaj(const DeriVector2 &center, const DeriVector2 &f1, double b, double db, double &ret_dRadMaj);
        double getRadMaj(double* derivparam, double &ret_dRadMaj);
        double getRadMaj();
        DeriVector2 CalculateNormal(Point &p, double* derivparam = 0);
        virtual int PushOwnParams(VEC_pD &pvec);
        virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
        virtual Ellipse* Copy();
    };
    
    class ArcOfEllipse: public Ellipse
    {
    public:
        ArcOfEllipse(){startAngle=0;endAngle=0;radmin = 0;}
        double *startAngle;
        double *endAngle;
        //double *radmin; //inherited
        Point start;
        Point end;
        //Point center;  //inherited
        //double *focus1.x; //inherited
        //double *focus1.y; //inherited
        virtual int PushOwnParams(VEC_pD &pvec);
        virtual void ReconstructOnNewPvec (VEC_pD &pvec, int &cnt);
        virtual ArcOfEllipse* Copy();
    };

} //namespace GCS

#endif // PLANEGCS_GEO_H
