/***************************************************************************
 *   Copyright (C) 2004 by Predrag Viceic                                  *
 *   viceic@net2000.ch                                             *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef MYMATH_H
#define MYMATH_H

#include <qobject.h>
#include <math.h>
#include <qpoint.h>
#include <splinepoint.h>
#include <sortablepoint.h>

/**
\class MyMath

\brief Some usefull functions

@author Predrag Viceic
*/
class MyMath : public QObject
{
Q_OBJECT
public:
    MyMath(QObject *parent = 0, const char *name = 0);

    ~MyMath();
    struct SPoint2D{
        long rx;
        float ry;
        long x(){
            return rx;
        }
        float y(){
            return ry;
        }
        void setX(long x){
            rx=x;
        }
        void setY(float y){
            ry=y;
        }
    };
    struct FPoint2D{
        float rx;
        float ry;
        float x(){
            return rx;
        }
        float y(){
            return ry;
        }
        void setX(float x){
            rx=x;
        }
        void setY(float y){
            ry=y;
        }
    };

    template <class T, class P> struct GenericPoint2D{
        T rx;
        P ry;
        T x(){
            return rx;
        }
        P y(){
            return ry;
        }
        void setX(T x){
            rx=x;
        }
        void setY(P y){
            ry=y;
        }
    };

    struct smartRound{
        int count;
        int max;
        float val;
        int roundratio;
        int roundedval;
        bool alreadyrounded;
        int returnval;
        void init(float fval,int _max){
            count=0;
            max=_max;
            val=fval;
            roundedval=0;
            returnval=0;
            if (ceil(fval)==floor(fval)) alreadyrounded=TRUE;
            else alreadyrounded=FALSE;
            if(fval<1){
                roundratio=int(fval*float(max));
            }else{
                roundratio=int(fval*float(max)) % int(floor(fval)*max);
            }
        }
        int round(){
            if(alreadyrounded) returnval=val;
            else{
                if(roundratio==max-roundratio){
                    if(count%2==0) returnval=ceil(val);
                    else returnval=floor(val);
                }else if(roundratio>max-roundratio){
                    int everyNth=2;
                    if (roundratio%(max-roundratio)==0){
                        everyNth=int(max/(abs(roundratio%(max-roundratio))+1));
                    }else{
                        everyNth=int((max/abs(roundratio%(max-roundratio))));
                    }
                    everyNth=everyNth%2==0?everyNth+1:everyNth;
                    if(count%2==0 || count%everyNth==0)
                        returnval=ceil(val);
                    else returnval=floor(val);
                }else{
                    int everyNth=2;
                    if ((max-roundratio)%roundratio==0){
                        everyNth=int(max/(abs((max-roundratio)%roundratio)+1));
                    }else{
                        everyNth=int((max/abs((max-roundratio)%roundratio)));
                    }
                    everyNth=everyNth%2==0?everyNth+1:everyNth;
                    if(count%2==0 || count%everyNth==0)
                        returnval=floor(val);
                    else returnval=ceil(val);
                }
                if(count==max) count=0;
                else count++;
            }
            roundedval=returnval;
            return returnval;
        }
        int rounded(){
            return roundedval;
        }
    };

    static float standard_deviation(const float* data, int length);
    static void gaussian_window(float* data, int length, float alpha=2.5);
    static double linear_regression(double x1, double x2, double y1,double y2, double val);

    template <class T> static T PointOnCubicBezier( T a, T b,T c, T d, float t ){
        float   ax, bx, cx;
        float   ay, by, cy;
        float   tSquared, tCubed;
        T result;

        cx = 3.0 * (b.x() - a.x());
        bx = 3.0 * (c.x() - b.x()) - cx;
        ax = d.x() - a.x() - cx - bx;

        cy = 3.0 * (b.y() - a.y());
        by = 3.0 * (c.y() - b.y()) - cy;
        ay = d.y() - a.y() - cy - by;

        tSquared = t * t;
        tCubed = tSquared * t;

        result.setX((ax * tCubed) + (bx * tSquared) + (cx * t) + a.x());
        result.setY((ay * tCubed) + (by * tSquared) + (cy * t) + a.y());

        return result;
    }

    template <class T, class P> static float PointOnCubicBezierAt(T a, T b, T c, T d,float dt, P x){
        T returnP,responseP;
        returnP.setX(1);
        for (float i=0;i<1;i+=dt){
            T response=MyMath::PointOnCubicBezier(a,b,c,d,i);
            if(fabs(response.x()-x)<fabs(returnP.x()-x)) returnP=response;
            if (response.x()>x) break;
        }
        return float(returnP.y());
    }
};

#endif
