//                                               -*- C++ -*-
/**
 *  @file  Field.cxx
 *  @brief The class Field implements samples indexed by a position
 *
 *  Copyright (C) 2005-2013 EDF-EADS-Phimeca
 *
 *  This library is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  @author schueller
 *  @date   2012-07-16 15:59:45 +0200 (Mon, 16 Jul 2012)
 */
#include <fstream>
#include "Field.hxx"

BEGIN_NAMESPACE_OPENTURNS

CLASSNAMEINIT(Field);


/* Default constructor */
Field::Field()
  : TypedInterfaceObject<FieldImplementation>(FieldImplementation().clone())
{
  // Nothing to do
}

/* Constructor from implementation */
Field::Field(const FieldImplementation & implementation)
  : TypedInterfaceObject<FieldImplementation>(implementation.clone())
{
  // Nothing to do
}

/* Constructor from implementation */
Field::Field(const Implementation & implementation)
  : TypedInterfaceObject<FieldImplementation>(implementation)
{
  // Nothing to do
}

/* Constructor from a TimeGrid and a dimension */
Field::Field(const Mesh & mesh,
             const UnsignedLong dim)
  : TypedInterfaceObject<FieldImplementation>(new FieldImplementation(mesh, dim))
{
  // Nothing to do
}

/* Constructor from a TimeGrid and a sample */
Field::Field(const Mesh & mesh,
             const NumericalSample & sample)
  : TypedInterfaceObject<FieldImplementation>(new FieldImplementation(mesh, sample))
{
  // Nothing to do
}

/* Comparison operator */
Bool Field::operator ==(const Field & other) const
{
  return *getImplementation() == *(other.getImplementation());
}

/* Individual value accessor */
NSI_point Field::operator[](const UnsignedLong index)
{
  copyOnWrite();
  return getImplementation()->operator[](index);
}

NSI_const_point Field::operator[](const UnsignedLong index) const
{
  return getImplementation()->operator[](index);
}

NumericalScalar & Field::operator () (const UnsignedLong i,
                                      const UnsignedLong j)
{
#ifdef DEBUG_BOUNDCHECKING
  // No copyOnWrite() as the at() method already do it
  return this->at(i, j);
#else
  copyOnWrite();
  return (*getImplementation())[i][j];
#endif /* DEBUG_BOUNDCHECKING */
}

const NumericalScalar & Field::operator () (const UnsignedLong i,
    const UnsignedLong j) const
{
#ifdef DEBUG_BOUNDCHECKING
  return this->at(i, j);
#else
  return (*getImplementation())[i][j];
#endif /* DEBUG_BOUNDCHECKING */
}


NSI_point Field::at (const UnsignedLong index)
{
  if (index >= getSize()) throw OutOfBoundException(HERE) << "Index (" << index << ") is not less than size (" << getSize() << ")";
  copyOnWrite();
  return (*getImplementation())[index];
}

NSI_const_point Field::at (const UnsignedLong index) const
{
  if (index >= getSize()) throw OutOfBoundException(HERE) << "Index (" << index << ") is not less than size (" << getSize() << ")";
  return (*getImplementation())[index];
}

NumericalScalar & Field::at (const UnsignedLong i,
                             const UnsignedLong j)
{
  if (i >= getSize()) throw OutOfBoundException(HERE) << "i (" << i << ") is not less than size (" << getSize() << ")";
  if (j > getDimension()) throw OutOfBoundException(HERE) << "j (" << j << ") is greater than dimension (" << getDimension() << ")";
  copyOnWrite();
  return (*getImplementation())[i][j];
}

const NumericalScalar & Field::at (const UnsignedLong i,
                                   const UnsignedLong j) const
{
  if (i >= getSize()) throw OutOfBoundException(HERE) << "i (" << i << ") is not less than size (" << getSize() << ")";
  if (j > getDimension()) throw OutOfBoundException(HERE) << "j (" << j << ") is greater than dimension (" << getDimension() << ")";
  return (*getImplementation())[i][j];
}

/* Values accessor */
NumericalPoint Field::getValueAtIndex(const UnsignedLong index) const
{
  return getImplementation()->getValueAtIndex(index);
}

void Field::setValueAtIndex(const UnsignedLong index,
                            const NumericalPoint & val)
{
  copyOnWrite();
  getImplementation()->setValueAtIndex(index, val);
}

NumericalPoint Field::getValueAtNearestPosition(const NumericalPoint & position) const
{
  return getImplementation()->getValueAtNearestPosition(position);
}

void Field::setValueAtNearestPosition(const NumericalPoint & position,
                                      const NumericalPoint & val)
{
  copyOnWrite();
  getImplementation()->setValueAtNearestPosition(position, val);
}

Mesh Field::getMesh() const
{
  return getImplementation()->getMesh();
}

RegularGrid Field::getTimeGrid() const
{
  return getImplementation()->getTimeGrid();
}

NumericalPoint Field::__getitem__ (const UnsignedLong index) const
{
  return this->getValueAtIndex(index);
}

void Field::__setitem__ (const UnsignedLong index,
                         const NumericalPoint & val)
{
  copyOnWrite();
  setValueAtIndex(index, val);
}

/* Method __len__() is for Python */
UnsignedLong Field::__len__() const
{
  return getSize();
}


/* String converter */
String Field::__repr__() const
{
  return OSS(true) << "class=" << getClassName()
         << " name=" << getName()
         << " description=" << getDescription()
         << " implementation=" << getImplementation()->__repr__();
}

String Field::__str__(const String & offset) const
{
  return getImplementation()->__str__(offset);
}


/* Description accessor */
void Field::setDescription(const Description & description)
{
  copyOnWrite();
  getImplementation()->setDescription(description);
}



/* Description accessor */
Description Field::getDescription() const
{
  return getImplementation()->getDescription();
}


/* Size accessor */
UnsignedLong Field::getSize() const
{
  return getImplementation()->getSize();
}

/* Dimension accessor */
UnsignedLong Field::getMeshDimension() const
{
  return getImplementation()->getMeshDimension();
}

UnsignedLong Field::getDimension() const
{
  return getImplementation()->getDimension();
}


/* Return the values stored in the field as a sample */
NumericalSample Field::getSample() const
{
  return getImplementation()->getSample();
}

NumericalSample Field::getValues() const
{
  return getImplementation()->getValues();
}

/* Compute the spatial mean of the field */
NumericalPoint Field::getSpatialMean() const
{
  return getImplementation()->getSpatialMean();
}

/* Compute the temporal mean of the field */
NumericalPoint Field::getTemporalMean() const
{
  return getImplementation()->getTemporalMean();
}

/* Draw a marginal of the field */
Graph Field::drawMarginal(const UnsignedLong index) const
{
  return getImplementation()->drawMarginal(index);
}

/* VTK export */
void Field::exportToVTKFile(const String & fileName) const
{
  return getImplementation()->exportToVTKFile(fileName);
}


END_NAMESPACE_OPENTURNS
