#ifndef __CAMERAPROJECTORCALIBRATION_HH
#define __CAMERAPROJECTORCALIBRATION_HH

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/smart_ptr.hpp>
#include "image.h"
#include "rgba.h"
#include <algorithm>
#include <vector>

/// Class for calibrating the camera-projector system.
class CameraProjectorCalibration
{
public:
  ///
  typedef boost::numeric::ublas::vector< double > Vector;
  ///
  typedef boost::numeric::ublas::matrix
     < double, boost::numeric::ublas::column_major > Matrix;
  /** Constructor.
      @param _searchPattern Pattern used in cross-correlation to locate
      projected pattern. */
  CameraProjectorCalibration( const mimas::image< unsigned char > &_searchPattern );
  ///
  int getNumCalibFrames(void) const { return patternFrame.size(); }
  /** Get currently stored background frame.
      Camera image of screen without projected pattern. */
  const mimas::image< unsigned char > &getBackgroundFrame(void) const
  { return backgroundFrame; }
  /// Get ith stored calibration frame.
  const mimas::image< unsigned char > &getCalibFrame( int i ) const
  { return patternFrame[i]; }
  ///
  const std::pair< Vector, Vector > &getCalibPointPair( int i ) const
  { return pointPairs[i]; }
  ///
  void setBackgroundFrame( const mimas::image< unsigned char > &img )
  { backgroundFrame = img; }
  /** Add a frame for calibration.
      The method \c findPatternImg is called to locate the projected pattern
              in the camera image. \c pointPairsModified is set to \c true .
      @param _frame Frame for calibration (camera image).
      @param _pos Screen-coordinates of the projected pattern.
      @see findPatternImg */
  void addCalibFrame( const mimas::image< unsigned char > &_frame,
                      const Vector &_pos );
  /** Compute calibration-matrix.
      The homography is computed if a new camera-image was added using
      \c addCalibFrame (i.e. \c pointPairsModified is \c true) using the point
      pairs acquired so far (at least 5 point-pairs are required). */
  Matrix getHomography(void);
protected:
  /** Compute homography.
      See <A HREF="http://research.microsoft.com/%7Ezhang/Papers/TR98-71.pdf">paper by Zhengyou Zhang</A> */
  static Matrix genHomography( const std::vector< std::pair< Vector, Vector > >
                               &pointPairs );
  /// Access a part of a boost::multi_array.
  static boost::multi_array< double, 2 >::array_view< 2 >::type view
    ( boost::multi_array< double, 2 > &in, int x, int y, int w, int h );
  /** Search a pattern in a difference image.
      The search is performed by searching the maximum of the
      cross-correlation. */
  static Vector findPatternDiffImg( const mimas::image< double > &diffImg,
                                    const mimas::image< double > &tpl );
  /** Search pattern in image.
      @see findPattern */
  Vector findPatternImg( const mimas::image< unsigned char > &img );
  ///
  mimas::image< unsigned char > searchPattern;
  /// Background image to subtract from acquired images.
  mimas::image< unsigned char > backgroundFrame;
  /// The vector of acquired images.
  std::vector< mimas::image< unsigned char > > patternFrame;
  /// Vector with estimated locations of points.
  std::vector< std::pair< Vector, Vector > > pointPairs;
  /// Indication wether the homography needs to be recomputed.
  bool pointPairsModified;
  /// Storing the homography matrix.
  Matrix homography;
};

/** Smart-pointer.
    @see CameraProjectorCalibration */
typedef boost::shared_ptr< CameraProjectorCalibration > CameraProjectorCalibrationPtr;

#endif