/*=========================================================================

  Program:   Visualization Toolkit
  Module:    pick.cxx
  Language:  C++
  Date:      Date: 08/15/2002 17:00:00 
  Version:   Revision: 1.0 

Copyright (c) 1993-2001 Ken Martin, Will Schroeder, Bill Lorensen 
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * Neither name of Ken Martin, Will Schroeder, or Bill Lorensen nor the names
   of any contributors may be used to endorse or promote products derived
   from this software without specific prior written permission.

 * Modified source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/

#include "vtkPointPicker.h"
#include "vtkStructuredGrid.h"
#include "vtkUnstructuredGrid.h"
#include "vtkPolyData.h"
#include "vtkCell.h"
#include "vtkCellPicker.h"
#include "vtkTextMapper.h"
#include "vtkFollower.h"
#include "vtkVectorText.h"
#include "vtkTexturedSphereSource.h"
#include "vtkEarthSource.h"
#include "vtkSphereSource.h"
#include "vtkTexture.h"
#include "vtkPNMReader.h"
#include "vtkConeSource.h"
#include "vtkGlyph3D.h"
#include "vtkPolyDataMapper.h"
#include "vtkShrinkFilter.h"
#include "vtkDataSetMapper.h"
#include "vtkCamera.h"
#include "vtkActor.h"
#include "vtkLODActor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"

#include "textDisplay.h"
#include "grid3D.h"
#include "visibility.h"
#include "topography.h"
#include "controls.h"


extern vtkRenderWindow *renWin;
extern vtkDataSetMapper *cellClusterMapper;    
extern vtkDataSetMapper *dataClusterMapper;    
//extern vtkLODActor *globeActor;

// Coarse grid actor
extern vtkLODActor *plotActor;

// Fine grid actor
//extern vtkLODActor *gridActor;


// Cells adjacent to and including picked cell
extern vtkLODActor *cellClusterActor;
extern vtkUnstructuredGrid *cellCluster;
extern vtkLODActor *dataClusterActor;
extern vtkUnstructuredGrid *dataCluster;

extern grid3D *POPgrid;
extern textDisplay *myText;
extern visibility *seeActors;
extern topography *colorDepths;

extern vtkRenderer *renderer;
extern vtkRenderWindowInteractor *iren;

extern float highMagnification, zoom;

extern controls *myControls;

// Implement a structure to save elevations and indices for undo
class ElevationValue
{
  public:
    long Id;
    long Index;
    float Elevation;
    ElevationValue* Next;

    void PrintSelf(int marker)
    {
        if (this != (ElevationValue *)0)
        {
            cout << marker << " ElevationValue: Id " << this->Id
                 << ", Index " << this->Index
                 << ", Elevation " << this->Elevation
                 << ", Next " << this->Next << endl;
        }
        else
        {
            cout << marker << " Null pointer" << endl;
        }
    }
};
class ElevationValueList
{
  public:
    ElevationValue* Last;

    void Push(long AnId, long AnIndex, float AnElevation)
    {
        // Upon entry, the Last pointer will always be defined,
        // but the definition can be the null pointer.
        //cout << "Entering Push with args " << AnId << " ";
        //cout << AnIndex << " " << AnElevation << endl;
        //this->Last->PrintSelf(1);
        //cout << "Last is " << this->Last << endl;
        ElevationValue* Previous = Last; // save pointer to current top
        this->Last = new ElevationValue; // now safe to reuse Last
        this->Last->Id = AnId;
        this->Last->Index = AnIndex;
        this->Last->Elevation = AnElevation;
        this->Last->Next = Previous; // preserves pointer to predecessor
        //cout << "Last is now " << this->Last << endl;
        //cout << "Leaving Push; " << endl;
        //this->Last->PrintSelf(2);
        //cout << endl;
    }
    void Pull(long &LastId, long &LastIndex, float &LastElevation)
    {
        //cout << "Entering Pull; " << flush;
        //this->Last->PrintSelf(3);
        //cout << "Last was " << this->Last << endl;
        ElevationValue* Current = Last->Next;
        LastId = Last->Id;
        LastIndex = Last->Index;
        LastElevation = Last->Elevation;
        //delete (Last);
        Last = Current;
        //cout << "Last is now " << this->Last << endl;
        //cout << "Leaving Pull; " << flush;
        //cout << "Pull return values are " << LastId << ", " << LastIndex;
        //cout  << ", " << LastElevation << endl;
        //this->Last->PrintSelf(4);
    }
    ElevationValueList()
    {
        Last = (ElevationValue* )0;  // null pointer, other fields undefined
    }
    ~ElevationValueList()
    {
       long Id, Index;
       float Elevation;
       while (Last != (ElevationValue* )0) Pull(Id,Index,Elevation);
    }
};

ElevationValueList *savedElevationChanges;

int Yes(char* prompt)
{
    const int ltmp = 8;
    
    char yesOrNo[ltmp];
    int itmp;
    for (itmp=0; itmp<ltmp; itmp++)
                yesOrNo[itmp] = 0;

    cout << prompt << flush;

    cin.setf(ios::skipws);
    cin.width(1);
    cin.getline(yesOrNo,ltmp);

    if (cin)
    {
        if (yesOrNo[0] == 'n' || (yesOrNo[0] == 'N'))
        {
             return 0;
        }
        else if (yesOrNo[0] == 'y' || (yesOrNo[0] == 'Y'))
        {
             return 1;
        }
    }

    cout << "You typed " << yesOrNo << endl;
    cin.clear();
    cout << "   Defaulting to NO\n" << flush;
    return 0;
}

void FillElevationToLand (long pointId, long cellId, float land)
{
    // cellElevation determines color, pointElevation is used for averages
    float oldCellElevation = POPgrid->GetCellElevation(cellId);
    //cout  << "FillElevationToLand was called with Cell Id " << cellId;
    //cout  << ", for a cell with an elevation of ";
    //cout  << " " << oldCellElevation;
    //cout  << " (POPgrid->GetCellElevation(" << cellId << "))" << endl;

    float oldPointElevation = POPgrid->GetPointElevation(pointId);
    //cout  << "FillElevationToLand was called with Point Id ";
    //cout  << " " << pointId << ", for a point with an elevation of ";
    //cout  << " " << oldPointElevation;
    //cout  << " (POPgrid->GetPointElevation(" << pointId << "))" << endl;

    // Change elevation to a value indicating land
    //cout << "Filling elevation " << oldCellElevation
    //     << " to land in cell " << cellId << endl;

    // First, set this cell's elevation,
    // then find an adjacent cell
    // whose elevation is not already land
    // and recursively call this procedure again.
    //cout  << "Calling Push with args " << pointId << " " << cellId;
    //cout  << " " << POPgrid->GetCellElevation(cellId);
    //cout  << " (POPgrid->GetCellElevation(" << cellId << "))" << endl;

    savedElevationChanges->Push(pointId, cellId,
        POPgrid->GetCellElevation(cellId));

    //cout  << "SAVED ELEVATION: ";
    //cout  << savedElevationChanges->Last->Elevation << ", ";
    //cout  << savedElevationChanges->Last->Index << ", ";
    //cout  << savedElevationChanges->Last->Id << endl;

    POPgrid->SetPointElevation(land, pointId);
    POPgrid->SetCellElevation(land, cellId);
    POPgrid->ResetScalarMapper();
    //colorDepths->UpdateNow(POPgrid);

    // For each adjacent cell, call again.

    // List of points belonging to current cell
    vtkIdList *ptIds = vtkIdList::New();
    ptIds->Allocate(12);

    // List for cells belonging to each point in the above list of points
    vtkIdList *cellIds = vtkIdList::New();
    cellIds->Allocate(12);

    vtkPolyData *output = POPgrid->GetOutput();
    // Put all points belonging to current cell into list of points
    output->GetCellPoints(cellId, ptIds);
    int numberOfPtIds = ptIds->GetNumberOfIds(); // Number of points listed
    int i, j, k;

//  Five-point stencil
//  Want all pairs of points without regard to order.  We are not
//  guaranteed any particular order in the list.
    vtkIdList *neighborCellIds = vtkIdList::New();
    vtkIdList *pairPtIds = vtkIdList::New();
    for (i=0; i<numberOfPtIds-1; i++)
    {
        //cout << "I = " << i << endl;
        for (j=i+1; j<numberOfPtIds; j++)
        {
            //cout << "    J = " << j << endl;
            if (i != j)
            {
                long iPtId = ptIds->GetId(i);
                long jPtId = ptIds->GetId(j);
                pairPtIds->InsertNextId(iPtId);
                pairPtIds->InsertNextId(jPtId);
                output->GetCellNeighbors(cellId, pairPtIds, neighborCellIds);
                int numberOfNeighborCellIds = neighborCellIds->GetNumberOfIds();
                for (k=0; k<numberOfNeighborCellIds; k++)
                {
                    long kCellId = neighborCellIds->GetId(k);
                    //cout << "        K = " << k << ", neighbor = ";
                    //cout << kCellId << endl;
                    neighborCellIds->DeleteId(kCellId);

                    long potentialCellIndex =
                         POPgrid->cellIndex[kCellId].I
                       + POPgrid->iNumPts * POPgrid->cellIndex[kCellId].J;
                    float potentialCellElevation = 
                         POPgrid->GetCellElevation(potentialCellIndex);
                    if (potentialCellElevation != land)  // Ocean cell
                    {
                        FillElevationToLand(kCellId, potentialCellIndex, land);
                    }
                }
                pairPtIds->DeleteId(iPtId);
                pairPtIds->DeleteId(jPtId);
            }
        }
    }
    pairPtIds->Delete();
    neighborCellIds->Delete();

/*  Nine-point stencil
    for (i=0; i<numberOfPtIds; i++)
    {
        long ptId = ptIds->GetId(i);
        output->GetPointCells(ptId, cellIds);
        int numberOfCellIds = cellIds->GetNumberOfIds();

        for (j=0; j<numberOfCellIds; j++)
        {
            long potentialCellId = cellIds->GetId(j);
            long potentialCellIndex = POPgrid->cellIndex[potentialCellId].I
                 + POPgrid->iNumPts * POPgrid->cellIndex[potentialCellId].J;
            float potentialCellElevation = 
                             POPgrid->GetCellElevation(potentialCellIndex);
            if (potentialCellElevation != land)  // Ocean cell
            {
                FillElevationToLand(potentialCellId, potentialCellIndex);
            }
        }
    }
*/
    ptIds->Delete();
    cellIds->Delete();
    return;
}

void PickCells(void *arg)
{
    const int length = 255;

    //Helps map elevation on sphere used primarily in pick.cxx
    vtkPolyData *gridOutput = POPgrid->GetOutput();

    char buffer[length];    //buffer will hold the text that is displayed
    ostrstream output(buffer,length,ios::out);
    
    long i, j, k;
    
    vtkIdList *cellClusterIds = vtkIdList::New();
    cellClusterIds->Allocate(128);
    
    vtkIdList *potentialCellClusterIds = vtkIdList::New();
    potentialCellClusterIds->Allocate(16);
   
    // pickedPtIds will hold ids of all of the picked cell's boundary points
    vtkIdList *pickedPtIds = vtkIdList::New();
    pickedPtIds->Allocate(12);
    
    // ptIds will temporarily hold ids of adjacent cell's boundary points  
    vtkIdList *ptIds = vtkIdList::New();
    ptIds->Allocate(12);
#pragma omp parallel
#pragma omp master
{
        //cout << "This is the master thread " << endl;
}
#pragma omp for
    for (i=0; i<length; i++)
    {
        buffer[i] = 0;  // Clear text string of old picked cell data
    }
    
    vtkRenderWindowInteractor *iren2 = (vtkRenderWindowInteractor *)arg; //why a new object??
    
    vtkCellPicker *picker = (vtkCellPicker *)iren2->GetPicker();
    picker->SetTolerance(0.001);
    
    float *worldPt = picker->GetPickPosition();

    //output << "World & Map Coordinates: "<< worldPt[0] << ", "
    //    << worldPt[1] << ", " << worldPt[2] << endl;
    
    float v[3];     // View Coordinates
    for (i=0; i<3; i++)  // Set view coordinates equal to world coordinates
    {
        v[i] = worldPt[i];
    }
    
    renderer->WorldToView(v[0],v[1],v[2]);
    //output << "View Coordinates: "<< v[0] << ", "
    //    << v[1] << ", " << v[2] << endl;
    
    float *selectionPt = picker->GetSelectionPoint(); // Display Coords (pixels)
    //output << "Display Pixel: " << selectionPt[0] 
    //    << ", " << selectionPt[1] << endl;
    long pickedCellId = picker->GetCellId();
    
    //output.flush();
    //cout << '\n' << buffer;
    
    long pickedCellIndex = -9999;

    savedElevationChanges = new ElevationValueList;
    bool checkUndo = false;
    myText->GetText()->VisibilityOff();
    if ( pickedCellId >= 0 ) // If valid pick
    {
        // Print debugging information
        output << "Picked Cell's ID: ";
        output << pickedCellId << endl;
        output << "   I = " << POPgrid->cellIndex[pickedCellId].I;
        output << ", J = " << POPgrid->cellIndex[pickedCellId].J << endl;
        output  << "Picked Cell's elevation: ";
        output  << POPgrid->GetCellElevation(pickedCellId) << endl;
        output  << "Picked Cell's longitude: ";
        output  << POPgrid->cellTheta[pickedCellId] << endl;
        output  << "Picked Cell's latitude: ";
        output  << POPgrid->cellPhi[pickedCellId] << endl;
        output.flush();

        pickedCellIndex = POPgrid->cellIndex[pickedCellId].I
             + POPgrid->iNumPts * POPgrid->cellIndex[pickedCellId].J;

        // Set up the target cell (red)
        cellClusterActor->VisibilityOn();

        cellCluster->Initialize();
        cellCluster->Allocate(16);
        cellCluster->SetPoints(gridOutput->GetPoints());

        cellClusterIds->InsertNextId(pickedCellId);
        
        // Set up the ring around the target (green)
        dataCluster->Initialize();
        dataCluster->Allocate(200);
        dataCluster->SetPoints(gridOutput->GetPoints());
        dataCluster->InsertNextCell(gridOutput->GetCellType(pickedCellId), ptIds);
        
        // Put target cell's points into list ( 4 most places, 3 around poles)
        gridOutput->GetCellPoints(pickedCellId, pickedPtIds);
        
        // For display purposes it is useful to list the cluster of cells around
        // the picked cell.  Picked cell is first entry (number 0) in the list.
        long numberOfIds = pickedPtIds->GetNumberOfIds();
        for (i=0; i < numberOfIds; i++) // For each point of cell "pickedCellId"
        {
            // display all cells sharing that point
            long pickedPtId = pickedPtIds->GetId(i); // First, get point's ID
            dataClusterMapper->Update();

            // Next, get world coordinates of the picked point
            float *ap = gridOutput->GetPoint(pickedPtId);
            
            // Add this point's cell IDs to the output list
            // First, get those cell IDs from the picked point
            gridOutput->GetPointCells(pickedPtId, potentialCellClusterIds);
                
            long numberOfPotentialCellClusterIds =  // # unique cell IDs so far
                potentialCellClusterIds->GetNumberOfIds();
    
            // Check all cells bounded by this point
            for (j=0; j<numberOfPotentialCellClusterIds; j++)
            {
                long potentialCellClusterId = potentialCellClusterIds->GetId(j);
    
                // Put cell's points into list
                gridOutput->GetCellPoints(potentialCellClusterId, ptIds);
           
                long n = 0;
                long match = 0;
                long numberOfCells = dataCluster->GetNumberOfCells();
                for (k=0; k<numberOfCells; k++)
                {
                    n = cellClusterIds->GetId(k);
                    if (potentialCellClusterId == n) match = 1;
                }
                    
                if (match == 0)
                {
                    cellClusterIds->InsertNextId(potentialCellClusterId);
                    dataCluster->InsertNextCell
                       (gridOutput->GetCellType(potentialCellClusterId), ptIds);
                }
                //else
                //{
                //    cout << " was a duplicate" << endl;
                //}
            } // end for j
        } // end for i
        
        // With the following commented out, the target cell does
        // not display in red.  Why?  (Don't know, but deleted
        // previous call and kept this one.)
        cellCluster->InsertNextCell
        (
            gridOutput->GetCellType(pickedCellId),
            pickedPtIds
        );

        //cout << "Number of cells in cluster is ";
        //cout << cellCluster->GetNumberOfCells() << endl; // Debugging
        //cout << endl;
        
        //cout << "Number of cells around target cell is ";
        //cout << dataCluster->GetNumberOfCells()-1 << endl; // Debugging
        //cout << endl;
        
        zoom = 1.0;
        renderer->GetActiveCamera()->Zoom(zoom);
        
        myText->GetTextMapper()->SetInput(buffer);
        //myText->GetText()->SetPosition(worldPt);
        
        // Make cells and grid visible
        cellClusterActor->GetProperty()->SetOpacity(1.0);
        cellClusterActor->VisibilityOn();

        dataClusterActor->GetProperty()->SetOpacity(0.5);
        dataClusterActor->VisibilityOn();
        float dataPt[3];
        cellClusterActor->GetPosition(dataPt);


        for (i=0; i<3; i++)
        {
            dataPt[i] = worldPt[i];
        }

        dataClusterActor->GetProperty()->SetRepresentationToSurface();

        myText->GetText()->VisibilityOn();
/*
        // This render action paints the bullseye before the editing dialog
        if (Yes("Paint bullseye? [n]:"))
        {
            cout << "Painting bullseye" << endl;
            renWin->Render();
        }
*/
        const int ltmp = 8;

        float oldCellElevation = POPgrid->GetCellElevation(pickedCellId);
        if (myControls->ctrlClick==true)
        {
            //cerr << "Click state in pick" << endl;
            //cerr << "shift-click " << myControls->shiftClick << endl;
            //cerr << "ctrl-click " << myControls->ctrlClick << endl;
			float landValue = 9999.0;
            cout << "ENTER LAND VALUE: " << flush;
			cin >> landValue;
			cout << " You entered " << landValue << endl;
			FillElevationToLand(pickedCellId, pickedCellIndex, landValue);
            myControls->shiftClick = false;
            myControls->ctrlClick = false;
            checkUndo = true;
        }
        else if (myControls->shiftClick==true)
        {            
            //cerr << "shift-click state in pick" << endl;
            //cerr << "shift-click " << myControls->shiftClick << endl;
            //cerr << "ctrl-click " << myControls->ctrlClick << endl;
            // Crude editing capability
            cout << "Current cell elevation " << oldCellElevation << endl;
            float newElevation = -9999.9;
            if (Yes("Change elevation? [n]:"))
            {
                savedElevationChanges->Push(pickedCellId, pickedCellIndex,
                    POPgrid->GetCellElevation(pickedCellIndex));

                //cout  << "SAVED ELEVATION: ";
                //cout  << savedElevationChanges->Last->Elevation << ", ";
                //cout  << savedElevationChanges->Last->Index << ", ";
                //cout  << savedElevationChanges->Last->Id << endl;

                cout << "ENTER NEW ELEVATION: " << flush;
                cin >> newElevation;
                cout << " You entered " << newElevation << endl;
                POPgrid->SetPointElevation(newElevation, pickedCellId);
                POPgrid->SetCellElevation(newElevation, pickedCellIndex);
                cin.ignore(ltmp, '\n');
                cin.clear();
                POPgrid->ResetScalarMapper();
            }
            myControls->shiftClick = false;
            myControls->ctrlClick = false;
            checkUndo = true;
        }
        else
        {
            //cerr << "Click in pick" << endl;
            //cerr << "shift-click " << myControls->shiftClick << endl;
            //cerr << "ctrl-click " << myControls->ctrlClick << endl;
            myControls->shiftClick = false;
            myControls->ctrlClick = false;
            checkUndo = false;
        } //end of shiftclick else
    }
    else
    {
        //renderer->GetActiveCamera()->Elevation(0.0);
        //renderer->GetActiveCamera()->Azimuth(0.0);

        zoom = 1.0;
        renderer->GetActiveCamera()->Zoom(zoom);
        
        cellClusterActor->GetProperty()->SetOpacity(0.0);
        cellClusterActor->VisibilityOff();

        dataClusterActor->GetProperty()->SetOpacity(0.0);
        dataClusterActor->GetProperty()->SetRepresentationToWireframe();
        dataClusterActor->VisibilityOn();
        //sphereActor->VisibilityOff();
        //gridActor->GetProperty()->SetColor(1.0,0.0,0.0);
        //gridActor->VisibilityOn();
        //globeActor->VisibilityOn();
        checkUndo = false;

    }
    //seeActors->UpdateNow();
    cellClusterActor->VisibilityOff();
    dataClusterActor->VisibilityOff();
/*
    if (Yes("Refresh to show changes? [n]"))
    {
        //cout << "Refreshing to show all changes..." << flush;
        renWin->Render();
        //cout << endl;
    }
*/
    if (checkUndo)
    {
        if (Yes("Undo last set of changes? [n]"))
        {
            //cout << "Undoing last set of changes..." << flush;
            while (savedElevationChanges->Last != (ElevationValue *)0)
            {
                //cout << "Pulling last change..." << flush;
                long anId, anIndex;
                float anElevation;
                savedElevationChanges->Pull(anId, anIndex, anElevation);
                //cout << "\nRestoring elevation " << anElevation << " to index ";
                //cout << anIndex << " where id was " << anId << "..." << flush;
                POPgrid->SetPointElevation(anElevation, anId);
                //cout << "\npoint elevation done..." << endl;
                POPgrid->SetCellElevation(anElevation, anIndex);
                //cout << "\ncell elevation done" << endl;
            }
        }
        else
        {
            cout << "Changes will be permanent" << endl;
        }
    }
    POPgrid->ResetScalarMapper();
    //colorDepths->UpdateNow(POPgrid);
    cout << "\nRerendering image..." << flush;
    renWin->Render();
    cout << "done" << endl;
    potentialCellClusterIds->Delete();
    ptIds->Delete();
    pickedPtIds->Delete();
    delete (savedElevationChanges);
}


