/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef otbImageFileReader_h
#define otbImageFileReader_h

#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "itkImageSource.h"
#pragma GCC diagnostic pop
#else
#include "itkImageSource.h"
#endif
#include "otbImageIOBase.h"
#include "itkExceptionObject.h"
#include "itkImageRegion.h"

#include "otbDefaultConvertPixelTraits.h"
#include "otbImageKeywordlist.h"
#include "otbExtendedFilenameToReaderOptions.h"

namespace otb
{

/** \class ImageFileReaderException
 *
 * \brief Base exception class for IO conflicts.
 *
 * \ingroup OTBImageIO
 */
class ImageFileReaderException : public itk::ExceptionObject
{
public:
  /** Run-time information. */
  itkTypeMacro( ImageFileReaderException, ExceptionObject );

  /** Constructor. */
  ImageFileReaderException(const char *file, unsigned int line,
                           const std::string& desc = "",
                           const std::string& filename = "") :
    ExceptionObject(file, line, desc),
    m_Filename(filename)
  {
  }

  std::string m_Filename;
};

/** \class ImageFileReader
 * \brief  Reads image data.
 *
 * ImageFileReader reads its output data from a single output file.
 * It interfaces with an ImageIO class to read in the data and
 * supports streaming (partial reading) if the source dataset does so.
 *
 * ImageFileReader supports extended filenames, which allow controlling
 * how the source dataset is read. See
 * http://wiki.orfeo-toolbox.org/index.php/ExtendedFileName for more
 * information.
 *
 * \sa ExtendedFilenameToReaderOptions
 * \sa ImageSeriesReader
 * \sa ImageIOBase
 *
 * \ingroup IOFilters
 *
 *
 * \ingroup OTBImageIO
 */
template <class TOutputImage,
          class ConvertPixelTraits=DefaultConvertPixelTraits<
                   typename TOutputImage::IOPixelType > >
class ITK_EXPORT ImageFileReader : public itk::ImageSource<TOutputImage>
{
public:
  /** Standard class typedefs. */
  typedef ImageFileReader                    Self;
  typedef itk::ImageSource<TOutputImage>     Superclass;
  typedef itk::SmartPointer<Self>            Pointer;

  /** Method for creation through the object factory. */
  itkNewMacro(Self);

  /** Run-time type information (and related methods). */
  itkTypeMacro(ImageFileReader, ImageSource);

  /** The size of the output image. */
  typedef typename TOutputImage::SizeType  SizeType;

  /** The size of the output image. */
  typedef typename TOutputImage::IndexType  IndexType;

  /** The region of the output image. */
  typedef typename TOutputImage::RegionType  ImageRegionType;

  /** The pixel type of the output image. */
  typedef typename TOutputImage::InternalPixelType OutputImagePixelType;

  /** The Filename Helper. */
  typedef ExtendedFilenameToReaderOptions            FNameHelperType;

  /** Prepare image allocation at the first call of the pipeline processing */
  void GenerateOutputInformation(void) ITK_OVERRIDE;

  /** Does the real work. */
  void GenerateData() ITK_OVERRIDE;

  /** Give the reader a chance to indicate that it will produce more
   * output than it was requested to produce. ImageFileReader cannot
   * currently read a portion of an image (since the ImageIO objects
   * cannot read a portion of an image), so the ImageFileReader must
   * enlarge the RequestedRegion to the size of the image on disk. */
  void EnlargeOutputRequestedRegion(itk::DataObject *output) ITK_OVERRIDE;

  /** Set/Get the ImageIO helper class. Often this is created via the object
   * factory mechanism that determines whether a particular ImageIO can
   * read a certain file. This method provides a way to get the ImageIO
   * instance that is created. Or you can directly specify the ImageIO
   * to use to read a particular file in case the factory mechanism will
   * not work properly (e.g., unknown or unusual extension). */
  void  SetImageIO( otb::ImageIOBase * imageIO );
  itkGetObjectMacro(ImageIO,otb::ImageIOBase);

  virtual void SetFileName(const char* extendedFileName);
  virtual void SetFileName(std::string extendedFileName);
  virtual const char* GetFileName () const;

  /** Get the resolution information from the file */
  bool GetResolutionsInfo( std::vector<unsigned int>& res,
                          std::vector<std::string>& desc);

  /** Get the number of overviews available into the file specified
   * Returns: overview count, zero if none. */
  unsigned int GetOverviewsCount();


  /** Get description about overviews available into the file specified
   * Returns: overview info, empty if none.*/
  std::vector<std::string> GetOverviewsInfo();

protected:
  ImageFileReader();
  ~ImageFileReader() ITK_OVERRIDE;
  void PrintSelf(std::ostream& os, itk::Indent indent) const ITK_OVERRIDE;

  /** Convert a block of pixels from one type to another. */
  void DoConvertBuffer(void* buffer, size_t numberOfPixels);

private:
  /** Test whether m_ImageIO is valid (not NULL). This is intended to be called
   * after trying to create it via an ImageIOFactory. Throws an exception with
   * an appropriate message otherwise. */
  void TestValidImageIO();

  /** Generate the filename (for GDALImageI for example). If filename is a directory, look if is a
    * CEOS product (file "DAT...") In this case, the GdalFileName contain the open image file.
    */
  bool GetGdalReadImageFileName(const std::string& filename, std::string& GdalFileName);

  // Retrieve the real source file name if derived dataset */
  std::string GetDerivedDatasetSourceFileName(const std::string& filename) const;
  
  ImageFileReader(const Self &); //purposely not implemented
  void operator =(const Self&); //purposely not implemented

  otb::ImageIOBase::Pointer m_ImageIO;
  bool                 m_UserSpecifiedImageIO; // keep track whether the
                                               // ImageIO is user specified

  std::string m_FileName; // The file to be read

  bool m_UseStreaming;

  // The region that the ImageIO class will return when we ask to
  // produce the requested region.
  itk::ImageIORegion m_ActualIORegion;

  FNameHelperType::Pointer m_FilenameHelper;

  unsigned int m_AdditionalNumber;

  bool m_KeywordListUpToDate;

  /** Mapping between origin components and output components (before any
   * conversion) */
  std::vector<unsigned int> m_BandList;

  /** Store the number of components to be exported to the output image
   *  This variable can be the number of components in m_ImageIO or the
   *  number of components in the m_BandList (if used) */
  unsigned int m_IOComponents;
};

} //namespace otb

#ifndef OTB_MANUAL_INSTANTIATION
#include "otbImageFileReader.txx"
#endif

#endif // otbImageFileReader_h
