/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2016 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK 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 Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/


// -- Core stuff
#include "PlaneC.h"

// -- VTK stuff
#include <vtkProperty.h>
#include <vtkPlaneSource.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkTransform.h>
#include <vtkAbstractTransform.h>
#include <vtkPlanes.h>
#include <vtkSmartPointer.h>


namespace camitk {
//--------------------Constructor---------------------
PlaneC::PlaneC() {
    // create the plane
    plane = vtkSmartPointer<vtkPlaneSource>::New();

    // create the mapper
    planeMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    planeMapper->SetInputConnection(plane->GetOutputPort());

    // creates the actor in order to give to the InteractiveViewer
    planeActor = vtkSmartPointer<vtkActor>::New();
    planeActor->SetMapper(planeMapper);
    planeActor->PickableOff();

    // Properties of the actor
    aProp = vtkSmartPointer<vtkProperty>::New();

    // clear the transformation data
    clear();

    // set the default visualization mode
    setVisuPlane();
}

//--------------------Destructor---------------------
PlaneC::~PlaneC() {
//TODO
}


//--------------------Set Visualization of Plane---------------------
void PlaneC::setVisuPlane() {
    //Initialize properties of the actor associated to the vtkPlaneSource
    aProp->SetColor(0.0, 0.0, 1.0);
    aProp->SetOpacity(0.1);
    //Visualization in wireframe
    aProp->SetRepresentationToWireframe();
    planeActor->SetProperty(aProp);
    planeActor->ApplyProperties();


}

//--------------------Set Visualization Of Active Plane in red---------------------
void PlaneC::setVisuActivePlane() {
    //Change properties of the actor associated to the vtkPlaneSource
    aProp->SetColor(1.0, 0.0, 0.0);//color red
    aProp->SetOpacity(0.2);
    //Visualization in surface
    aProp->SetRepresentationToSurface();
    planeActor->SetProperty(aProp);
    planeActor->ApplyProperties();
}

//--------------------Set Visualization Of Active Plane in blue---------------------
void PlaneC::setVisuActivePlaneOff() {
    //Change properties of the actor associated to the vtkPlaneSource
    aProp->SetColor(0.0, 0.0, 1.0);//color blue
    aProp->SetOpacity(0.1);
    //Visualization in surface
    aProp->SetRepresentationToSurface();
    planeActor->SetProperty(aProp);
    planeActor->ApplyProperties();
}


//--------------------Reset the Plane of the View---------------------
void PlaneC::clear() {
    for (int i = 0; i < 3; i++) {
        transfor[i] = 0;
    }
    translat = 0;
    angle1 = 0; // no angle when I start
    angle2 = 0;
    translation1 = 0;// no angle when I start

    // Initialization by default
    plane->SetOrigin(0.0, 0.0, 0.0);
    plane->SetPoint1(0.0, 1.0, 0.0);
    plane->SetPoint2(0.0, 0.0, 1.0);

}

//--------------------getActor----------
vtkSmartPointer<vtkActor> PlaneC::getActor() {
    return planeActor;
}

//--------------------Initialize a plane with PlaneCtype axis as normal----------
void PlaneC::init(PlaneC::PlaneCType type, double *bounds) {

    setOrigin(bounds[0], bounds[2], bounds[4]);

    switch (type) {

    case X_MAX:
        setXAxisPoint(bounds[0], bounds[3], bounds[4]);
        setYAxisPoint(bounds[0], bounds[2], bounds[5]);
        setTranslationMaxInPercent(bounds[1] - bounds[0]);
        translatePlaneX();
        break;
    case Y_MIN:
        setXAxisPoint(bounds[1], bounds[2], bounds[4]);
        setYAxisPoint(bounds[0], bounds[2], bounds[5]);
        break;
    case Y_MAX:
        setXAxisPoint(bounds[0], bounds[2], bounds[5]);
        setYAxisPoint(bounds[1], bounds[2], bounds[4]);
        setTranslationMaxInPercent(bounds[3] - bounds[2]);
        translatePlaneY();
        break;
    case Z_MIN:
        setXAxisPoint(bounds[0], bounds[3], bounds[4]);
        setYAxisPoint(bounds[1], bounds[2], bounds[4]);
        break;
    case Z_MAX:
        setXAxisPoint(bounds[1], bounds[2], bounds[4]);
        setYAxisPoint(bounds[0], bounds[3], bounds[4]);
        setTranslationMaxInPercent(bounds[5] - bounds[4]);
        translatePlaneZ();
        break;
    case X_MIN:
    case UNDEFINED:
    default:
        setXAxisPoint(bounds[0], bounds[2], bounds[5]);
        setYAxisPoint(bounds[0], bounds[3], bounds[4]);
        break;
    }



}


//--------------------Translate the Plane on Xaxis---------------------
void PlaneC::translatePlaneX() {

    vtkSmartPointer<vtkTransform> translation = vtkSmartPointer<vtkTransform>::New();
    double a = double(translat - translation1);
    translation1 = translat;

    translation->Translate(a, 0, 0);

    double tab[3];
    plane->GetCenter(tab);
    //change the coordinates of the Plane center and all the plane is translated
    translation->TransformPoint(tab, tab);
    plane->SetCenter(tab);

    translation->Update();
}

//--------------------Translate the Plane on Yaxis---------------------
void PlaneC::translatePlaneY() {

    vtkSmartPointer<vtkTransform> translation = vtkSmartPointer<vtkTransform>::New();
    double a = double(translat - translation1);
    translation1 = translat;

    translation->Translate(0, a, 0);

    double tab[3];
    plane->GetCenter(tab);
    //change the coordinates of the Plane center and all the plane is translated
    translation->TransformPoint(tab, tab);
    plane->SetCenter(tab);

    translation->Update();
}

//--------------------Translate the Plane on Zaxis---------------------
void PlaneC::translatePlaneZ() {

    vtkSmartPointer<vtkTransform> translation = vtkSmartPointer<vtkTransform>::New();
    double a = double(translat - translation1);
    translation1 = translat;

    translation->Translate(0, 0, a);

    double tab[3];
    plane->GetCenter(tab);
    //change the coordinates of the Plane center and all the plane is translated
    translation->TransformPoint(tab, tab);
    plane->SetCenter(tab);

    translation->Update();
}


//--------------------Rotation around Axe1---------------------
void PlaneC ::rotationAxe1() {
    //coordinates of the origin
    double tabO[3];
    //coordinates of the center
    double tabC[3];
    //coordinates of the Point1
    double tabP[3];
    //coordinates of the Normal
    double tabN[3];
    //coordinates of the vector for the rotation
    double tab[3];

    //Initialization of each point
    plane->GetCenter(tabC);
    plane->GetOrigin(tabO);
    plane->GetPoint1(tabP);
    plane->GetNormal(tabN);
    //Initialization of the vector with the Axis1
    for (unsigned int i = 0; i < 3; i++) {
        tab[i] = tabP[i] - tabO[i];
    }


    // rotate with the difference between what I want (ang) and the current angle (angle1)
    double differenceAngle = transfor[1] - angle1;

    // if this rotation is not too small
    if (fabs(differenceAngle) > 0.5) {

        vtkSmartPointer<vtkTransform> transfo = vtkSmartPointer<vtkTransform>::New();
        //Initialization of the transfo with an angle and the vector
        transfo->RotateWXYZ(double(differenceAngle), tab);

        // record the new angle
        angle1 = transfor[1];

        //Apply the rotation to the normal and all the plane will rotate
        transfo->TransformPoint(tabN, tabN);
        plane->SetNormal(tabN);
        plane->Update();
        transfo->Update();
    }

}


//--------------------Rotation around Axe2---------------------
void PlaneC ::rotationAxe2() {
    //coordinates of the origin
    double tabO[3];
    //coordinates of the center
    double tabC[3];
    //coordinates of the Point2
    double tabP[3];
    //coordinates of the Normal
    double tabN[3];
    //coordinates of the vector for the rotation
    double tab[3];

    //Initialization of each point
    plane->GetCenter(tabC);
    plane->GetOrigin(tabO);
    plane->GetPoint2(tabP);
    plane->GetNormal(tabN);

    //Initialization of the vector with the Axis2
    for (int i = 0; i < 3; i++) {

        tab[i] = tabP[i] - tabO[i];
    }

    // rotate with the difference between what I want (ang) and the current angle (angle2)
    double differenceAngle = (double)transfor[2] - (double)angle2;

    // if this rotation is not too small
    if (fabs(differenceAngle) > 0.5) {
        vtkSmartPointer<vtkTransform> transfo = vtkSmartPointer<vtkTransform>::New();
        //Initialization of the transfo with an angle and the vector
        transfo->RotateWXYZ(differenceAngle, tab);

        // record the new angle
        angle2 = transfor[2];

        //Apply the rotation to the normal and all the plane will rotate
        transfo->TransformPoint(tabN, tabN);
        plane->SetNormal(tabN);
        plane->Update();
        transfo->Update();
    }

}

//--------------------Set the transformation---------------------
void PlaneC ::setTransfoPercentToRealValue(double *tab, double min, double max) {
    for (int i = 0; i < 3; i++)
        transfor[i] = tab[i];
    translat = transfor[0] * (max - min) / 100;
}

//--------------------Get the transformation---------------------
void PlaneC::getTransformationInPercent(double *t, double *r1, double *r2) {
    *t = transfor[0];
    *r1 = transfor[1];
    *r2 = transfor[2];
}

//--------------------Set the translation---------------------
void  PlaneC::setTranslationMaxInPercent(double trans) {
    translat = trans;
    transfor[0] = 100;
}

//--------------------Get the origin---------------------
void PlaneC::getOrigin(double *tab) {
    plane->GetOrigin(tab);

}

//--------------------Get the normal---------------------
void PlaneC::getNormal(double *tab) {
    plane->GetNormal(tab);

}
//--------------------Set the origin---------------------
void PlaneC::setOrigin(double x, double y, double z) {

    plane->SetOrigin(x, y, z);

}
//--------------------setXAxisPoint---------------------
void PlaneC::setXAxisPoint(double x, double y, double z) {

    plane->SetPoint1(x, y, z);

}
//--------------------setYAxisPoint---------------------
void PlaneC::setYAxisPoint(double x, double y, double z) {

    plane->SetPoint2(x, y, z);

}

}
