/*****************************************************************************
*
* Copyright (c) 2000 - 2017, 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: avtChannelCommFilter.C
// ************************************************************************* //

#include <avtChannelCommFilter.h>

#include <avtExtents.h>

#include <vtkRectilinearGrid.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>

class avtChannelCommIC : public avtIntegralCurve
{
  public:
    avtChannelCommIC( const avtIVPSolver* model,
                      Direction dir,
                      const double& t_start,
                      const avtVector &p_start,
                      long ID )
        : avtIntegralCurve(model, dir, t_start, p_start, ID)
    {
        last_loc[0] = p_start.x;
        last_loc[1] = p_start.y;
        numSteps = 0;
        dist = 0.;
    }

    avtChannelCommIC()
    {
        dist = 0.;
    }

    double  GetDistance(void)        { return dist; };
    const int *GetIndex(void) { return idx; };
    void   SetIndex(const int *i) {idx[0] = i[0]; idx[1] = i[1]; };

    virtual void AnalyzeStep( avtIVPStep &step, avtIVPField *field)
    {
        avtVector p = step.GetP1();
        dist += sqrt((p.x-last_loc[0])*(p.x-last_loc[0])+
                     (p.y-last_loc[1])*(p.y-last_loc[1]));
        last_loc[0] = p.x;
        last_loc[1] = p.y;
        if (numSteps++ >= 1000)
            status = STATUS_FINISHED;

    }
    virtual void Serialize(MemStream::Mode mode, MemStream &buff,
                                avtIVPSolver *solver)

    {
        avtIntegralCurve::Serialize(mode, buff, solver);
        buff.io(mode, dist);
        buff.io(mode, idx[0]);
        buff.io(mode, idx[1]);
        buff.io(mode, last_loc[0]);
        buff.io(mode, last_loc[1]);
        buff.io(mode, numSteps);
    }

  protected:
    double dist;
    int idx[2];
    double last_loc[2];
    int numSteps;
};



// ****************************************************************************
//  Method: avtChannelCommFilter constructor
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Wed Nov 3 13:27:55 PST 2010
//
// ****************************************************************************

avtChannelCommFilter::avtChannelCommFilter()
{
}


// ****************************************************************************
//  Method: avtChannelCommFilter destructor
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Wed Nov 3 13:27:55 PST 2010
//
//  Modifications:
//
// ****************************************************************************

avtChannelCommFilter::~avtChannelCommFilter()
{
}


// ****************************************************************************
//  Method:  avtChannelCommFilter::Create
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Wed Nov 3 13:27:55 PST 2010
//
// ****************************************************************************

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


// ****************************************************************************
//  Method:      avtChannelCommFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Wed Nov 3 13:27:55 PST 2010
//
// ****************************************************************************

void
avtChannelCommFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const ChannelCommAttributes*)a;
}


// ****************************************************************************
//  Method: avtChannelCommFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtChannelCommFilter with the given
//      parameters would result in an equivalent avtChannelCommFilter.
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Wed Nov 3 13:27:55 PST 2010
//
// ****************************************************************************

bool
avtChannelCommFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(ChannelCommAttributes*)a);
}

std::vector<avtVector>
avtChannelCommFilter::GetInitialLocations()
{
    std::vector<avtVector> rv;
    int numInX = atts.GetNumInX();
    int numInY = atts.GetNumInY();
    for (int j = 0 ; j < numInY ; j++)
        for (int i = 0 ; i < numInX ; i++)
        {
            avtVector v;
            v.x = -10 + 20.0*i/(numInX-1.0);
            v.y = -10 + 20.0*j/(numInY-1.0);
            v.z = 0;
            rv.push_back(v);
        }

    return rv;
}

void
avtChannelCommFilter::CreateIntegralCurveOutput(std::vector<avtIntegralCurve*> &ics)
{
    vtkRectilinearGrid *rg = vtkRectilinearGrid::New();
    int dims[3];
    int numInX = atts.GetNumInX();
    int numInY = atts.GetNumInY();
    dims[0] = numInX;
    dims[1] = numInY;
    dims[2] = 1;

    vtkFloatArray *x = vtkFloatArray::New();
    x->SetNumberOfTuples(numInX);
    for (int i = 0 ; i < dims[0] ; i++)
       x->SetTuple1(i, -10+20.0*i/(numInX-1.0));
    rg->SetXCoordinates(x);
    x->Delete();

    vtkFloatArray *y = vtkFloatArray::New();
    y->SetNumberOfTuples(numInY);
    for (int i = 0 ; i < dims[1] ; i++)
       y->SetTuple1(i, -10+20.0*i/(numInY-1.0));
    rg->SetYCoordinates(y);
    y->Delete();

    vtkFloatArray *z = vtkFloatArray::New();
    z->SetNumberOfTuples(1);
    z->SetTuple1(0, 0.0);
    rg->SetZCoordinates(z);
    z->Delete();

    vtkFloatArray *arr = vtkFloatArray::New();
    arr->SetNumberOfTuples(numInX*numInY);
    for (int i = 0 ; i < numInX*numInY ; i++)
        arr->SetTuple1(i, 0.);
    
    double max = 0;
    for (int i = 0 ; i < ics.size() ; i++)
    {
        avtChannelCommIC *cc = (avtChannelCommIC *) ics[i];
        const int *id = cc->GetIndex();
        int II = id[0];
        int JJ = id[1];
        int idx = JJ*numInX + II;
        arr->SetTuple1(idx, cc->GetDistance());
        if (cc->GetDistance() > max)
           max = cc->GetDistance();
    }

    arr->SetName("operators/ChannelComm/Mesh");
    rg->GetPointData()->AddArray(arr);
    arr->Delete();
    rg->GetPointData()->SetActiveScalars("operators/ChannelComm/Mesh");
    
    GetOutput()->GetInfo().GetAttributes().AddVariable("operators/ChannelComm/Mesh");
    double range[2] = { 0, max };
    GetOutput()->GetInfo().GetAttributes().SetVariableDimension(1, "operators/ChannelComm/Mesh");
    GetOutput()->GetInfo().GetAttributes().SetVariableType(AVT_SCALAR_VAR, "operators/ChannelComm/Mesh");
    GetOutput()->GetInfo().GetAttributes().GetActualDataExtents("operators/ChannelComm/Mesh")->Set(range);
    avtDataTree_p dt = new avtDataTree(rg, 0);
    rg->Delete();
    SetOutputDataTree(dt);
}


avtContract_p
avtChannelCommFilter::ModifyContract(avtContract_p c)
{
    const char *meshname = c->GetDataRequest()->GetVariable()+strlen("operators/ChannelComm/");
    //avtDataRequest_p dr = new avtDataRequest(c->GetDataRequest(), meshname);
    ////dr->AddSecondaryVariable(atts.GetVelocity().c_str());
    avtDataRequest_p dr = new avtDataRequest(c->GetDataRequest(), atts.GetVelocity().c_str());
    return new avtContract(c, dr);
}


avtIntegralCurve *
avtChannelCommFilter::CreateIntegralCurve(void)
{
    avtChannelCommIC *rv = new avtChannelCommIC();
    int idx[2] = { -1, -1 };
    rv->SetIndex(idx);
    return rv;
}

avtIntegralCurve *
avtChannelCommFilter::CreateIntegralCurve(const avtIVPSolver* model,
                                        avtIntegralCurve::Direction dir,
                                        const double& t_start,
                                        const avtVector &p_start, long ID)
{
    int numInX = atts.GetNumInX();
    int numInY = atts.GetNumInY();
    int idx[2];
    idx[0] = ID%numInX;
    idx[1] = ID/numInX;
    avtChannelCommIC *rv = new avtChannelCommIC(model, dir, t_start, p_start, ID);
    rv->SetIndex(idx);
    return rv;
}


void
avtChannelCommFilter::PreExecute(void)
{
    SetActiveVariable(atts.GetVelocity().c_str());
    avtPICSFilter::PreExecute();
}

void
avtChannelCommFilter::UpdateDataObjectInfo(void)
{
    GetOutput()->GetInfo().GetAttributes().SetSpatialDimension(2);
    GetOutput()->GetInfo().GetAttributes().SetTopologicalDimension(2);
    GetOutput()->GetInfo().GetAttributes().AddFilterMetaData("ChannelComm");
}

