#include "ImageOrientationHelper.h"

// Vtk Includes
#include <vtkTransform.h>
#include <vtkMatrix4x4.h>

// CamiTK Includes
#include <Core.h>

namespace camitk {

// ---------------------- getPossibleImageOrientations ----------------------
const QStringList ImageOrientationHelper::getPossibleImageOrientations() {
    QStringList list;
    list << "RAI";
    list << "RPS";
    list << "RIP";
    list << "RSA";
    list << "LAS";
    list << "LPI";
    list << "LIA";
    list << "ARS";
    list << "ALI";
    list << "AIR";
    list << "ASL";
    list << "PRI";
    list << "PLS";
    list << "PIL";
    list << "PSR";
    list << "IRA";
    list << "ILP";
    list << "IAL";
    list << "IPR";
    list << "SRP";
    list << "SLA";
    list << "SAR";
    list << "SPL";
    list << "UNKNOWN";

    return list;
}

// ---------------------- getOrientationAsEnum ----------------------
ImageOrientationHelper::PossibleImageOrientations ImageOrientationHelper::getOrientationAsEnum(QString orientation) {
    PossibleImageOrientations orient;
    QStringList list = getPossibleImageOrientations();
    int enumNumber = list.indexOf(orientation.toUpper());

    if (enumNumber != -1) {
        orient  = (PossibleImageOrientations)(enumNumber);
    }
    else {
        orient = UNKNOWN;
    }

    return orient;

}

// ---------------------- getOrientationAsQString ----------------------
QString ImageOrientationHelper::getOrientationAsQString(ImageOrientationHelper::PossibleImageOrientations orientation) {
    QStringList list = getPossibleImageOrientations();
    int index = (int)(orientation);

    if ((index >= 0) && (index < list.size())) {
        return list[index];
    }
    else {
        return "UNKNOWN";
    }
}


// ---------------------- getTransformToRAI ----------------------
vtkSmartPointer<vtkMatrix4x4> ImageOrientationHelper::getTransformToRAI(QString orientation, double dimX, double dimY, double dimZ) {
    PossibleImageOrientations orient = getOrientationAsEnum(orientation);
    if (orient != UNKNOWN) {
        return getTransformToRAI(orient, dimX, dimY, dimZ);
    }
    else {
        return nullptr;
    }
}

// ---------------------- getTransformFromRAI ----------------------
vtkSmartPointer<vtkMatrix4x4> ImageOrientationHelper::getTransformFromRAI(QString orientation, double dimX, double dimY, double dimZ) {
    PossibleImageOrientations orient = getOrientationAsEnum(orientation);
    if (orient != UNKNOWN) {
        return getTransformFromRAI(orient, dimX, dimY, dimZ);
    }
    else {
        return nullptr;
    }

}

// ---------------------- getTransformFromRAI ----------------------
vtkSmartPointer<vtkMatrix4x4> ImageOrientationHelper::getTransformFromRAI(PossibleImageOrientations orientation, double dimX, double dimY, double dimZ) {
    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
    transform->Identity();

    switch (orientation) {
        default:
        case RAI:
            // Keep Identity for RAI (default orientation)
            break;
        case RPS:
            transform->RotateX(180);
            // translate axis are in the new frame !
            transform->Translate(0.0, -dimY, -dimZ);
            break;

        case RIP:
            transform->RotateX(90);
            transform->Translate(0.0, 0.0, -dimY);
            break;
        case RSA:
            transform->RotateX(-90);
            transform->Translate(0.0, -dimZ, 0.0);
            break;
        case LAS:
            transform->RotateY(180);
            transform->Translate(-dimX, 0.0, -dimZ);
            break;
        case LPI:
            transform->RotateZ(180);
            transform->Translate(-dimX, -dimY, 0.0);
            break;
        case LIA:
            transform->RotateX(-90);
            transform->RotateZ(180);
            transform->Translate(-dimX, 0.0, 0.0);
            break;
        case ARS:
            transform->RotateZ(90);
            transform->RotateX(180);
            transform->Translate(0.0, 0.0, -dimZ);
            break;
        case ALI:
            transform->RotateZ(90);
            transform->Translate(0.0, -dimX, 0.0);
            break;
        case AIR:
            transform->RotateZ(90);
            transform->RotateX(90);
            break;
        case ASL:
            transform->RotateZ(90);
            transform->RotateX(-90);
            transform->Translate(0.0, -dimZ, -dimX);
            break;
        case PRI:
            transform->RotateZ(-90);
            transform->Translate(-dimY, 0.0, 0.0);
            break;
        case PLS:
            transform->RotateZ(-90);
            transform->RotateX(180);
            transform->Translate(-dimY, -dimX, -dimZ);
            break;
        case PIL:
            transform->RotateZ(-90);
            transform->RotateX(90);
            transform->Translate(-dimY, 0.0, -dimX);
            break;
        case PSR:
            transform->RotateZ(-90);
            transform->RotateX(-90);
            transform->Translate(-dimY, -dimZ, 0.0);
            break;
        case IRA:
            transform->RotateY(-90);
            transform->RotateX(-90);
            break;
        case ILP:
            transform->RotateY(-90);
            transform->RotateX(90);
            transform->Translate(0.0, -dimX, -dimY);
            break;
        case IAL:
            transform->RotateY(-90);
            transform->Translate(0.0, 0.0, -dimX);
            break;
        case IPR:
            transform->RotateY(-90);
            transform->RotateX(180);
            transform->Translate(0.0, -dimY, 0.0);
            break;
        case SRP:
            transform->RotateY(90);
            transform->RotateX(90);
            transform->Translate(-dimZ, 0.0, -dimY);
            break;
        case SLA:
            transform->RotateY(90);
            transform->RotateX(-90);
            transform->Translate(-dimZ, -dimX, 0.0);
            break;
        case SAR:
            transform->RotateY(90);
            transform->Translate(-dimZ, 0.0, 0.0);
            break;
        case SPL:
            transform->RotateY(90);
            transform->RotateX(180);
            transform->Translate(-dimZ, -dimY, -dimX);
            break;
    }

    transform->Update();

    return transform->GetMatrix();
}

// ---------------------- getTransformToRAI ----------------------
vtkSmartPointer<vtkMatrix4x4> ImageOrientationHelper::getTransformToRAI(PossibleImageOrientations orientation, double dimX, double dimY, double dimZ) {
    double myDimX = dimX;
    double myDimY = dimY;
    double myDimZ = dimZ;

    // Re-orient dimensions first
    switch (orientation) {
        default:
        case RAI:
            // Keep Identity for RAI (default orientation)
            break;
        case RPS:
            myDimX = dimX;
            myDimY = dimY;
            myDimZ = dimZ;
            break;

        case RIP:
            myDimX = dimX;
            myDimY = dimZ;
            myDimZ = dimY;
            break;
        case RSA:
            myDimX = dimX;
            myDimY = dimZ;
            myDimZ = dimY;
            break;
        case LAS:
            myDimX = dimX;
            myDimY = dimY;
            myDimZ = dimZ;
            break;
        case LPI:
            myDimX = dimX;
            myDimY = dimY;
            myDimZ = dimZ;
            break;
        case LIA:
            myDimX = dimX;
            myDimY = dimZ;
            myDimZ = dimY;
            break;
        case ARS:
            myDimX = dimY;
            myDimY = dimX;
            myDimZ = dimZ;
            break;
        case ALI:
            myDimX = dimY;
            myDimY = dimX;
            myDimZ = dimZ;
            break;
        case AIR:
            myDimX = dimZ;
            myDimY = dimX;
            myDimZ = dimY;
            break;
        case ASL:
            myDimX = dimZ;
            myDimY = dimX;
            myDimZ = dimY;
            break;
        case PRI:
            myDimX = dimY;
            myDimY = dimX;
            myDimZ = dimZ;
            break;
        case PLS:
            myDimX = dimY;
            myDimY = dimX;
            myDimZ = dimZ;
            break;
        case PIL:
            myDimX = dimZ;
            myDimY = dimX;
            myDimZ = dimY;
            break;
        case PSR:
            myDimX = dimZ;
            myDimY = dimX;
            myDimZ = dimY;
            break;
        case IRA:
            myDimX = dimY;
            myDimY = dimZ;
            myDimZ = dimX;
            break;
        case ILP:
            myDimX = dimY;
            myDimY = dimZ;
            myDimZ = dimX;
            break;
        case IAL:
            myDimX = dimZ;
            myDimY = dimY;
            myDimZ = dimX;
            break;
        case IPR:
            myDimX = dimZ;
            myDimY = dimY;
            myDimZ = dimX;
            break;
        case SRP:
            myDimX = dimY;
            myDimY = dimZ;
            myDimZ = dimX;
            break;
        case SLA:
            myDimX = dimY;
            myDimY = dimZ;
            myDimZ = dimX;
            break;
        case SAR:
            myDimX = dimZ;
            myDimY = dimY;
            myDimZ = dimX;
            break;
        case SPL:
            myDimX = dimZ;
            myDimY = dimY;
            myDimZ = dimX;
            break;
    }

    vtkSmartPointer<vtkMatrix4x4> transformMatrix = getTransformFromRAI(orientation, myDimX, myDimY, myDimZ);
    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
    transform->SetMatrix(transformMatrix);
    transform->Inverse();
    transform->Update();

    return transform->GetMatrix();
}

} // namespace
