#include <../src/vec/pf/pfimpl.h> /*I "petscpf.h" I*/

static PetscErrorCode PFApply_Constant(void *value, PetscInt n, const PetscScalar *x, PetscScalar *y)
{
  PetscInt    i;
  PetscScalar v = ((PetscScalar *)value)[0];

  PetscFunctionBegin;
  n *= (PetscInt)PetscRealPart(((PetscScalar *)value)[1]);
  for (i = 0; i < n; i++) y[i] = v;
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFApplyVec_Constant(void *value, Vec x, Vec y)
{
  PetscFunctionBegin;
  PetscCall(VecSet(y, *((PetscScalar *)value)));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFView_Constant(void *value, PetscViewer viewer)
{
  PetscBool iascii;

  PetscFunctionBegin;
  PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
  if (iascii) {
#if !defined(PETSC_USE_COMPLEX)
    PetscCall(PetscViewerASCIIPrintf(viewer, "Constant = %g\n", *(double *)value));
#else
    PetscCall(PetscViewerASCIIPrintf(viewer, "Constant = %g + %gi\n", (double)PetscRealPart(*(PetscScalar *)value), (double)PetscImaginaryPart(*(PetscScalar *)value)));
#endif
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFDestroy_Constant(void *value)
{
  PetscFunctionBegin;
  PetscCall(PetscFree(value));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFSetFromOptions_Constant(PF pf, PetscOptionItems *PetscOptionsObject)
{
  PetscScalar *value = (PetscScalar *)pf->data;

  PetscFunctionBegin;
  PetscOptionsHeadBegin(PetscOptionsObject, "Constant function options");
  PetscCall(PetscOptionsScalar("-pf_constant", "The constant value", "None", *value, value, NULL));
  PetscOptionsHeadEnd();
  PetscFunctionReturn(PETSC_SUCCESS);
}

PETSC_INTERN PetscErrorCode PFCreate_Constant(PF pf, void *value)
{
  PetscScalar *loc;

  PetscFunctionBegin;
  PetscCall(PetscMalloc1(2, &loc));
  if (value) loc[0] = *(PetscScalar *)value;
  else loc[0] = 0.0;
  loc[1] = pf->dimout;
  PetscCall(PFSet(pf, PFApply_Constant, PFApplyVec_Constant, PFView_Constant, PFDestroy_Constant, loc));

  pf->ops->setfromoptions = PFSetFromOptions_Constant;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*typedef PetscErrorCode (*FCN)(void*,PetscInt,const PetscScalar*,PetscScalar*);  force argument to next function to not be extern C*/

PETSC_INTERN PetscErrorCode PFCreate_Quick(PF pf, PetscErrorCode (*function)(void *, PetscInt, const PetscScalar *, PetscScalar *))
{
  PetscFunctionBegin;
  PetscCall(PFSet(pf, function, NULL, NULL, NULL, NULL));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/* -------------------------------------------------------------------------------------------------------------------*/
static PetscErrorCode PFApply_Identity(void *value, PetscInt n, const PetscScalar *x, PetscScalar *y)
{
  PetscInt i;

  PetscFunctionBegin;
  n *= *(PetscInt *)value;
  for (i = 0; i < n; i++) y[i] = x[i];
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFApplyVec_Identity(void *value, Vec x, Vec y)
{
  PetscFunctionBegin;
  (void)value;
  PetscCall(VecCopy(x, y));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFView_Identity(void *value, PetscViewer viewer)
{
  PetscBool iascii;

  PetscFunctionBegin;
  (void)value;
  PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
  if (iascii) PetscCall(PetscViewerASCIIPrintf(viewer, "Identity function\n"));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode PFDestroy_Identity(void *value)
{
  PetscFunctionBegin;
  PetscCall(PetscFree(value));
  PetscFunctionReturn(PETSC_SUCCESS);
}

PETSC_INTERN PetscErrorCode PFCreate_Identity(PF pf, void *value)
{
  PetscInt *loc;

  PetscFunctionBegin;
  (void)value;
  PetscCheck(pf->dimout == pf->dimin, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Input dimension must match output dimension for Identity function, dimin = %" PetscInt_FMT " dimout = %" PetscInt_FMT, pf->dimin, pf->dimout);
  PetscCall(PetscNew(&loc));
  loc[0] = pf->dimout;
  PetscCall(PFSet(pf, PFApply_Identity, PFApplyVec_Identity, PFView_Identity, PFDestroy_Identity, loc));
  PetscFunctionReturn(PETSC_SUCCESS);
}
