/*
* This file is part of project OpenEmbroidery. It's copyrighted by
* the contributors recorded in the version control history of the file.
* Original project location https://code.electrolab.fr/openEmbroidery/openEmbroidery_software
*
* SPDX-License-Identifier: CECILL-2.1
* License-Filename: Licence_CeCILL_V2.1-en.txt
*/

#pragma once

#include "OE_base.h"
#include "OE_utils.h"
#include <stdint.h>
#include <vector>
#include "OE_fstream.h"

namespace Pakal { class Archive; }

class OE_curve : public OE_base
{
	public:

		/** \brief is displaying controls. */
		bool controls;

		/** \brief the curve array of discretized points */
		std::vector<vector_2dt> discPts;

		/** \brief the bezier curve array of points */
		std::vector<vector_2d> pts;

		/** Default constructor */
		OE_curve();
		/** Default destructor */
		virtual ~OE_curve();

		void persist(Pakal::Archive* archive);

		//virtual bool getNeedRefresh();
		//virtual bool setNeedRefresh(bool value);

		/** \brief return the number of points in the curve (control points, without handles) */
		int getNpts();

		/** \brief return true if the curve is set as closed. */
		bool getClosed();

		/** \brief return the length of the curve. */
		float getLength(float maxDist);

		/** \brief set the curve closed attribute. */
		virtual void setClosed(bool closed);

		/** \brief calculate the curve bounding box
		 */
		virtual BoundingBox getBound();

		/** \brief move the curve
		 */
		virtual void move(vector_2d /*offset*/){}

		/** \brief scale the curve
		 */
		virtual void scale(vector_2d /*ratio*/, vector_2d /*pos*/){}

		/** \brief return a segment of the original curve
		 * \param start, end -> the position of the start and end point of the curve segment
		 * integral value is the segment number and float value the position on this segment (ex:2.3 mean pos 0.3 on segment 2)
		 * \param rev invert the path
		 * \return return an array of point, the curve segment
		 */
		std::vector<vector_2d> subCurve(float start, float end, bool rev);

		/** \brief discretise a curve using a level of detail approach
		 * \param maxDist is a maximum error distance beetween the discretised curve and the real one
		 * \return return an array of point, the curve discretised
		 */
		std::vector<vector_2dt> discretizeFast(float maxDist);

		/** \brief discretise a curve using uniform segment lengths
		 * \param dist is the length of a segment along the curve
		 * \return return an array of point, the curve discretised
		 */
		std::vector<vector_2dt> discretizeRegular(float dist);

		/** \brief reverse the curve. */
		void reverse();

		/** \brief check if the curve is valid. */
		virtual bool check();

		//virtual void refresh()=0;

		/** \brief refresh the discPts array. */
		virtual void refresh(float dpi, bool force=false);

		virtual void refreshDependency(){}

		vector_2dt closestPoint(vector_2d pt);

		void transform(const OE_Matrix& m);

	protected:
		/** \brief Flag indicating if shapes should be treated as closed. */
		bool closed;

		/** \brief Tight bounding box of the shape [minx,miny,maxx,maxy]. */
		//float bounds[4];

		/** \brief color of the curve */
		static  unsigned char lineColor[4];

		/** \brief calculate the distance beetween a point and a segment
		 * \param pt -> the point to test
		 * \param seg1 and seg2 -> the extremity of the segment
		 * \return return the distance
		 */
		static float distPtSeg(vector_2d pt, vector_2d seg1, vector_2d seg2);

		static vector_2d posPtSeg(vector_2d pt, vector_2d seg1, vector_2d seg2);

		/** \brief discretise a curve segment to display or treating it
		 * \param pt1 to pt4 -> the bezier points of the curve segment
		 * \param the maximum distance beetween a discretised point and the real curve
		 * \param a value that represent the deep of the discretisation (level of subdivision)
		 * \return return an array of point, the segment discretised
		 */
		std::vector<vector_2dt> discretizeCubicBez(vector_2dt pt1, vector_2dt pt2, vector_2dt pt3, vector_2dt pt4, float tol, int level);

		/** \brief check if a point is in a given bounding box
		 * \param pt -> the point
		 * \param bounds -> an array representing the bounding box
		 * \return true if the point is inside the bounding box
		 */
		static bool ptInBounds(vector_2d pt, float* bounds);

		/** \brief get the position of a 1D point on a bezier segment
		 * \param t -> the value of the position along the segment
		 * \param p0 to p3 -> the 1D points values
		 * \return return the position of the 1D point
		 */
		static double evalBezier(double t, double p0, double p1, double p2, double p3);

		/** \brief return a segment of the original curve
		 * \param pt1 to pt4 -> the bezier points of the curve segment
		 * \param t -> the position of the point along the segments
		 * \return return an array of 3 point (handle,point,handle)
		 */
		std::vector<vector_2d> interPoint(vector_2d pt1, vector_2d pt2, vector_2d pt3, vector_2d pt4, float t);

		/** \brief calculate a segment bounding box
		 * \param bounds -> a pointer to a bounding box array (float[4])
		 * \param curve -> the curve to be estimated
		 */
		static BoundingBox segmentBounds(vector_2d* segment);

		vector_2dt closestT(vector_2d pt, double t1 , double t2);
		vector_2dt closestT(vector_2dt pt1, vector_2dt pt2, vector_2dt pt3, vector_2dt pt4, float limit, vector_2d targetPt, int level);
};

