var Hermite = {};

Hermite.Polynom = function(t, f, fp) {
    this.times = t;
    this.evals = f;
    this.primes = fp;

    this.baseFunctions = new Array();

    for (var i in this.times) {
        this.baseFunctions.push(new Hermite.BaseFunction(i, this.times));
    }

    // Let's do something at least a little reusable
    this.tools = {};
    if (f[0] instanceof THREE.Vector3) {
        this.tools.whatType = 'THREE.Vector3';
        this.tools.sum  = Tools.sum;
        this.tools.prod = Tools.mul;
    } else {
        this.tools.whatType = 'number';
        this.tools.sum  = function(a, b) { return a + b;  };
        this.tools.prod = function(a, b) { return a * b;  };
    }
}

Hermite.Polynom.prototype.eval = function(t) {
    var ret;

    if (this.tools.whatType === 'THREE.Vector3') {
        ret = new THREE.Vector3();
    } else {
        ret = 0;
    }

    for (var i in this.times) {
        var ti = this.times[i];
        var qi_t = this.baseFunctions[i].eval(t);
        // var qi_ti = this.baseFunctions[i].eval(ti);

        var qip_ti = this.baseFunctions[i].prime(ti);
        var f_ti = this.evals[i];
        var fp_ti = this.primes[i];

        // This is the wikipedia formula
        // ret += (qi_t / qi_ti) * ((1 - (t - ti) * (qip_ti / qi_ti)) * f_ti + (t - ti) * fp_ti);
        // Let's not forget that qi_ti = 1

        // This is the final formula
        // ret += (qi_t) * ((1 - (t - ti) * (qip_ti)) * f_ti + (t - ti) * fp_ti);

        // This is the implementation working with THREE.Vector3
        // In terms of disgusting code, we're quite good there
        ret =
            this.tools.sum(
                ret,
                this.tools.prod(
                    this.tools.sum(
                        this.tools.prod(f_ti, 1 - (t - ti) * (qip_ti)),
                        this.tools.prod(fp_ti, t - ti)
                    ),
                    qi_t
                )
            );
    }

    return ret;
}

Hermite.Polynom.prototype.prime = function(t) {
    var ret;

    if (this.tools.whatType === 'THREE.Vector3') {
        ret = new THREE.Vector3();
    } else {
        ret = 0;
    }

    for (var i in this.times) {
        var ti = this.times[i];
        var qi_t = this.baseFunctions[i].eval(t);
        // var qi_ti = this.baseFunctions[i].eval(ti);

        var qip_t  = this.baseFunctions[i].prime(t );
        var qip_ti = this.baseFunctions[i].prime(ti);
        var f_ti = this.evals[i];
        var fp_ti = this.primes[i];

        // The return of the disgusting code...
        // First part is the same that the eval function, but changing qi_t by qip_t
        // (first part of the derivative)
        ret =
            this.tools.sum(
                ret,
                this.tools.prod(
                    this.tools.sum(
                        this.tools.prod(f_ti, 1 - (t - ti) * (qip_ti)),
                        this.tools.prod(fp_ti, t - ti)
                    ),
                    qip_t
                )
            );

        // Here, we just add
        // ret += qi_t * (-qip_t * f_ti + fp_ti);
        ret =
            this.tools.sum(
                ret,
                this.tools.prod(
                    this.tools.sum(
                        this.tools.prod(
                            f_ti,
                            -qip_t
                        ),
                        fp_ti
                    ),
                    qi_t
                )
            );

        // Now the following code is the same as the precedent affectation
        // However it doesn't work, and I can't see the difference between
        // this and the previous one... so I keep it here, to find the
        // mistate later
        // ret =
        //     this.tools.sum(
        //         ret,
        //         this.tools.prod(
        //             this.tools.sum(
        //                 fp_ti,
        //                 this.tools.prod(
        //                     f_ti,
        //                     -qip_ti
        //                 )
        //             ),
        //             qi_t
        //         )
        //     );
    }

    return ret;
}

Hermite.BaseFunction = function(index, times) {
    this.index = index;
    this.times = times;
}

Hermite.BaseFunction.prototype.eval = function(t) {
    var ret = 1;

    for (var i in this.times) {
        if (i !== this.index) {
            ret *= (t - this.times[i]) / (this.times[this.index] - this.times[i]);
        }
    }

    return ret * ret;
}

Hermite.BaseFunction.prototype.prime = function(t) {
    var ret = 0;

    for (var i in this.times) {
        if (i !== this.index) {
            ret += 2 / (t - this.times[i]);
        }
    }

    return this.eval(t) * ret;
}

Hermite.special = {};

// This polynom interpolates with two coords and one derivative
// t = [0,1]
Hermite.special.Polynom = function(P0, P1, PP1) {
    this.tools = {};
    if (P0 instanceof THREE.Vector3) {
        this.tools.sum = Tools.sum;
        this.tools.mul = Tools.mul;
        this.tools.diff = Tools.diff;
        this.c = P0.clone();
    } else {
        this.tools.sum = function(a,b) { return a+b; };
        this.tools.mul = function(a,b) { return a*b; };
        this.tools.diff = function(a,b) { return a-b; };
        this.c = P0;
    }

    this.a = this.tools.sum(PP1, this.tools.diff(P0, P1));
    this.b = this.tools.diff(this.tools.mul(this.tools.diff(P1,P0), 2), PP1);
}

Hermite.special.Polynom.prototype.eval = function(t) {
    return this.tools.sum(this.tools.mul(this.a, t*t), this.tools.sum(this.tools.mul(this.b, t), this.c));
}

Hermite.special.Polynom.prototype.prime = function(t) {
    return this.tools.sum(this.tools.mul(this.a,2*t), this.b);
}
