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

  Program:   Visualization Toolkit
  Module:    eCubed.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.

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

/****************************************************************
 This is the visual driver that puts together all of the other
 files and actually runs the visual editor

  Written by John F. Davis
  Modified by Walter E. Voit
  Los Alamos National Lab
  20 June 2002 16:47:05 

****************************************************************/

//vtkLibrary header files
#include "vtkPointPicker.h"
#include "vtkStructuredGrid.h"
#include "vtkUnstructuredGrid.h"
#include "vtkPolyData.h"
#include "vtkLookupTable.h"
#include "vtkCell.h"
#include "vtkCellPicker.h"
#include "vtkTextMapper.h"
#include "vtkFollower.h"
#include "vtkVectorText.h"
#include "vtkTexturedSphereSource.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 "vtkScalarBarActor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkActor2D.h"

//Not necessary for this version but may need later
//#include "vtkInteractorStyleMyPicker.h"
//#include "vtkInteractorStyleUser.h"
//#include "vtkEarthSource.h"

//custom header files
#include "controls.h"
#include "grid3D.h"
#include "coastlines.h"
#include "textDisplay.h"
#include "topography.h"
#include "visibility.h"
#include "netcdfcpp.h"
#include "gridlines.h"

//Not necessary for this version but may need later
//#include "mySpherePlotter.h"  // Include for coarse subgrid
//#include "mySphereSource.h"  // Include only if customized

//C++ header files
#include <iostream.h>
#include <fstream.h>
#include <string.h>


extern void PickCells(void *);

/*************** Declaration of global objects *********************/

vtkDataSetMapper *cellClusterMapper;    
vtkDataSetMapper *dataClusterMapper;    

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

// Cells used for separate data dialogue
vtkLODActor *dataClusterActor;
vtkUnstructuredGrid *dataCluster;

// creates underlying sphere
vtkLODActor *globeActor;

//creates the color-mapped elevation cells across the grid
grid3D *POPgrid;

//the necessary rendering windows
vtkRenderWindow *renWin;
vtkRenderer *renderer;
vtkRenderWindowInteractor *iren;

//custom objects
textDisplay *myText;        //displays text on picks of cells
topography *colorDepths;    //shows ocean depths in sliding color scale
coastlines *presentEarth;   //make continental outlines visible (vtkEarthSource)
visibility *seeActors;      //
controls *myControls;
gridlines *horizGrid;

vtkLODActor* aGridActor;
vtkLODActor* aVariableActor;
vtkLODActor* aCoastalActor;
vtkScalarBarActor* aScalarBarActor;

char* dataVar;

/************ Declaration of global variables ******************/

const char* const program_name = "eCubed";
const char* const program_version = " version 2.5 (2/19/2003)";

//used by pick.cxx
float zoom = 1.0;
float highMagnification = 2.0;

//error catching: maybe can be deleted later
inline void usage(int exit_value = 0)
{
    cerr << "usage:\n" << program_name 
         << " [-i input.nc] [-o output.nc]" << endl;
    exit(exit_value);
}

int main( int argc, char *argv[] )
{

    
    extern int Yes(char* prompt);
    float red, green, blue;
    const float colorBase = 256;
    bool ifile_on = false;
    char* ifile = "e.nc";
    bool ofile_on = false;
    char* ofile = "e.nc";
    int argn;

  cerr << program_name << program_version << endl;

    //only used when command line args specified
    for (argn=1; argn<argc; argn++)
    {
        char* pchar = argv[argn];
        switch (pchar[0])
        {
        case '-':
                if (pchar[1] == 'i')
                {
                    ifile_on = true;
                }
                else if (pchar[1] == 'o')
                {
                    ofile_on = true;
                }
                else
                {
                    cerr << program_name << " : error : "
                        << "unrecognized option : "
                        << pchar << "\n\n";
                    usage(-1);
                }
                break;
        default:  // file name
            if (ifile_on)
            {
                ifile_on = false;
                ifile = pchar;
                cout << "Input file: " << ifile << ' ' << endl;
            }
            if (ofile_on)
            {
                ofile_on = false;
                ofile = pchar;
                cout << "Output file: " << ofile << ' ' << endl;
            }
            break;
        }
    }
    /******************* Visualization Model ***********************/

    // Interface between visualization pipeline and graphics model
    // (transforms information into graphical data)
    //---------------------------------------------------------------

    // cellCluster 
    cellCluster = vtkUnstructuredGrid::New();
    cellClusterActor = vtkLODActor::New(); 
    vtkShrinkFilter *shrink = vtkShrinkFilter::New();
    cellClusterMapper = vtkDataSetMapper::New();

    shrink->SetInput(cellCluster);
    shrink->SetShrinkFactor(1.0);

    cellClusterMapper->SetInput(shrink->GetOutput());

    cellClusterActor->SetMapper(cellClusterMapper);
    cellClusterActor->PickableOff();
    cellClusterActor->VisibilityOff();
    red = 255/colorBase; green = 1/colorBase; blue = 1/colorBase;
    cellClusterActor->GetProperty()->SetColor(red,green,blue);
    
    
    //dataCluster defines 8 grid region around picked cell
    dataCluster = vtkUnstructuredGrid::New();
    dataClusterActor = vtkLODActor::New();
    vtkShrinkFilter *dataShrink = vtkShrinkFilter::New();
    dataClusterMapper = vtkDataSetMapper::New();

    dataShrink->SetInput(dataCluster);
    dataShrink->SetShrinkFactor(1.0);

    dataClusterMapper->SetInput(dataCluster);

    dataClusterActor->SetMapper(dataClusterMapper);
    dataClusterActor->PickableOff();
    dataClusterActor->VisibilityOff();
    red = 255/colorBase; green = 1/colorBase; blue = 1/colorBase;
    dataClusterActor->GetProperty()->SetColor(red,green,blue);

    
    /******************** making underlying globe ************************/
    
    vtkSphereSource *globe = vtkSphereSource::New();
    vtkPolyDataMapper *globeMapper = vtkPolyDataMapper::New();
    globeActor = vtkLODActor::New();   

    globe->SetThetaResolution(72); globe->SetPhiResolution(36);
    globe->SetRadius(0.999);

    globeMapper->SetInput(globe->GetOutput());

    globeActor->SetMapper(globeMapper);// Sphere Actor has-a Mapper
    globeActor->GetProperty()->SetOpacity(1.0);
    red = 256/colorBase; green = 256/colorBase; blue = 256/colorBase;
    //red = 1.0; green = 1.0; blue = 1.0;
    globeActor->GetProperty()->SetColor(red,green,blue);
    globeActor->GetProperty()->SetRepresentationToSurface();
    globeActor->PickableOff();
    globeActor->VisibilityOn();


    /*********************** coastlines object **************************/

    presentEarth = new coastlines();
    presentEarth->MakeOutlines();

    
    /************************** text object *****************************/

    myText = new textDisplay();
    myText->MakeText();


    /**************** grid3D (horizontal grid) object ********************/

    //calls on constructor to set up variables
    POPgrid = new grid3D(4); 

    dataVar = "elevation";
    char* xVar    = "ULON";
    char* yVar    = "ULAT";
/* Experimental code */
    // This series of prompts is tedious for the interactive user,
    // but it is really intended for interaction with the Expectk GUI
    // gets info from netCDF file

    char dataBuf[80], xBuf[80], yBuf[80];

    char *prompt = "Please enter the name of the data variable [elevation]: ";
    cin.setf(ios::skipws);
    cout << prompt << flush;
    cin.getline(dataBuf,sizeof(dataBuf));
    if ((cin) && (strlen(dataBuf) > 0))
    {
        cout << " You entered " << strlen(dataBuf) << ' ' << dataBuf << endl;
        dataVar = dataBuf;
        cin.clear();
    }
    else
    {
        cout << " Defaulting to elevation" << endl;
        dataVar = "elevation";
        cin.clear();
    }

    prompt = "Please enter the name of the X variable [ULON]: ";
    cin.setf(ios::skipws);
    cout << prompt << flush;
    cin.getline(xBuf,sizeof(xBuf));
    if ((cin) && (strlen(xBuf) > 0))
    {
        cout << " You entered " << strlen(xBuf) << ' ' << xBuf << endl;
        xVar = xBuf;
        cin.clear();
    }
    else
    {
        cout << " Defaulting to ULON" << endl;
        xVar = "ULON";
        cin.clear();
    }

    prompt = "Please enter the name of the Y variable [ULAT]: ";
    cin.setf(ios::skipws);
    cout << prompt << flush;
    cin.getline(yBuf,sizeof(yBuf));
    if ((cin) && (strlen(yBuf) > 0))
    {
        cout << " You entered " << strlen(yBuf) << ' ' << yBuf << endl;
        yVar = yBuf;
        cin.clear();
    }
    else
    {
        cout << " Defaulting to ULAT" << endl;
        yVar = "ULAT";
        cin.clear();
    }

    POPgrid->ReadElevationFromFile(ifile,dataVar,xVar,yVar);
    
    //made these changes to the constructor; no need to do it twice!!
    POPgrid->OnRatio=1;
    POPgrid->Radius=1.002;

    /************************* updating objects **************************/

    //updates the color mapping stuff
    colorDepths = new topography();
    colorDepths->UpdateNow(POPgrid);
    
    //re-renders the scene and all of the actors that change ---needs fixing---
    seeActors = new visibility();


    
    /******************** fine and coarse grids ******************************/

    horizGrid = new gridlines();
    horizGrid->MakeGrid(POPgrid);


    /******************* Graphics Model ********************************/

    // Interface between graphics engine and windowing system
    // (transforms graphical data into pictures)
    //------------------------------------------------------------------

    //NOW two different styles: left click picker(myControls)
    //                      and arrow keys(istyle)
    myControls = controls::New();

    iren->SetInteractorStyle(myControls);//Using custom controls of left clicker

    //need description of cell picker
    vtkCellPicker *picker = vtkCellPicker::New();
    picker->SetTolerance(0.001);
    iren->SetPicker(picker);

    renWin->AddRenderer(renderer);// A Window has a Renderer
    iren->SetRenderWindow(renWin);// An Interactor has a Window
    renderer->SetBackground(0.69,0.69,0.69); //(1.0,1.0,1.0);
    
    //List of other rendered Actors
    renderer->AddActor(globeActor);  // Solid globe 

    renderer->AddActor(dataClusterActor);
    renderer->AddActor(cellClusterActor);
    
    //if(horizGrid->GetFineGrid() )
        //aGridActor = horizGrid->GetGrid();
        //renderer->AddActor(aGridActor);
        renderer->AddActor(horizGrid->GetGrid() );      
    //else
    //    renderer->AddActor(horizGrid->GetPlot() );  // Displays red partial grid 

    aCoastalActor = presentEarth->GetLandShapes();
    renderer->AddActor(aCoastalActor);
    //renderer->AddActor(presentEarth->GetLandShapes());  // Continental outlines

    aVariableActor = colorDepths->GetColorFill();
    renderer->AddActor(aVariableActor);
    //renderer->AddActor(colorDepths->GetColorFill());  // Elevation in color

    aScalarBarActor = colorDepths->GetScalarBar();
    renderer->AddActor(aScalarBarActor);

    renderer->AddActor(myText->GetText());

    //vtkCamera *camera = renderer->GetActiveCamera();

/****************************************************************************
For some reason unknown to me this when renderer->GetActiveCamera() is called
it also calls grid3D::ExecuteInformation() and grid3D::Execute.  Maybe this
is comiler optimization or maybe this is actually where the execution should
take place.  I don't know.  
*****************************************************************************/

        myControls->OriginalSettings();
/*        camera->SetViewUp(0.0, 1.0, 0.0);
        camera->OrthogonalizeViewUp();
        camera->SetPosition(6.0, 0.0, 0.0);
        camera->Roll(-90);
        camera->Azimuth(-30);
        camera->Elevation(60);
        camera->Dolly(1.0);
        camera->Zoom(1.5);
*/        


    // interact with data
    cout << "Earth Window: " << earthWindowX << " x " << earthWindowY << endl;
    renWin->SetSize(earthWindowX,earthWindowY);
    renWin->SetDesiredUpdateRate(1.0); // frames per second

    // 'update' (asking for graphical data).  Actors send 'update' to
    // Mappers (so they will produce graphical data).  Mappers send
    // 'update' to their input (filter or source) in order to get the
    // needed information..

    // Before (during?) this call, need to update CELL elevations
    //seeActors->UpdateNow(); //calls many of the renderers

    iren->SetEndPickMethod(PickCells,(void *)iren);

    globeActor->VisibilityOn();
    dataClusterActor->VisibilityOff();
    cellClusterActor->VisibilityOff();
    horizGrid->GetGrid()->VisibilityOn();
    aVariableActor->VisibilityOn();
    aCoastalActor->VisibilityOn();
    //Actor->VisibilityOff();

    iren->Start();

    if(!Yes("Save before exiting? [n]")) exit(0);

    if (!strcmp(ifile,ofile)) // If input filename same as output filename
    {
        cout << "Updating input file " << ifile << endl;
    }
    else // Create (or replace) output filename
    {
        cout << "Creating (or replacing) file " << ofile << endl;
#ifdef MIPS
        ifstream in(ifile, ios::in);
#else
        ifstream in(ifile, ios::in); // | ios::binary);
#endif
        if (!in)
        {
            cout << "Cannot open file " << ifile << endl;
            return 1;
        }
#ifdef MIPS
        ofstream out(ofile, ios::out);
#else
        ofstream out(ofile, ios::out); // | ios::binary);
#endif
        if (!out)
        {
            cout << "Cannot open file " << ofile << endl;
            return 1;
        }

        // Copying is required because the input netCDF file is allowed to
        // have all kinds of other information:  HTE, HTN, etc.  We do not
        // want to make this code dependent on anything but the elevation
        // data and its dimensions.
        cout << "Copying " << ifile << " to new file " << ofile << endl;
        char ch;
        while (in)
        {
             in.get(ch);
             if (in) out.put(ch);
        }
        in.close();
        out.close();
        cout << "Updating new output file " << ofile << endl;
    }

    // Open an existing netCDF file and replace data in the elevation variable
    POPgrid->WriteElevationToFile(ofile, dataVar);

    //Reclaim memory by exiting
    exit(0);
}
