/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * 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$
 ****************************************************************************/

#include "vtkInteractorStylePick.h"

//-- VTK stuff
// disable warning generated by clang about the surrounded headers
#include "CamiTKDisableWarnings"
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
// ---- Picking
#include <vtkPicker.h>
#include <vtkAreaPicker.h>
#include "CamiTKReEnableWarnings"

#include <vtkUnsignedCharArray.h>
#include <vtkObjectFactory.h> // for the New macros

#include <vtkRenderer.h>
#include <vtkCamera.h>
#include <vtkHomogeneousTransform.h>
#include <vtkAssemblyPath.h>

namespace camitk {

//----------------------- Picking Interactor -----------------------
vtkStandardNewMacro(vtkInteractorStylePick);

//----------------------- constructor -----------------------
vtkInteractorStylePick::vtkInteractorStylePick() {
    areaPicking = false;
    startPosition[0] = startPosition[1] = 0;
    endPosition[0] = endPosition[1] = 0;
    moving = false;
    pixelArray = vtkSmartPointer<vtkUnsignedCharArray>::New();
}

//----------------------- SetAreaPicking ------------------------
void vtkInteractorStylePick::SetAreaPicking(bool b) {
    areaPicking = b;
}

//----------------------- OnLeftButtonDown -----------------------
void vtkInteractorStylePick::OnLeftButtonDown() {
    if (!this->Interactor) {
        return;
    }

    this->moving = true;

    if (areaPicking) {
        vtkRenderWindow* renWin = this->Interactor->GetRenderWindow();

        this->startPosition[0] = this->Interactor->GetEventPosition() [0];
        this->startPosition[1] = this->Interactor->GetEventPosition() [1];
        this->endPosition[0] = this->startPosition[0];
        this->endPosition[1] = this->startPosition[1];

        this->pixelArray->Initialize();
        this->pixelArray->SetNumberOfComponents(4);
        int* size = renWin->GetSize();
        this->pixelArray->SetNumberOfTuples(size[0]*size[1]);

        renWin->GetRGBACharPixelData(0, 0, size[0] - 1, size[1] - 1, 1, this->pixelArray);

        this->FindPokedRenderer(this->startPosition[0], this->startPosition[1]);
    }
    else {
        vtkRenderWindowInteractor* rwi = this->Interactor;
        int* eventPos = rwi->GetEventPosition();
        this->FindPokedRenderer(eventPos[0], eventPos[1]);
        this->startPosition[0] = eventPos[0];
        this->startPosition[1] = eventPos[1];
        this->endPosition[0] = eventPos[0];
        this->endPosition[1] = eventPos[1];
        this->Pick();
    }
}

//------------------------------- OnMouseMove -------------------------------
void vtkInteractorStylePick::OnMouseMove() {
    if (!this->Interactor || !this->moving) {
        return;
    }
    if (areaPicking) {
        this->endPosition[0] = this->Interactor->GetEventPosition() [0];
        this->endPosition[1] = this->Interactor->GetEventPosition() [1];
        int* size = this->Interactor->GetRenderWindow()->GetSize();
        if (this->endPosition[0] > (size[0] - 1)) {
            this->endPosition[0] = size[0] - 1;
        }
        if (this->endPosition[0] < 0) {
            this->endPosition[0] = 0;
        }
        if (this->endPosition[1] > (size[1] - 1)) {
            this->endPosition[1] = size[1] - 1;
        }
        if (this->endPosition[1] < 0) {
            this->endPosition[1] = 0;
        }
        this->RedrawRubberBand();
    }
    else {
        vtkRenderWindowInteractor* rwi = this->Interactor;
        int* eventPos = rwi->GetEventPosition();
        this->FindPokedRenderer(eventPos[0], eventPos[1]);
        this->startPosition[0] = eventPos[0];
        this->startPosition[1] = eventPos[1];
        this->endPosition[0] = eventPos[0];
        this->endPosition[1] = eventPos[1];
        this->Pick();
    }
}

// ------------------------------- OnLeftButtonUp -------------------------------
void vtkInteractorStylePick::OnLeftButtonUp() {
    if (!this->Interactor || !this->moving) {
        return;
    }

    if (areaPicking) {
        if ((this->startPosition[0] != this->endPosition[0])
                || (this->startPosition[1] != this->endPosition[1])) {
            this->Pick();
        }
    }

    this->moving = false;
}

// ------------------------------- RedrawRubberBand -------------------------------
void vtkInteractorStylePick::RedrawRubberBand() {
    //update the rubber band on the screen
    int* size = this->Interactor->GetRenderWindow()->GetSize();

    vtkUnsignedCharArray* tmpPixelArray = vtkUnsignedCharArray::New();
    tmpPixelArray->DeepCopy(this->pixelArray);
    unsigned char* pixels = tmpPixelArray->GetPointer(0);

    int min[2], max[2];

    min[0] = this->startPosition[0] <= this->endPosition[0] ?
             this->startPosition[0] : this->endPosition[0];
    if (min[0] < 0) {
        min[0] = 0;
    }
    if (min[0] >= size[0]) {
        min[0] = size[0] - 1;
    }

    min[1] = this->startPosition[1] <= this->endPosition[1] ?
             this->startPosition[1] : this->endPosition[1];
    if (min[1] < 0) {
        min[1] = 0;
    }
    if (min[1] >= size[1]) {
        min[1] = size[1] - 1;
    }

    max[0] = this->endPosition[0] > this->startPosition[0] ?
             this->endPosition[0] : this->startPosition[0];
    if (max[0] < 0) {
        max[0] = 0;
    }
    if (max[0] >= size[0]) {
        max[0] = size[0] - 1;
    }

    max[1] = this->endPosition[1] > this->startPosition[1] ?
             this->endPosition[1] : this->startPosition[1];
    if (max[1] < 0) {
        max[1] = 0;
    }
    if (max[1] >= size[1]) {
        max[1] = size[1] - 1;
    }

    int i;
    for (i = min[0]; i <= max[0]; i++) {
        pixels[4 * (min[1]*size[0] + i)] = 255 ^ pixels[4 * (min[1] * size[0] + i)];
        pixels[4 * (min[1]*size[0] + i) + 1] = 255 ^ pixels[4 * (min[1] * size[0] + i) + 1];
        pixels[4 * (min[1]*size[0] + i) + 2] = 255 ^ pixels[4 * (min[1] * size[0] + i) + 2];
        pixels[4 * (max[1]*size[0] + i)] = 255 ^ pixels[4 * (max[1] * size[0] + i)];
        pixels[4 * (max[1]*size[0] + i) + 1] = 255 ^ pixels[4 * (max[1] * size[0] + i) + 1];
        pixels[4 * (max[1]*size[0] + i) + 2] = 255 ^ pixels[4 * (max[1] * size[0] + i) + 2];
    }
    for (i = min[1] + 1; i < max[1]; i++) {
        pixels[4 * (i * size[0] + min[0])] = 255 ^ pixels[4 * (i * size[0] + min[0])];
        pixels[4 * (i * size[0] + min[0]) + 1] = 255 ^ pixels[4 * (i * size[0] + min[0]) + 1];
        pixels[4 * (i * size[0] + min[0]) + 2] = 255 ^ pixels[4 * (i * size[0] + min[0]) + 2];
        pixels[4 * (i * size[0] + max[0])] = 255 ^ pixels[4 * (i * size[0] + max[0])];
        pixels[4 * (i * size[0] + max[0]) + 1] = 255 ^ pixels[4 * (i * size[0] + max[0]) + 1];
        pixels[4 * (i * size[0] + max[0]) + 2] = 255 ^ pixels[4 * (i * size[0] + max[0]) + 2];
    }

    this->Interactor->GetRenderWindow()->SetRGBACharPixelData(0, 0, size[0] - 1, size[1] - 1, pixels, 0);
    this->Interactor->GetRenderWindow()->Frame();

    tmpPixelArray->Delete();
}

// ------------------------------- Pick -------------------------------
void vtkInteractorStylePick::Pick() {
    static bool inPick = false;

    if (inPick) {
        return;
    }
    inPick = true;
    //find rubber band lower left, upper right and center
    double rbcenter[3];
    int* size = this->Interactor->GetRenderWindow()->GetSize();
    int min[2], max[2];
    min[0] = this->startPosition[0] <= this->endPosition[0] ?
             this->startPosition[0] : this->endPosition[0];
    if (min[0] < 0) {
        min[0] = 0;
    }
    if (min[0] >= size[0]) {
        min[0] = size[0] - 2;
    }

    min[1] = this->startPosition[1] <= this->endPosition[1] ?
             this->startPosition[1] : this->endPosition[1];
    if (min[1] < 0) {
        min[1] = 0;
    }
    if (min[1] >= size[1]) {
        min[1] = size[1] - 2;
    }

    max[0] = this->endPosition[0] > this->startPosition[0] ?
             this->endPosition[0] : this->startPosition[0];
    if (max[0] < 0) {
        max[0] = 0;
    }
    if (max[0] >= size[0]) {
        max[0] = size[0] - 2;
    }

    max[1] = this->endPosition[1] > this->startPosition[1] ?
             this->endPosition[1] : this->startPosition[1];
    if (max[1] < 0) {
        max[1] = 0;
    }
    if (max[1] >= size[1]) {
        max[1] = size[1] - 2;
    }

    rbcenter[0] = (min[0] + max[0]) / 2.0;
    rbcenter[1] = (min[1] + max[1]) / 2.0;
    rbcenter[2] = 0;

    if (this->State == VTKIS_NONE) {
        vtkAssemblyPath* path = nullptr;
        this->Interactor->StartPickCallback();
        vtkSmartPointer<vtkAbstractPropPicker> picker = vtkAbstractPropPicker::SafeDownCast(this->Interactor->GetPicker());
        if (picker != nullptr) {
            vtkAreaPicker* areaPicker = vtkAreaPicker::SafeDownCast(picker);
            if (areaPicker != nullptr) {
                areaPicker->AreaPick(min[0], min[1], max[0], max[1], this->CurrentRenderer);
            }
            else {
                picker->Pick(rbcenter[0], rbcenter[1],
                             0.0, this->CurrentRenderer);
            }
            path = picker->GetPath();
            picker = nullptr;
        }
        if (path == nullptr) {
            this->HighlightProp(nullptr);
            this->PropPicked = 0;
        }
        else {
            this->PropPicked = 1;
        }
        this->Interactor->EndPickCallback();
    }

    // meld VTK-9.1.0/Interaction/Style/vtkInteractorStyleRubberBandPick.cxx camitk/sdk/libraries/core/viewer/vtkInteractorStylePick.cpp
    // this->Interactor->Render();
    inPick = false;

}

// ------------------------------- PrintSelf -------------------------------
void vtkInteractorStylePick::PrintSelf(ostream& os, vtkIndent indent) {
    this->Superclass::PrintSelf(os, indent);
}


}
