/*=========================================================================

Program:   Insight Segmentation & Registration Toolkit
Module:    $RCSfile: itkInverseConsistentLandmarks.h,v $
Language:  C++
Date:      $Date: 2004/01/29 20:52:45 $
Version:   $Revision: 1.1 $

Copyright (c) Insight Software Consortium. All rights reserved.
See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
// This file is for defining landmarks and what to do with them.
#ifndef __itkInverseConsistentLandmarks_h
#define __itkInverseConsistentLandmarks_h

#include <string>
#include <map>
#include "itkPoint.h"
#include "itkPointSet.h"

#if !defined(CLREG_PRECISION)
#define CLREG_PRECISION double
#endif

//typedef itk::Point<PointStorageType,3> LandmarkPointType;
namespace itk
{

    template <typename PointStorageType, unsigned int Dimension= 3>
        class InverseConsistentLandmarkPoint : public itk::Point<PointStorageType,Dimension>
            {
        public:
            typedef InverseConsistentLandmarkPoint Self;
            typedef itk::Point<PointStorageType,Dimension> Superclass;

            InverseConsistentLandmarkPoint(void)
                {
                _fWeighting = 0.0;
                (*this)[0] = (*this)[1] = (*this)[2] = 0.0;

                };
            InverseConsistentLandmarkPoint(const PointStorageType x,
                const PointStorageType y,
                const PointStorageType z,
                const PointStorageType t,
                const PointStorageType weighting) :
                _fWeighting(weighting)
                    {
                    (*this)[0] = x; (*this)[1] = y; (*this)[2] = z;
                    m_T = t;
                    };
            InverseConsistentLandmarkPoint(const InverseConsistentLandmarkPoint &rhs)
                : Superclass(rhs),_fWeighting(rhs._fWeighting)
                    {
                    };

            PointStorageType GetT() const { return m_T; }
            void SetT(const PointStorageType val) { m_T = val; }
            //PointStorageType &SetT(void) { return m_T; }
            PointStorageType GetWeighting(void) const { return _fWeighting; };
            //PointStorageType & SetWeighting(void) { return _fWeighting; };
            void SetWeighting(const PointStorageType val) { _fWeighting = val; }
            virtual ~InverseConsistentLandmarkPoint(void) {};
        private:
            PointStorageType m_T;
            PointStorageType _fWeighting;
            };

    template <typename PointStorageType,class PointSetType>
        class InverseConsistentLandmarks
        : public std::map<std::string, InverseConsistentLandmarkPoint<PointStorageType > >
            {
        public:
            typedef InverseConsistentLandmarks Self;
            typedef InverseConsistentLandmarkPoint<PointStorageType> PointType;

            typedef std::map< std::string, PointType > Superclass;
            typedef SmartPointer<Self>  Pointer;
            typedef SmartPointer<const Self>  ConstPointer;

            typedef typename PointSetType::Pointer PointSetTypePointer;

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

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

            typedef enum
                {
                GEC_LANDMARKS = 0, INTELLX_LANDMARKS, ANALYZE_LANDMARKS,
                IPL_LANDMARKS, IPL_TALAIRACH_BOUNDS_LANDMARKS, IPL_CEREBELLAR_BOUNDS_LANDMARKS,
                UNKNOWN_LANDMARKS
                }
            Landmark_File_Format;
            InverseConsistentLandmarks(void);
            InverseConsistentLandmarks( const int XDim, const int YDim,
                const int ZDim, const int TDim = 0 );
            virtual ~InverseConsistentLandmarks(void)
                {
                };
            InverseConsistentLandmarks & operator=(const InverseConsistentLandmarks & rhs);

            PointSetTypePointer GetPointSet()
                {
                typedef typename PointSetType::PointIdentifier PointIdentifierType;
                typedef typename PointSetType::PointType PointSetPointType;
                typedef typename Self::const_iterator const_iterator;

                PointSetTypePointer pointSet = PointSetType::New();
                PointIdentifierType PointID =
                    itk::NumericTraits<PointIdentifierType>::Zero;

                const_iterator it = this->begin();
                const_iterator itend = this->end();
                while(it != itend)
                    {
                    const PointType cur = (*it).second;
                    PointSetPointType psPoint;

                    for(unsigned int i = 0; i < 3; i++)
                        {
                        psPoint[i] = cur[i];
                        }

                    pointSet->SetPoint(PointID,psPoint);
                    it++;
                    PointID++;
                    }
                return pointSet;
                }

            bool ReadPointTypes( const std::string lmrkfilename );
            bool ReadPointTypes( const std::string lmrkfilenamee, const int XDim, const int YDim,
                const int ZDim, const int TDim = 0 );
            bool WritePointTypes( const std::string lmrkfilename,
                const Landmark_File_Format lmkff = GEC_LANDMARKS );
            bool PrintPointTypes( void ) const;
            Landmark_File_Format QueryLandmarkFile( const std::string lmrkfilename );

            /**
             * Function for reading a landmark file in GEC landmark format
             * \param The landmark filename
             * \return true if successful, false if failed
             */
            bool ReadGECPointTypes( const std::string lmrkfilename );
            /**
             * Shortcut for calling
             * gec::InverseConsistentLandmarks::ReadGECPointTypes(lmrkfilename);
             * gec::InverseConsistentLandmarks::rescale(XDim,YDim,ZDim,TDim);
             * \param The landmark filename
             * \param XDim the XDimension of the image for the landmarks to be rescaled to
             * \param YDim the YDimension of the image for the landmarks to be rescaled to
             * \param ZDim the ZDimension of the image for the landmarks to be rescaled to
             * \param TDim the TDimension of the image for the landmarks to be rescaled to
             * \return true if successful, false if failed
             */
            bool ReadGECPointTypes( const std::string lmrkfilename, const int XDim, const int YDim, const int ZDim, const int TDim = 0 );

            /**
             * Behavior is flaky at best, because lmks file does not specify image dimensions
             * If this is used, call set[XYZT]Dim immediatly after this call.
             */
            bool ReadIntellXPointTypes( const std::string lmrkfilename );
            /**
             * Reads in a IntellXLandmark File, and uses the given dimensions for the image dimensions, NO RESCALING OF LANDMARKS
             * \param The landmark filename
             * \param XDim The value for XImage Dim
             * \param YDim The value for YImage Dim
             * \param ZDim The value for ZImage Dim
             * \param TDim The value for TImage Dim
             * \return true if successful, false if failed
             */
            bool ReadIntellXPointTypes( const std::string lmrkfilename, const int XDim, const int YDim,
                const int ZDim, const int TDim = 0 );

            /**
             * Behavior is flaky at best, because lmks file does not specify image dimensions
             * If this is used, call set[XYZT]Dim immediatly after this call.
             */
            bool ReadAnalyzePointTypes( const std::string lmrkfilename );
            /**
             * Reads in an Analyze Landmark File, and uses the given dimensions for the image dimensions, NO RESCALING OF LANDMARKS
             * \param The landmark filename
             * \param XDim The value for XImage Dim
             * \param YDim The value for YImage Dim
             * \param ZDim The value for ZImage Dim
             * \param TDim The value for TImage Dim
             * \return true if successful, false if failed
             */
            bool ReadAnalyzePointTypes( const std::string lmrkfilename, const int XDim, const int YDim,
                const int ZDim, const int TDim = 0 );

            /**
             * Function for reading a landmark file in IPL landmark format
             * \param The landmark filename
             * \return true if successful, false if failed
             */
            bool ReadIPLPointTypes( const std::string lmrkfilename );
            /**
             * Shortcut for calling
             * gec::InverseConsistentLandmarks::ReadIPLPointTypes(lmrkfilename);
             * gec::InverseConsistentLandmarks::rescale(XDim,YDim,ZDim,TDim);
             * \param The landmark filename
             * \param XDim the XDimension of the image for the landmarks to be rescaled to
             * \param YDim the YDimension of the image for the landmarks to be rescaled to
             * \param ZDim the ZDimension of the image for the landmarks to be rescaled to
             * \param TDim the TDimension of the image for the landmarks to be rescaled to
             * \return true if successful, false if failed
             */
            bool ReadIPLPointTypes( const std::string lmrkfilename, const int XDim, const int YDim, const int ZDim, const int TDim = 0 );

            /**
             * Function for reading a landmark file in IPL talairach bounds format
             * \param lmrkfilename The landmark filename
             * \return true if successful, false if failed
             */
            bool ReadIPLTalairachPointTypes( const std::string lmrkfilename );
            /**
             * Shortcut for calling
             * gec::InverseConsistentLandmarks::ReadIPLTalairachPointTypes(lmrkfilename);
             * gec::InverseConsistentLandmarks::rescale(XDim,YDim,ZDim,TDim);
             * \param The landmark filename
             * \param XDim the XDimension of the image for the landmarks to be rescaled to
             * \param YDim the YDimension of the image for the landmarks to be rescaled to
             * \param ZDim the ZDimension of the image for the landmarks to be rescaled to
             * \param TDim the TDimension of the image for the landmarks to be rescaled to
             * \return true if successful, false if failed
             */
            bool ReadIPLTalairachPointTypes( const std::string lmrkfilename, const int XDim, const int YDim, const int ZDim, const int TDim = 0 );
            /**
             * Function for reading a landmark file in IPL talairach bounds format
             * \param The landmark filename
             * \return true if successful, false if failed
             */
            bool ReadIPLCerebellarPointTypes( const std::string lmrkfilename );
            /**
             * Shortcut for calling
             * gec::InverseConsistentLandmarks::ReadIPLCerebellarPointTypes(lmrkfilename);
             * gec::InverseConsistentLandmarks::rescale(XDim,YDim,ZDim,TDim);
             * \param The landmark filename
             * \param XDim the XDimension of the image for the landmarks to be rescaled to
             * \param YDim the YDimension of the image for the landmarks to be rescaled to
             * \param ZDim the ZDimension of the image for the landmarks to be rescaled to
             * \param TDim the TDimension of the image for the landmarks to be rescaled to
             * \return true if successful, false if failed
             */
            bool ReadIPLCerebellarPointTypes( const std::string lmrkfilename, const int XDim, const int YDim, const int ZDim, const int TDim = 0 );

            bool WriteGECPointTypes( const std::string lmrkfilename ) const;
            bool WriteIntellXPointTypes( const std::string lmrkfilename ) const;
            bool WriteAnalyzePointTypes( const std::string lmrkfilename ) const;

            bool ConcatLandmarks( InverseConsistentLandmarks &newlmks );
            bool ConcatLandmarks( const std::string lmrkfilename );
            bool ConcatLandmarks( const std::string lmrkfilename, const int XDim, const int YDim,
                const int ZDim, const int TDim = 0 );

            bool RemoveClosePoints( const PointStorageType distance = 0.0 );
            bool RemoveUnmatchedPoints( InverseConsistentLandmarks &tempmap1 );

            /**
             * If image was 256x256, then getXDim should return 255, which is the maxXDim available
             * \return MaxXDim()
             */
            inline unsigned short getXDim(void) const {return ImageDims[0];};
            inline unsigned short getYDim(void) const {return ImageDims[1];};
            inline unsigned short getZDim(void) const {return ImageDims[2];};
            inline unsigned short getTDim(void) const {return ImageDims[3];};

            /**
             * If the image is 256x256, then this should be set to 255 because that is the max pixel number of the image.
             */
            inline void setXDim(const unsigned short newx) {assert(newx>0);ImageDims[0]=newx;};
            inline void setYDim(const unsigned short newy) {assert(newy>0);ImageDims[1]=newy;};
            inline void setZDim(const unsigned short newz) {assert(newz>0);ImageDims[2]=newz;};
            inline void setTDim(const unsigned short newt) {assert(newt>0);ImageDims[3]=newt;};

            /**
             * This rescales the landmarks to fit a new coordinate system.
             * \param newx new ImageDims[0]
             * \param newy new ImageDims[1]
             * \param newz new ImageDims[2]
             * \param newt new ImageDims[3]
             * \return  true if successful
             */
            bool rescale(const int newx, const int newy, const int newz, const int newt);

            void AddExtendedPointTypes3D_OnN(const InverseConsistentLandmarks &input,
                const int nx, const int ny, const int nz);
            void AddExtendedPointTypes3D_UnitCube(const InverseConsistentLandmarks &input);
        private:
            bool process_bnd_point(const std::string & CurrentLandmarkName,
                const char * buffer,
                const unsigned short ImageDims[4],
                PointType & ModifiedPoint);
            unsigned short ImageDims[4];
            };


}
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkInverseConsistentLandmarks.txx"
#endif

#endif                           //__LANDMARKS_H__
