//------------------------------------------------------------------------------
// Description:
// Returns the double array
static void GetDoubleArrayByName(
        const hid_t rootIdx, const char *name, std::vector<double> &array)
{
    // turn off warnings
    void *pContext = NULL;
    H5E_auto_t erorFunc;
    H5Eget_auto(&erorFunc, &pContext);
    H5Eset_auto(NULL, NULL);

    hid_t arrayIdx = H5Dopen(rootIdx, name);
    if (arrayIdx < 0)
    {
        vtkGenericWarningMacro("Cannot open array: " << name << "\n");
        return;
    }

    // turn warnings back on
    H5Eset_auto(erorFunc, pContext);
    pContext = NULL;

    // get the number of particles
    hsize_t dimValus[3];
    hid_t spaceIdx = H5Dget_space(arrayIdx);
    H5Sget_simple_extent_dims(spaceIdx, dimValus, NULL);
    int numbPnts = dimValus[0];

    array.resize(numbPnts);
    H5Dread(arrayIdx, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &array[0]);

//  H5Dclose( spaceIdx );
//  H5Dclose( arrayIdx );
}
//------------------------------------------------------------------------------
// Description:
// Finds the block index (blockIndx) within the HDF5 file associated with
// the given file index.
static bool FindBlockIndex(hid_t fileIndx, const int blockIdx, hid_t &rootIndx)
{
    // retrieve the contents of the root directory to look for a group
    // corresponding to the target block, if available, open that group
    hsize_t numbObjs;
    rootIndx = H5Gopen(fileIndx, "/");
    if (rootIndx < 0)
    {
        vtkGenericWarningMacro("Failed to open root node of particles file");
        return false;
    }

    bool found = false;
    H5Gget_num_objs(rootIndx, &numbObjs);
    for (int objIndex = 0; objIndex < static_cast < int >(numbObjs); objIndex++)
    {
        if (H5Gget_objtype_by_idx(rootIndx, objIndex) == H5G_GROUP)
        {
            int blckIndx;
            char blckName[65];
            H5Gget_objname_by_idx(rootIndx, objIndex, blckName, 64);

            // Is this the target block?
            if ((sscanf(blckName, "Grid%d", &blckIndx) == 1) &&
                (blckIndx == blockIdx))
            {
                // located the target block
                rootIndx = H5Gopen(rootIndx, blckName);
                if (rootIndx < 0)
                {
                    vtkGenericWarningMacro("Could not locate target block!\n");
                }
                found = true;
                break;
            }
        } // END if group
    } // END for all objects
    return (found);
}
void StackedImageDataLIC3D::SimpleExecute(vtkImageData * input, vtkImageData * output)
{
    vtkDataArray * vectors = input->GetPointData()->GetVectors();
    if (!vectors)
    {
        vtkGenericWarningMacro("StackeImageDataLIC3D::SimpleExecute: missing input vectors");
        return;
    }

    initialize();

    auto appendTo3D = vtkSmartPointer<vtkImageAppend>::New();
    appendTo3D->SetAppendAxis(2);

    int inputExtent[6];
    input->GetExtent(inputExtent);

    for (int position = inputExtent[4]; position < inputExtent[5]; ++position)
    {
        int sliceExtent[6];
        input->GetExtent(sliceExtent);
        sliceExtent[4] = sliceExtent[5] = position;

        auto slice = vtkSmartPointer<vtkExtractVOI>::New();
        slice->SetVOI(sliceExtent);
        slice->SetInputData(input);

        auto lic2D = vtkSmartPointer<vtkImageDataLIC2D>::New();
        lic2D->SetInputConnection(0, slice->GetOutputPort());
        lic2D->SetInputConnection(1, m_noiseImage->GetOutputPort());
        lic2D->SetSteps(50);
        lic2D->GlobalWarningDisplayOff();
        lic2D->SetContext(m_glContext);

        appendTo3D->AddInputConnection(lic2D->GetOutputPort());
    }

    appendTo3D->Update();
    output->DeepCopy(appendTo3D->GetOutput());
}
VTK_THREAD_RETURN_TYPE
vtkVisItStreamLine::ThreadedIntegrate( void *arg)
{
    vtkVisItStreamLine       *self;
    int                      thread_count;
    int                      thread_id;
    vtkStreamer::StreamArray           *streamer;
    vtkStreamer::StreamPoint           *sNext = 0, *sPtr;
    vtkStreamer::StreamPoint           pt1, pt2;
    int                      i;
    vtkIdType                idxNext, ptId;
    double                    d, step, dir;
    double                    xNext[3], vel[3], *cellVel, derivs[9];
    double                    *w, pcoords[3];
    double                    coords[4];
    vtkDataSet               *input;
    vtkGenericCell           *cell;
    vtkPointData             *pd;
    vtkDataArray             *inScalars;
    vtkDataArray             *inVectors;
    vtkDoubleArray           *cellVectors;
    vtkDataArray             *cellScalars=0;
    double tOffset, vort[3];
    double err;
    int nSavePts = 0, counter=0;

    thread_id = ((vtkMultiThreader::ThreadInfo *)(arg))->ThreadID;
    thread_count = ((vtkMultiThreader::ThreadInfo *)(arg))->NumberOfThreads;
    self = (vtkVisItStreamLine *)(((vtkMultiThreader::ThreadInfo *)(arg))->UserData);

    input     = vtkDataSet::SafeDownCast(self->GetInput());
    pd        = input->GetPointData();
    inScalars = pd->GetScalars();
    inVectors = pd->GetVectors();

    cell = vtkGenericCell::New();
    cellVectors = vtkDoubleArray::New();
    cellVectors->SetNumberOfComponents(3);
    cellVectors->Allocate(3*VTK_CELL_SIZE);
    if (inScalars)
    {
        cellScalars = inScalars->NewInstance();
        cellScalars->SetNumberOfComponents(inScalars->GetNumberOfComponents());
        cellScalars->Allocate(inScalars->GetNumberOfComponents()*VTK_CELL_SIZE);
    }

    // Set the function set to be integrated
    vtkInterpolatedVelocityField* func = vtkInterpolatedVelocityField::New();
    func->AddDataSet(input);

    if (self->GetIntegrator() == 0)
    {
        vtkGenericWarningMacro("No integrator is specified.");
        return VTK_THREAD_RETURN_VALUE;
    }

    w = new double[input->GetMaxCellSize()];

    // Create a new integrator, the type is the same as Integrator
    vtkInitialValueProblemSolver* integrator = self->GetIntegrator()->NewInstance();
    integrator->SetFunctionSet(func);

    // Used to avoid calling these function many times during
    // the integration
    double termspeed = self->GetTerminalSpeed();
    double maxtime = self->GetMaximumPropagationTime();
    double savePointInterval = self->GetSavePointInterval();

    // Take the largest of 2*maxtime or 100. It's simply an artificial
    // limit to the number of times to iterate to break out of a loop that
    // looks infinite.
    int MAX_ITERATIONS = int(maxtime * 2.);
    MAX_ITERATIONS = (MAX_ITERATIONS < 100) ? 100 : MAX_ITERATIONS;

    // For each streamer, integrate in appropriate direction
    // Do only the streamers that this thread should handle.
    for (ptId=0; ptId < self->GetNumberOfStreamers(); ptId++)
    {
        if(ptId % thread_count == thread_id)
        {
            // Get starting step
            streamer = self->GetStreamers() + ptId;
            sPtr = streamer->GetStreamPoint(0);
            if(sPtr->cellId < 0)
            {
                continue;
            }

            // Set the last cell id in the vtkInterpolatedVelocityField
            // object to speed up FindCell calls
            func->SetLastCellId(sPtr->cellId);

            dir = streamer->Direction;

            // Copy the first point
            pt1 = *sPtr;
            pt2 = *sPtr;
            tOffset = pt1.t;

            //integrate until time has been exceeded
            int iterations = 0;
            while(pt1.cellId >= 0 &&
                  pt1.speed > termspeed &&
                  pt1.t <  maxtime &&
                  iterations < MAX_ITERATIONS)
            {
                if(counter++ % 1000 == 0)
                {
                    if (!thread_id)
                    {
                        self->UpdateProgress((double)ptId/self->GetNumberOfStreamers()
                                             +pt1.t/maxtime/self->GetNumberOfStreamers());
                    }
                    if (self->GetAbortExecute())
                    {
                        break;
                    }
                }

                // Set the integration step to be characteristic cell length
                // time IntegrationStepLength
                input->GetCell(pt1.cellId, cell);
                step = dir * self->GetIntegrationStepLength() *
                       sqrt((double)cell->GetLength2())/pt1.speed;

                // Calculate the next step using the integrator provided
                if (integrator->ComputeNextStep(pt1.x, pt1.v, xNext, 0, step, 0, err) != 0)
                {
                    break;
                }

                for(i=0; i<3; i++)
                {
                    coords[i] = xNext[i];
                }

                // Interpolate the velocity field at coords
                if(!func->FunctionValues(coords, vel))
                {
                    break;
                }

                for(i=0; i<3; i++)
                {
                    pt2.v[i] = vel[i];
                }

                for (i=0; i<3; i++)
                {
                    pt2.x[i] = xNext[i];
                }
        
                pt2.cellId = func->GetLastCellId();
                func->GetLastWeights(w);
                func->GetLastLocalCoordinates(pcoords);
                input->GetCell(pt2.cellId, cell);
        
                if(inScalars)
                {
                    // Interpolate scalars
                    inScalars->GetTuples(cell->PointIds, cellScalars);
                    for (pt2.s=0.0, i=0; i < cell->GetNumberOfPoints(); i++)
                    {
                        pt2.s += cellScalars->GetComponent(i,0) * w[i];
                    }
                }

                pt2.speed = vtkMath::Norm(pt2.v);

                d = sqrt((double)vtkMath::Distance2BetweenPoints(pt1.x,pt2.x));
                pt2.d = pt1.d + d;
                // If at stagnation region, stop the integration
                if(d == 0 || (pt1.speed + pt2.speed) < VTK_STREAMLINE_EPSILON)
                {
                    pt2.t = pt1.t;
                    break;
                }
                pt2.t = pt1.t + (2.0 * d / (pt1.speed + pt2.speed));

                if (self->GetVorticity() && inVectors)
                {
                     // compute vorticity
                     inVectors->GetTuples(cell->PointIds, cellVectors);
      
                     cellVel = cellVectors->GetPointer(0);
                     cell->Derivatives(0, pcoords, cellVel, 3, derivs);
                     vort[0] = derivs[7] - derivs[5];
                     vort[1] = derivs[2] - derivs[6];
                     vort[2] = derivs[3] - derivs[1];
                     // rotation
                     pt2.omega = vtkMath::Dot(vort, pt2.v);
                     pt2.omega /= pt2.speed;
                     pt2.theta += (pt1.omega+pt2.omega)/2 * (pt2.t - pt1.t);
                }
        
                // Store only points which have a point to be displayed
                // between them
                if (tOffset >= pt1.t && tOffset <= pt2.t)
                {
                     // Do not store if same as the last point.
                     // To avoid storing some points twice.
                    if(!sNext || sNext->x[0] != pt1.x[0] || sNext->x[1] != pt1.x[1]
                         || sNext->x[2] != pt1.x[2])
                    {
                        idxNext = streamer->InsertNextStreamPoint();
                        sNext = streamer->GetStreamPoint(idxNext);
                        *sNext = pt1;
                        nSavePts++;
                    }
                    idxNext = streamer->InsertNextStreamPoint();
                    sNext = streamer->GetStreamPoint(idxNext);
                    *sNext = pt2;
                    nSavePts++;
                }

                if (tOffset < pt2.t)
                {
                    tOffset += ((int)(( pt2.t - tOffset) / savePointInterval) + 1) *
                               savePointInterval;
                }
      
                pt1 = pt2;

                ++iterations;
            } 

            // Store the last point anyway.
            if(!sNext || sNext->x[0] != pt2.x[0] || sNext->x[1] != pt2.x[1]
                || sNext->x[2] != pt2.x[2])
            {
                idxNext = streamer->InsertNextStreamPoint();
                sNext = streamer->GetStreamPoint(idxNext);
                *sNext = pt2;
                nSavePts++;
            }

            // Clear the last cell to avoid starting a search from
            // the last point in the streamline
            func->ClearLastCellId();
        }
    }

    integrator->Delete();
    func->Delete();

    cell->Delete();
    cellVectors->Delete();

    if (cellScalars)
    {
        cellScalars->Delete();
    }
    delete[] w;

    return VTK_THREAD_RETURN_VALUE;
}
//----------------------------------------------------------------------------
void vesKiwiImageWidgetRepresentation::setSliceIndex(int planeIndex, int sliceIndex)
{
  int dimensions[3];
  this->imageData()->GetDimensions(dimensions);

  if (sliceIndex < 0) {
    sliceIndex = 0;
  }
  else if (sliceIndex >= dimensions[planeIndex]) {
    sliceIndex = dimensions[planeIndex] - 1;
  }

  // allocate images if needed
  if (planeIndex == 0) {
    if (!this->Internal->SliceImages[0]) {
      this->Internal->SliceImages[0] = vtkSmartPointer<vtkImageData>::New();
      this->Internal->SliceImages[0]->SetOrigin(this->imageData()->GetOrigin());
      this->Internal->SliceImages[0]->SetSpacing(this->imageData()->GetSpacing());
      this->Internal->SliceImages[0]->SetDimensions(1, dimensions[1], dimensions[2]);
      this->Internal->SliceImages[0]->AllocateScalars(this->imageData()->GetScalarType(), 1);
      this->Internal->SliceImages[0]->GetPointData()->GetScalars()->SetName(this->imageData()->GetPointData()->GetScalars()->GetName());
    }
  }
  else if (planeIndex == 1) {
    if (!this->Internal->SliceImages[1]) {
      this->Internal->SliceImages[1] = vtkSmartPointer<vtkImageData>::New();
      this->Internal->SliceImages[1]->SetOrigin(this->imageData()->GetOrigin());
      this->Internal->SliceImages[1]->SetSpacing(this->imageData()->GetSpacing());
      this->Internal->SliceImages[1]->SetDimensions(dimensions[0], 1, dimensions[2]);
      this->Internal->SliceImages[1]->AllocateScalars(this->imageData()->GetScalarType(), 1);
      this->Internal->SliceImages[1]->GetPointData()->GetScalars()->SetName(this->imageData()->GetPointData()->GetScalars()->GetName());
    }
  }
  else {
    if (!this->Internal->SliceImages[2]) {
      this->Internal->SliceImages[2] = vtkSmartPointer<vtkImageData>::New();
      this->Internal->SliceImages[2]->SetOrigin(this->imageData()->GetOrigin());
      this->Internal->SliceImages[2]->SetSpacing(this->imageData()->GetSpacing());
      this->Internal->SliceImages[2]->SetDimensions(dimensions[0], dimensions[1], 1);
      this->Internal->SliceImages[2]->AllocateScalars(this->imageData()->GetScalarType(), 1);
      this->Internal->SliceImages[2]->GetPointData()->GetScalars()->SetName(this->imageData()->GetPointData()->GetScalars()->GetName());
    }
  }

  vtkImageData* sliceImage = this->Internal->SliceImages[planeIndex];

  #define mycall(vtktypename, vtkarraytype, primitivetype)   \
    case vtktypename: extractSliceExecute<vtkarraytype, primitivetype>(this->imageData(), sliceImage, planeIndex, sliceIndex); break;

  switch (this->imageData()->GetScalarType())
    {
      mycall(VTK_CHAR, vtkCharArray, char);
      mycall(VTK_UNSIGNED_CHAR, vtkUnsignedCharArray, unsigned char);
      mycall(VTK_SHORT, vtkShortArray, short);
      mycall(VTK_UNSIGNED_SHORT,vtkUnsignedShortArray, unsigned short);
      mycall(VTK_INT, vtkIntArray, int);
      mycall(VTK_UNSIGNED_INT,vtkUnsignedIntArray, unsigned int);
      mycall(VTK_LONG, vtkLongArray, long);
      mycall(VTK_UNSIGNED_LONG,vtkUnsignedLongArray, unsigned long);
      mycall(VTK_FLOAT, vtkFloatArray, float);
      mycall(VTK_DOUBLE, vtkDoubleArray, double);
      mycall(VTK_ID_TYPE, vtkIdTypeArray, vtkIdType);
    default:
      vtkGenericWarningMacro("Execute: Unknown input ScalarType");
      return;
    }


  vesKiwiImagePlaneDataRepresentation::Ptr rep = this->Internal->SliceReps[planeIndex];
  rep->setImageData(sliceImage);

  this->Internal->AppendFilter->GetInput(planeIndex)->DeepCopy(rep->imagePlanePolyData());
  this->Internal->CurrentSliceIndices[planeIndex] = sliceIndex;
}