 /// SeriesHandler.h
/**
*/

#ifndef SERIES_HANDLER_H
#define SERIES_HANDLER_H

#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <math.h>
#include <cmath>

#include <wx/log.h>
#include <wx/utils.h>
#include <wx/app.h>
#include <wx/filename.h>
#include <wx/stdpaths.h>

#include "DicomFile.h"
#include "Dictionary.h"
#include "Volume.h"
#include "StringConvert.h"

class wxFileName;

namespace jcs {

  enum AoCode {
    ASCENDING,
    DESCENDING,
    INTERLEAVED_ODD,
    INTERLEAVED_EVEN,
    UNKNOWN,
    OTHER
  };

  ///
  /*** Things constant for a 3D volume.
    */
  struct VolId {
    typedef std::vector<std::string> IdType;
    IdType ids;
    int orientNumber;
    
    bool operator< (const VolId &rhs) const
    {
      return (ids < rhs.ids);
    }

    bool operator== (const VolId &rhs) const
    {
      return ((ids == rhs.ids) && (orientNumber == rhs.orientNumber));
    }
  };


  /** Stuff used for automatic generation of filenames & directories,
  save to avoid unneccessary file reads.
    */
  struct SeriesInfo {
    std::string subject_name;
    std::string subject_id;
    std::string study_number;
    std::string series_number;
    std::string StudyDate;
    std::string SeriesDate;
    std::string study_description;
    std::string series_description;
  };


  struct GradientInfo {
    std::vector<double> values;
    std::vector<double> xGrads;
    std::vector<double> yGrads;
    std::vector<double> zGrads;
  };


  void Normalize(GradientInfo& info);
  void RotateGradInfo(GradientInfo& info, std::vector<double>& r);


  ///
  /**
    */
  class SeriesHandler
  {
    public :
    SeriesHandler(const std::string& seriesUid);
    virtual ~SeriesHandler();

    const std::string& GetSeriesUid() const { return mSeriesUid; }

    template <class T> int Find(DicomElement e, T& value) const;
    template <class T> int Find(const std::string& s, T& value) const;
    template <class T> int Find(const std::string& s, T& value, const VolId& id) const;
    std::string Find(const std::string& s) const;
    std::string Find(DicomElement e) const;

    virtual std::vector<std::string> GetStringInfo();

    virtual void GetVolumes (std::map <VolId, Volume <wxInt64> >& v) { mGetVolumes <wxInt64> (v); }
    virtual void GetVolumes (std::map <VolId, Volume <wxUint64> >& v) { mGetVolumes <wxUint64> (v); } 
    virtual void GetVolumes (std::map <VolId, Volume <wxInt32> >& v) { mGetVolumes <wxInt32> (v); } 
    virtual void GetVolumes (std::map <VolId, Volume <wxUint32> >& v) { mGetVolumes <wxUint32> (v); } 
    virtual void GetVolumes (std::map <VolId, Volume <wxInt16> >& v) { mGetVolumes <wxInt16> (v); } 
    virtual void GetVolumes (std::map <VolId, Volume <wxUint16> >& v) { mGetVolumes <wxUint16> (v); } 
    virtual void GetVolumes (std::map <VolId, Volume <wxInt8> >& v) { mGetVolumes <wxInt8> (v); }
    virtual void GetVolumes (std::map <VolId, Volume <wxUint8> >& v) { mGetVolumes <wxUint8> (v); } 

    int AddFile(const char* filename);
    int GetNumberOfFiles() const { return mFileMap.size(); }
    virtual int GetNumberOfSlices() const;
    virtual int GetNumberOfVolumes() const;
    virtual std::vector<double> GetVoxelSize();
    virtual int GetRows();
    virtual int GetColumns();
    virtual int GetFrames() const;
    virtual double GetSliceDuration() const;
    virtual double GetVolumeInterval() const;
    virtual AoCode GetAcquisitionOrder() const { return UNKNOWN; }
    virtual bool IsMosaic() const { return false; }
    virtual bool IsDti() const { return false; }
    virtual bool IsMoCo() const { return false; }
    virtual bool IsMultiEcho() const { return multiecho; }
    bool IsBigEndian() const;
    std::set<VolId> GetVolIds() const;
    std::map<VolId, std::string> GetVolIdsAndFiles() const;
    std::vector<double> GetIppForFirstSlice(const VolId& id);
    std::vector<double> GetRotationMatrix(const VolId& id);
    virtual GradientInfo GetGradientInfo() { return GradientInfo(); };
    void GetImageComments(std::vector<std::string>& comments) const;
    std::vector<std::string> GetFilenames() const;
    void GetEchoTimes();

    SeriesInfo seriesInfo;
    bool rescale;
    bool multiecho;
    /// Indexed by EchoNumber.
    std::map<int, std::string> echo_times;

    int GetRescaleSlopeAndIntercept(const VolId& id, double& slope, double& intercept) const;
    virtual int GetRescaleSlopeAndIntercept(DicomFile& dfile, double& slope, double& intercept, int frame = 0) const;

    protected :
    typedef std::vector< VolId > VolListType;
    typedef std::map < std::string, VolListType > FileMapType;
    FileMapType mFileMap;
    std::map<VolId, std::vector<double> > position; // ipp for slice 1, calculated in GetVolumes
    std::vector<std::vector<double> > orientations;

    virtual VolListType ReadVolIds(DicomFile& file);
    virtual std::string GetImagePositionPatient(DicomFile& dfile, int frame);
    double GetMeanVolumeInterval(std::vector<double>& vol_times) const;
    void CheckIntervalSanity(std::vector<double>& vol_times, double value) const;
    double CalculateVolIntVal(std::map<VolId, std::vector<double> >& vtmap, unsigned int min_num_volumes) const;
    private :
    // moved over from DicomSeries
    const std::string mSeriesUid;
    std::set<std::string> mInstanceUids;

    SeriesHandler& operator=(const SeriesHandler& rhs);
    bool GetFirstFilename(const VolId& id, std::string& str) const;

    // todo: see if some of this can be moved out of the template?
    template <class T> void	mGetVolumes(std::map<VolId, Volume<T> >& v);
  };

} // end namespace jcs

#include "SeriesHandler.txx"

#endif
