/// DicomFile.h
/** Represents a file of DICOM format and its conents.
 */

#ifndef DICOMFILE_H_
#define DICOMFILE_H_

#include <fstream>
#include <vector>
#include <map>
#include <sstream>
#include <bitset>
#include <iostream>
#include <string>

#include <wx/defs.h>
#include <wx/log.h>
#include <wx/utils.h>

#include "Dictionary.h"
#include "StringConvert.h"
#include "Globals.h"
#include "ReadStream.h"
#include "DicomTags.h"
//#include "ValueRepresentations.h"
#include "Preamble.h"
#include "MetaHeader.h"
#include "MainHeader.h"

namespace jcs {

  struct DicomTag;
  struct DicomElement;
  class Dictionary;

  class DicomFile {

  public :

    // Constructor/destructor
    //DicomFile(const char* pFileName);
    DicomFile(wxString pFileName);

    ~DicomFile();

    // Export file
    void DataDump(const char *pFilename);
    std::vector <DicomElement> DataDump();
    int FindInSequence(const std::string& seq,
                       DicomElement& d, 
                       std::vector<std::string>& v);
    int FindInSequence(const std::string& seq,
                       const char* str, 
                       std::vector<std::string>& v);
    
    template <class T> int Find(DicomElement d, T& rValue);
    // Todo 20130215cdt: Find by DicomTag
    //template <class T> int Find(DicomTag t, T& rValue);
    template <class T> int Find(const std::string& s, T& rValue);
    
    template <class T> int PixelData(std::vector<T>& data,
                                     std::streamsize data_length,
                                     /*double slope = 1,
                                     double intercept = 0,*/
                                     int frame = 0);

    // Or read directly into buffer...user must alloc/free mem
    template <class T> int PixelData(T* data, std::streamsize data_length);

    // These functions are for Syngo files only
    bool HasSiemensMrHeader();

    bool GetCSAImageHeader(std::string& header);
    bool GetCSASeriesHeader(std::string& header);

    int ReadCSAImageHeader(const std::string& tag, std::string& value);
    int ReadCSAImageHeader(const std::string& tag, std::vector<std::string>& value);

    int ReadCSASeriesHeader(const std::string& tag, std::string& value);
    int ReadCSASeriesHeader(const std::string& tag, std::vector<std::string>& value);

    int ReadCSAHeader(const std::string& header, const std::string& tag, std::string& value);
    int ReadCSAHeader(const std::string& header, const std::string& tag, std::vector<std::string>& value);

    bool IsBigEndian() const { return (mByteOrder == dBIG_ENDIAN); }
    bool IsOk(); 

    //int GetHeaderSize();
    //int GetFileSize();

    const char* ErrorMessage() const;

  private :
    
    std::ifstream mInputFile;
    std::streampos mBeginning;
    std::streampos mPixelDataOffset;
    std::string mCharacterSet;
    MetaHeader mh;
    MainHeader main;

    // Error codes for DICOM files.
    enum ErrorCodes {
      NO_BYTES,
      NO_DICM,
      METAFILE_ERROR,
      TRANSFER_SYNTAX,
      NO_UID,
      NO_SERIES_UID,
      NO_INSTANCE,
      NO_IMAGE,
      NUM_CODES
    };
    std::bitset<NUM_CODES> mErrors;

    Dictionary* mDicom;
    Dictionary* mSpecial;
    
    const std::string mFileName;

    enum dicomEndianType { dLITTLE_ENDIAN, dBIG_ENDIAN } mByteOrder;
    enum VrSyntaxType { IMPLICIT_VR,  EXPLICIT_VR } mVrSyntax;

    void mCheckOk();
    bool mSkipPreamble();
    bool mReadMetafile();	

    void mSetTransferSyntax(VrSyntaxType v, dicomEndianType b);

    bool mFindElement(const DicomTag& rTarget, DicomElement& rMatch, std::istream& input);

    std::vector<DicomElement> mReadNextElementAll();
    void mReadNextElement(DicomElement& e, std::istream& input);
    void mReadElementTag(DicomElement& e, std::istream& input);
    void mReadValueRepresentation(DicomElement& e, std::istream& input);
    void mReadValueLength(DicomElement& e, std::istream& input);

    template<class T> int mReadValue(DicomElement d, T& rValue, std::istream& input);

    // Specialization for strings
    int mReadValue(DicomElement d, std::string& rValue, std::istream& input);

    typedef std::vector<std::string> (*read_ptr)(std::istream&, std::streamsize);

    std::map<std::string, read_ptr> ReadMap;

    void mInitializeMap();

    wxUint16 ByteSwap(wxUint16 value_to_swap) { return wxUINT16_SWAP_ON_BE(value_to_swap); }
    wxInt16 ByteSwap(wxInt16 value_to_swap) { return wxINT16_SWAP_ON_BE(value_to_swap); }
    wxUint32 ByteSwap(wxUint32 value_to_swap) { return wxUINT32_SWAP_ON_BE(value_to_swap); }
    wxInt32 ByteSwap(wxInt32 value_to_swap) { return wxINT32_SWAP_ON_BE(value_to_swap); }

    template<class T> int FindInStream(std::istream& input, DicomElement d, T& rValue);

    bool FindElement(DicomElement& d, std::istream& input);

    std::istream::pos_type GetPixelDataStart();
    std::istream::pos_type pixelDataStart;
    bool foundPixelDataStart;
    
    bool FindCSAHeader(int or_val, std::string& value);
    bool IsSyngoFile();
  };
}

#include "DicomFile.txx"

#endif

