//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
//

#ifndef __BEZIER_HPP__
#define __BEZIER_HPP__

#include <Parms.hpp>
#include <Geometry.hpp> 

namespace mgx 
{
  class mgx_EXPORT Bezier 
  {
  private:
    Point2u _bezPoints; // number of bezier control points in each direction
    Point2d _bezSize; // size of Bezier in each direction
    Point2u _bezLines; // number of lines for drawing Bezier, in each direction
    std::vector<Point3d> _bezierV; // position of control points
    std::vector<uint> _binomV; // binomial coefficients to evaluate Bezier

  public:
    Bezier() : _bezPoints(5 ,5), _bezSize(20 ,20), _bezierV(0), _binomV(0) {}
    ~Bezier() {}
    
    Point2u bezPoints() const {
      return _bezPoints;
    }
    void setBezPoints(Point2u n)
    {
      if(n != _bezPoints) {
        _bezPoints = n;
        // re-populate the vectors for binomial and control points
        initBez();
//        hasChanged();
      }
    }

    Point2d bezSize() const {
      return _bezSize;
    }
    void setBezSize(Point2d n) {
        _bezSize = n;
    }

    Point2u bezLines() const {
      return _bezLines;
    }
    void setBezLines(Point2u n) {
      if(n.x() < 2)
        n.x() = 2;
      if(n.y() < 2)
        n.y() = 2;
      _bezLines = n;
    }

    void setBezierV(const std::vector<Point3d>& points)
    {
      if(points.size() == _bezierV.size()) {
        _bezierV = points;
//        hasChanged();
      }
    }
    const std::vector<Point3d>& bezierV() const {
      return _bezierV;
    }
    std::vector<Point3d>& bezierV() {
      return _bezierV;
    }
  
    Point3d& bezierV(uint u, uint v) {
      return _bezierV[idx(u, v)];
    }
    const Point3d& bezierV(uint u, uint v) const {
      return _bezierV[idx(u, v)];
    }  
    
    void resetBezier() {
      _bezPoints = Point2u(5,5);
      _bezSize = Point2d(20.0,20.0);
      _bezLines = Point2u(10,10);
      _bezierV.clear();
      _binomV.clear();
    }

    void readParms(Parms& parms, QString section);
    
    void loadBezier(QString filename);
    void saveBezier(QString filename);

    // Copy and/or scale
    void scale(float ScaleX, float ScaleY);

    // Create new interpolated (and scaled) surface
    void interpolate(Bezier &src1, Bezier &src2,
                     double scale1, double scale2, double s);
    // Initialize bezier
    void initBez(); 

    // Return x,y,z point from u,v parameters
    Point3d evalCoord(float u, float v) const; // ALR: const needed in CuttingSurface
    Point3d evalCoordTrim(double& u, double& v);

    Point3d evalCoordSpline(double u, double v);

    // Converts u,v into an array value (assuming u is the first coordinate)
    uint idx(uint u, uint v) const {
      return (v * _bezPoints.x() + u);
    }

    // returns a vector of points which represents a discretized line of the bezier grid along a fix v-coordinate
    void discretizeLine(int discPoints, double vPos, Matrix4d rotMat,
                        std::vector<Point3d>& discretizedBez, std::vector<Point3d>& differentialBez);

    // returns a vector of points which represents a discretized line of the bezier grid along a fix v-coordinate
    // the points of this line have approx equal spacial distribution
    void discretizeLineEqualWeight(int discPoints, double vPos, Matrix4d rotMat, 
      std::vector<Point3d>& discretizedBez, std::vector<Point3d>& differentialBez, double& totalLength);

    // returns an 2D array of points which represents a discretized grid of the bezier
    void discretizeGrid(int discPoints, Matrix4d rotMat, 
      std::vector<std::vector<Point3d> >& discretizedBez);
  };
}

#endif
