/***************************************************************************
 *   Copyright (C) 2004 by Trenton Carr                                    *
 *   trenton.carr@gmail.com                                                *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/

#include "hyperbola.h"

#include <math.h>

#include <klocale.h>
#include <knuminput.h>
#include <ktextbrowser.h>

#include <qlabel.h>
#include <qlayout.h>
#include <qstring.h>
#include <qcheckbox.h>
//Added by qt3to4:
#include <QPaintEvent>
#include <Q3VBoxLayout>

#include <../kplotwidget.h>

#define SWAP(a,b) do { double tmp = b ; b = a ; a = tmp ; } while(0)

Hyperbola::Hyperbola(QWidget *parent, const char *name)
        : HyperbolaD(parent, name)
{
    QWidget *viewPlot = new QWidget ( PlotFrame, 0);

    Q3VBoxLayout *vlay1 = new Q3VBoxLayout( viewPlot, 0, -1 );

    hi = new HyperInputD();

    pw = new KPlotWidget( -10.0, 10.0, -10.0, 10.0, viewPlot);

    pw->setShowAxes( false );
    pw->setShowTickMarks( false );
    pw->setCartesian ( true );
    pw->setBGColor( QColor( "black" ) );
    pw->setFGColor( QColor ( "white") );

    pw->setShowCartAxis( true );
    pw->setShowCartTickMarks( true );
    pw->setShowCartTickLabels( true );

    vlay1->addWidget( pw );

    viewPlot->resize( 400, 400 );

    connect( hi->Point_x, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( hi->Point_y, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( hi->parabola_ax, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( hi->parabola_bx, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( hi->parabola_c, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( hi->Line_mx, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( hi->Line_c, SIGNAL(valueChanged(double)), SLOT( slotUpdateHyperbola()) );

    connect( showAxis,SIGNAL(toggled( bool )), SLOT(slotUpdateHyperbola()) );

    connect( showTickMarks, SIGNAL(toggled( bool )), SLOT( slotUpdateHyperbola()) );

    connect( showTickLabels, SIGNAL(toggled( bool )), SLOT( slotUpdateHyperbola()) );

    initObjects();
}

Hyperbola::~Hyperbola()
{}

void Hyperbola::paintEvent( QPaintEvent* )
{
    pw->update();
}

void Hyperbola::initObjects()
{
    double a = 0.0;
    double b = 0.0;
    double c = 0.0;

    _y = 0.0;
    _x = 0.0;

    double k = _x * _y;

    KPlotObject *hyperbola;

    KPlotObject *parabola;

    KPlotObject *line;

    hyperbola = new KPlotObject( "1", "green", KPlotObject::CURVE, 1, KPlotObject::SOLID );

    for( double x = -19.0; x < 19.0; x=x+0.1 )
    {
        DPoint *p = new DPoint( x, k/x ); //sets both X and Y coords
        hyperbola->addPoint( p );
    }
    pw->addObject( hyperbola );

    parabola = new KPlotObject( "2", "red", KPlotObject::CURVE, 1, KPlotObject::SOLID );

    for( double x = -99.0; x < 99.0; x=x+0.1 )
    {
        DPoint *p = new DPoint( x,  a*x*x + b*x + c ); //sets both X and Y coords
        parabola->addPoint( p );
    }
    pw->addObject( parabola );

    line = new KPlotObject( "3", "blue", KPlotObject::POLYGON, 1, KPlotObject::SOLID );
    line->addPoint( new DPoint( -20.0, 0.0 ) );
    line->addPoint( new DPoint( 20.0, 0.0 ) );

    pw->addObject( line );

}

void Hyperbola::slotUpdateHyperbola()
{
    if ( showAxis->isChecked() )
    {
        pw->setShowCartAxis( true );
    }
    else
    {
        pw->setShowCartAxis( false );
    }

    if ( showTickMarks->isChecked() )
    {
        pw->setShowCartTickMarks( true );
        showTickLabels->setEnabled ( true );
    }
    else
    {
        pw->setShowCartTickMarks( false );
        showTickLabels->setEnabled ( false );
    }

    if ( showTickLabels->isChecked() )
    {
        pw->setShowCartTickLabels( true );
    }
    else
    {
        pw->setShowCartTickLabels( false );
    }

    KPlotObject *hyperbola = pw->object( 0 );
    double _x = hi->Point_x->value();
    double _y = hi->Point_y->value();

    double k = (_x*_y);

    DPoint *hp = new DPoint();
    for ( hp = hyperbola->points()->first(); hp; hp = hyperbola->points()->next() )
    {
        hp->setY( k/(hp->x()) );
    }

    KPlotObject *parabola = pw->object( 1 );              //Parabolas one
    double a = hi->parabola_ax->value();
    double b = hi->parabola_bx->value();
    double c = hi->parabola_c->value();

    DPoint *pp1 = new DPoint();
    for ( pp1 = parabola->points()->first(); pp1; pp1 = parabola->points()->next() )
    {
        //No need to change X values, they just go from -99. to +99.
        pp1->setY( a*(pp1->x())*(pp1->x()) + b*(pp1->x()) + c );
    }

    KPlotObject *line = pw->object( 2 );
    double m = hi->Line_mx->value();
    double cl = hi->Line_c->value();

    DPoint *p = new DPoint();
    for ( p = line->points()->first(); p; p = line->points()->next() )
    {
        p->setY( m*(p->x()) + cl );
    }

    QString qsResult;

    if ( ! line_or_parabola )
    {
        double x0;
        double x1;
        double x2;

        double *p_x0 = &x0;
        double *p_x1 = &x1;
        double *p_x2 = &x2;

        b /= a ;
        c /= a ;
        k /= a ;

        int numroot = solveCubic ( b, c, -k, p_x0, p_x1, p_x2);

        if (numroot == 1)
        {
            if ( a == 0.)
            {
                qsResult = "Medic!!!!!\nThis might be a straightline.";
                result->setText(qsResult);
            }
            else
            {
                double y0 = (_x*_y)/x0;

                QString qsXs0 = qsXs0.number(x0);
                QString qsYs0 = qsYs0.number(y0);

                qsResult = i18n("The Hyperbola and Parabola touch here\n"
                                "(%1)|(%2)").arg(qsXs0,qsYs0);
                result->setText(qsResult);
            }
        }
        else if (numroot == 3)
        {
            double y0 = (_x*_y)/x0;
            double y1 = (_x*_y)/x1;
            double y2 = (_x*_y)/x2;

            QString qsXs0 = qsXs0.number(x0);
            QString qsYs0 = qsYs0.number(y0);

            QString qsXs1 = qsXs1.number(x1);
            QString qsYs1 = qsYs1.number(y1);

            QString qsXs2 = qsXs2.number(x2);
            QString qsYs2 = qsYs2.number(y2);

            qsResult = i18n("They touch here\n"
                            "(%1)|(%2)\n"
                            "and here\n"
                            "(%3)|(%4)\n"
                            "and here\n"
                            "(%5)|(%6)").arg(qsXs0,qsYs0,qsXs1,qsYs1).arg(qsXs2,qsYs2);
            result->setText(qsResult);
        }
    }
    else
    {
        double dis = cl*cl-(4*m*(-k));

        if (dis == 0 )
        {
            double xs1 = (-cl + sqrt(dis))/(2*m);
            double ys1 = m*xs1+cl;

            QString qsXs1 = qsXs1.number(xs1);
            QString qsYs1 = qsYs1.number(ys1);

            qsResult = i18n("The Hyperbola and line touch here\n"
                            "(%1|%2)").arg(qsXs1,qsYs1);
            result->setText(qsResult);
        }
        else if ( dis < 0)
        {
            qsResult = i18n("The Hyperbola and line do not intersect");
            result->setText(qsResult);
        }
        else if ( dis > 0)
        {
            double xs1 = (-cl + sqrt(dis))/(2*m);
            double ys1 = k/xs1;

            double xs2 = (-cl - sqrt(dis))/(2*m);
            double ys2 = k/xs2;

            QString qsXs1 = qsXs1.number(xs1);
            QString qsYs1 = qsYs1.number(ys1);

            QString qsXs2 = qsXs2.number(xs2);
            QString qsYs2 = qsYs2.number(ys2);

            qsResult = i18n("The Hyperbola and line intersect here (%1|%2) and here (%3|%4)").arg(qsXs1,qsYs1,qsXs2,qsYs2);
            result->setText(qsResult);
        }
    }
    update();
}



// finds the real roots of x^3 + a x^2 + b x + c = 0

int Hyperbola::solveCubic (double a, double b, double c, double *x0, double *x1, double *x2)
{
    double q = (a * a - 3 * b);
    double r = (2 * a * a * a - 9 * a * b + 27 * c);

    double Q = q / 9;
    double R = r / 54;

    double Q3 = Q * Q * Q;
    double R2 = R * R;

    double CR2 = 729 * r * r;
    double CQ3 = 2916 * q * q * q;

    if (R == 0 && Q == 0)
    {
        *x0 = - a / 3 ;
        *x1 = - a / 3 ;
        *x2 = - a / 3 ;
        return 3 ;
    }
    else if (CR2 == CQ3)
    {
        double sqrtQ = sqrt (Q);

        if (R > 0)
        {
            *x0 = -2 * sqrtQ  - a / 3;
            *x1 = sqrtQ - a / 3;
            *x2 = sqrtQ - a / 3;
        }
        else
        {
            *x0 = - sqrtQ  - a / 3;
            *x1 = - sqrtQ - a / 3;
            *x2 = 2 * sqrtQ - a / 3;
        }
        return 3 ;
    }
    else if (CR2 < CQ3)
    {
        double sqrtQ = sqrt (Q);
        double sqrtQ3 = sqrtQ * sqrtQ * sqrtQ;
        double theta = acos (R / sqrtQ3);
        double norm = -2 * sqrtQ;
        *x0 = norm * cos (theta / 3) - a / 3;
        *x1 = norm * cos ((theta + 2.0 * M_PI) / 3) - a / 3;
        *x2 = norm * cos ((theta - 2.0 * M_PI) / 3) - a / 3;

        if (*x0 > *x1)
            SWAP(*x0, *x1) ;

        if (*x1 > *x2)
        {
            SWAP(*x1, *x2) ;

            if (*x0 > *x1)
                SWAP(*x0, *x1) ;
        }
        return 3;
    }
    else
    {
        double sgnR = (R >= 0 ? 1 : -1);
        double A = -sgnR * pow (fabs (R) + sqrt (R2 - Q3), 1.0/3.0);
        double B = Q / A ;
        *x0 = A + B - a / 3;
        return 1;
    }
}

void Hyperbola::slotStraight_Line()
{
    hi->parabolaEquation1->hide();
    hi->parabola_ax->hide();
    hi->x2rd->hide();
    hi->parabola_bx->hide();
    hi->X1->hide();
    hi->parabola_c->hide();
    hi->textLabel5_2->hide();

    hi->TextLabelFirstInfoPoint->show();
    hi->Point_x->show();
    hi->line5->show();
    hi->Point_y->show();
    hi->textLabel4->show();

    hi->textLabelFirtsInfoLine->show();
    hi->Line_mx->show();
    hi->textLabel6->show();
    hi->Line_c->show();
    hi->textLabel5->show();

    KPlotObject *line = pw->object( 2 );
    line->setHidden( false );

    KPlotObject *parabola = pw->object( 1 );
    parabola->setHidden( true );

    line_or_parabola = true;

    hi->show();

    update();
}


void Hyperbola::slotParabola()
{
    hi->textLabelFirtsInfoLine->hide();
    hi->Line_mx->hide();
    hi->textLabel6->hide();
    hi->Line_c->hide();
    hi->textLabel5->hide();

    hi->TextLabelFirstInfoPoint->show();
    hi->Point_x->show();
    hi->line5->show();
    hi->Point_y->show();
    hi->textLabel4->show();

    hi->parabolaEquation1->show();
    hi->parabola_ax->show();
    hi->x2rd->show();
    hi->parabola_bx->show();
    hi->X1->show();
    hi->parabola_c->show();
    hi->textLabel5_2->show();

    KPlotObject *line = pw->object( 2 );
    line->setHidden( true );


    KPlotObject *parabola = pw->object( 1 );
    parabola->setHidden( false );

    line_or_parabola = false;

    hi->show();

    update();
}


/*!
    \fn Hyperbola::slotShowTheory()
 */
void Hyperbola::slotShowTheory()
{
    /// @todo implement me
}


#include "hyperbola.moc"
