/*****************************************************************************
*
* Copyright (c) 2000 - 2018, Lawrence Livermore National Security, LLC
* Produced at the Lawrence Livermore National Laboratory
* LLNL-CODE-442911
* All rights reserved.
*
* This file is  part of VisIt. For  details, see https://visit.llnl.gov/.  The
* full copyright notice is contained in the file COPYRIGHT located at the root
* of the VisIt distribution or at http://www.llnl.gov/visit/copyright.html.
*
* 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 disclaimer below.
*  - Redistributions in binary form must reproduce the above copyright notice,
*    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
*    documentation and/or other materials provided with the distribution.
*  - Neither the name of  the LLNS/LLNL nor the names of  its contributors may
*    be used to endorse or promote products derived from this software without
*    specific prior written permission.
*
* 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 LAWRENCE  LIVERMORE NATIONAL  SECURITY,
* LLC, THE  U.S.  DEPARTMENT OF  ENERGY  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.
*
*****************************************************************************/

// ************************************************************************* //
//  File: avtLineSurfaceFilter.C
// ************************************************************************* //

#include <avtLineSurfaceFilter.h>

#include <avtCurveConstructorFilter.h>
#include <avtExtents.h>
#include <avtLineoutFilter.h>
#include <avtSourceFromAVTDataset.h>

#include <vtkCellArray.h>
#include <vtkDataArray.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkRectilinearGrid.h>


// ****************************************************************************
//  Method: avtLineSurfaceFilter constructor
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Jan 23 11:30:14 PDT 2008
//
// ****************************************************************************

avtLineSurfaceFilter::avtLineSurfaceFilter()
{
}


// ****************************************************************************
//  Method: avtLineSurfaceFilter destructor
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Jan 23 11:30:14 PDT 2008
//
//  Modifications:
//
// ****************************************************************************

avtLineSurfaceFilter::~avtLineSurfaceFilter()
{
}


// ****************************************************************************
//  Method:  avtLineSurfaceFilter::Create
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Jan 23 11:30:14 PDT 2008
//
// ****************************************************************************

avtFilter *
avtLineSurfaceFilter::Create()
{
    return new avtLineSurfaceFilter();
}


// ****************************************************************************
//  Method:      avtLineSurfaceFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Jan 23 11:30:14 PDT 2008
//
// ****************************************************************************

void
avtLineSurfaceFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const LineSurfaceAttributes*)a;
    SetTimeLoop(atts.GetStartTime(), atts.GetEndTime(), atts.GetStride());
}


// ****************************************************************************
//  Method: avtLineSurfaceFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtLineSurfaceFilter with the given
//      parameters would result in an equivalent avtLineSurfaceFilter.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Jan 23 11:30:14 PDT 2008
//
// ****************************************************************************

bool
avtLineSurfaceFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(LineSurfaceAttributes*)a);
}


// ****************************************************************************
//  Method: avtLineSurfaceFilter::Execute
//
//  Purpose:
//      Executes on the current time slice.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Jan 23 11:30:14 PDT 2008
//
// ****************************************************************************

void
avtLineSurfaceFilter::Execute(void)
{
    avtDataObject_p dObj = GetInput();
    avtDataset_p ds;
    CopyTo(ds, dObj);
    avtSourceFromAVTDataset termsrc(ds);
    avtDataObject_p data = termsrc.GetOutput();

    avtLineoutFilter lineouter;
    lineouter.SetInput(data);
    lineouter.SetPoint1(atts.GetPoint1());
    lineouter.SetPoint2(atts.GetPoint2());
    
    avtCurveConstructorFilter ccf;
    ccf.SetInput(lineouter.GetOutput());

    avtContract_p specForDB = GetGeneralContract();
    ccf.Update(specForDB);

    avtDataObject_p ccfOutput = ccf.GetOutput();
    avtDataset_p ccfDSOutput;
    CopyTo(ccfDSOutput, ccfOutput);
    avtDataTree_p tree = ccfDSOutput->GetDataTree();
    vtkDataSet *line = tree->GetSingleLeaf();
    line->Register(NULL);
    lineouts.push_back(line);
}


// ****************************************************************************
//  Method: avtLineSurfaceFilter::CreateFinalOutput
//
//  Purpose:
//      Takes the existing lineouts and stitches them together.
//
//  Programmer: Hank Childs
//  Creation:   January 24, 2008
//
//  Modifications:
//
//    Hank Childs, Thu Aug 26 13:47:30 PDT 2010
//    Change extents names.
//
// ****************************************************************************

void
avtLineSurfaceFilter::CreateFinalOutput(void)
{
    unsigned int  i, j;
    unsigned int  nLineouts = lineouts.size(); 

    if (nLineouts <= 1)
        return;

    unsigned int npts = 0;
    for (i = 0 ; i < nLineouts ; i++)
    {
        npts += lineouts[i]->GetNumberOfPoints();
    }

    // Set up the points for the output polydata.
    vtkPoints   *pts = vtkPoints::New();
    pts->SetNumberOfPoints(npts);
    vtkPolyData *pd  = vtkPolyData::New();
    pd->GetPointData()->CopyAllocate(lineouts[0]->GetPointData(), npts);
    unsigned int numSoFar = 0;
    for (i = 0 ; i < nLineouts ; i++)
    {
        unsigned int nptsForI = lineouts[i]->GetNumberOfPoints();
        for (j = 0 ; j < nptsForI ; j++)
        {
            double pt[3];
            lineouts[i]->GetPoint(j, pt);
            pt[1] = lineouts[i]->GetPointData()->GetScalars()->GetTuple1(j);
            pt[2] = (double) i;
            pts->SetPoint(numSoFar, pt);
            pd->GetPointData()->CopyData(lineouts[i]->GetPointData(), j, numSoFar);
            numSoFar++;
        }
    }
    pd->SetPoints(pts);
    pts->Delete();

    // Set up connectivity between the lineouts.
    int numTris = 0;
    int numQuads = 0;
    for (i = 0 ; i < nLineouts-1 ; i++)
    {
        int npts1 = lineouts[i]->GetNumberOfPoints();
        int npts2 = lineouts[i+1]->GetNumberOfPoints();
        int max = (npts1 > npts2 ? npts1 : npts2);
        int min = (npts1 < npts2 ? npts1 : npts2);
        numQuads += min-1;
        numTris  += (max-min);
    }
    vtkCellArray *polys = vtkCellArray::New();
    polys->Allocate(numQuads*(4+1) + numTris*(3+1));
    int idxForLine1 = 0;
    for (i = 0 ; i < nLineouts-1 ; i++)
    {
        int npts1 = lineouts[i]->GetNumberOfPoints();
        int npts2 = lineouts[i+1]->GetNumberOfPoints();
        int idxForLine2 = idxForLine1 + npts1;
        int min = (npts1 < npts2 ? npts1 : npts2);
        for (j = 0 ; j < min-1 ; j++)
        {
            vtkIdType quad[4];
            quad[0] = idxForLine1+j;
            quad[1] = idxForLine1+j+1;
            quad[2] = idxForLine2+j+1;
            quad[3] = idxForLine2+j;
            polys->InsertNextCell(4, quad);
        }
        int max = (npts1 > npts2 ? npts1 : npts2);
        int numTris = max-min;
        for (j = 0 ; j < numTris ; j++)
        {
            vtkIdType tri[3];
            if (npts1 == max)
            {
                tri[0] = idxForLine1+(min-1)+j;
                tri[1] = idxForLine1+(min-1)+j+1;
                tri[2] = idxForLine2+(min-1);
            }
            else
            {
                tri[0] = idxForLine2+(min-1)+j;
                tri[1] = idxForLine1+(min-1);
                tri[2] = idxForLine2+(min-1)+j+1;
            }
            polys->InsertNextCell(3, tri);
        }

        idxForLine1 = idxForLine2;
    }
    pd->SetPolys(polys);
    polys->Delete();

    double *bounds = pd->GetBounds();
    avtDataAttributes &in_atts  = GetInput()->GetInfo().GetAttributes();
    avtDataAttributes &out_atts = GetOutput()->GetInfo().GetAttributes();
    out_atts.SetTopologicalDimension(2);
    out_atts.SetSpatialDimension(3);
    out_atts.GetOriginalSpatialExtents()->Set(bounds);
    out_atts.GetThisProcsOriginalSpatialExtents()->Set(bounds);
    out_atts.GetDesiredSpatialExtents()->Set(bounds);
    out_atts.GetActualSpatialExtents()->Set(bounds);
    out_atts.GetThisProcsActualSpatialExtents()->Set(bounds);

    out_atts.SetYLabel(pipelineVariable);
    out_atts.SetYUnits(in_atts.GetVariableUnits(pipelineVariable));
    out_atts.SetZLabel("time slice");
    out_atts.SetZUnits("index");

    avtDataTree_p outputTree = new avtDataTree(pd, -1);
    SetOutputDataTree(outputTree);
    pd->Delete();
 
    for (i = 0 ; i < nLineouts ; i++)
        lineouts[i]->Delete();
}


