#include "Matrix.h"
#include "RungeKuttaIntegrator.h"
#include "LinearInterpolator.h"
#include "Plot.h"
#include "Attitude.h"
#include "AttitudeState.h"
#include <vector.h>
using namespace std;
Vector NullFunctor(const ssfTime& _pSSFTime, const OrbitState& _pOrbitState, const AttitudeState& _pAttitudeState)
{
    return Vector(3);
}
static Vector AttituteDynamics(const ssfTime &_time, const Vector& _integratingState, Orbit *_pOrbit, Attitude *_pAttitude, const Matrix &_parameters, const Functor &_forceFunctorPtr)
{
    
    
    
    static Vector stateDot(7);
    static Matrix bMOI(3,3); 
    static Matrix qtemp(4,3); 
    static Matrix Tmoment(3,1);
    static Vector qIn(4); 
    static Vector wIn(3);
    
    
    qIn = _integratingState(_(VectorIndexBase,VectorIndexBase + 3));
    wIn = _integratingState(_(VectorIndexBase + 4,VectorIndexBase + 6));
    qIn /= norm2(qIn);
    
    qtemp(_(VectorIndexBase+0,VectorIndexBase+2),_(VectorIndexBase+0,VectorIndexBase+2)) = skew(qIn(_(VectorIndexBase+0,VectorIndexBase+2))) + qIn(VectorIndexBase+3) * eye(3);
    qtemp(VectorIndexBase+3, _(VectorIndexBase+0,VectorIndexBase+2)) = -(~qIn(_(VectorIndexBase+0,VectorIndexBase+2)));
    qtemp(_,MatrixIndexBase) = 0.5 * qtemp * wIn;
    
    bMOI = _parameters(_(MatrixIndexBase+0,MatrixIndexBase+2),_(MatrixIndexBase+0,MatrixIndexBase+2));
    Tmoment = (bMOI.inverse() * (Tmoment - skew(wIn) * (bMOI * wIn)));
    
    
    stateDot(_(VectorIndexBase,VectorIndexBase + 3)) = qtemp(_,MatrixIndexBase);
    stateDot(_(VectorIndexBase + 4,VectorIndexBase + 6)) = Tmoment(_,MatrixIndexBase);
    cout << 0.5 * ~wIn * (bMOI * wIn);
    return stateDot;
}
int main()
{
    
    RungeKuttaIntegrator myIntegrator; 
    myIntegrator.SetNumSteps(1000);
    
    vector<ssfTime> integrationTimes;
    ssfTime begin(0);
    ssfTime end(begin + 20);
    integrationTimes.push_back(begin);
    integrationTimes.push_back(end);
    
    
    AttitudeState myAttitudeState;
    myAttitudeState.SetRotation(Rotation(Quaternion(0,0,0,1)));
    Vector initAngVelVector(3);
        initAngVelVector(1) = 0.1;
    myAttitudeState.SetAngularVelocity(initAngVelVector);
    SpecificFunctor AttitudeForcesFunctor(&NullFunctor);
    
    
    Matrix Parameters = eye(3);
    
    Matrix I(3,3);                              
        I(1,1) = 100;
        I(2,2) = 200;
        I(3,3) = 150;
    Vector h(3);
        h(1) = 10;
        h(2) = 20;
        h(3) = 30;
    Vector w = I.inverse() * h;
    Matrix Tmat = 0.5 * (~w * (I * w));
    Vector initEugeneState(4);
        initEugeneState(_(1,3)) = h(_(1,3));
        initEugeneState(4) = Tmat(1,1);
 
    cout << "PropTime = " << begin.GetSeconds() << " s -> " << end.GetSeconds() << " s" << endl;
    cout << "Attitude State: " << ~myAttitudeState.GetState() << endl;
    
    tick();
    Matrix history = myIntegrator.Integrate(
                            integrationTimes,           
                            &AttituteDynamics, 
                            myAttitudeState.GetState(),
                            NULL,
                            NULL,
                            Parameters,
                            AttitudeForcesFunctor
                        );
    cout << "finished propagating in " << tock() << " seconds." << endl;                                 
    
    
    Matrix plotting = history(_,_(MatrixIndexBase,MatrixIndexBase+4));
    
    
    AttitudeHistory myAttHistory;
    myAttHistory.SetInterpolator(new LinearInterpolator);
    Vector AngVel(3);
    Vector Quat(4);
    Rotation myRot;
    for(int jj = 1;jj <= history[MatrixRowsIndex].getIndexBound(); ++jj)
    {
        AngVel = ~(history(jj, _(6,8)));
        Quat = ~(history(jj,_(2,5)));
        myRot.Set(Quaternion(Quat));
        myAttHistory.AppendHistory(history(jj,1), myRot, AngVel);
    }
    
    
    double requestTime;
    cout << "Output attitude state at time (s): " << flush;
    cin >> requestTime;
    ssfTime myTime(requestTime);
    myAttHistory.GetState(myTime, myRot, AngVel);
    cout << myTime.GetSeconds() << " : " << ~myRot.GetQuaternion() << endl;
    
    
    
    ofstream ofile;
    ofile.open("AttitudeHistory.dat");
    ofile << history;
    ofile.close();
    return 0;
                            
}