/// FslNiftiOutputter.h
/**
*/

#ifndef FSL_NIFTI_OUTPUTTER_H_
#define FSL_NIFTI_OUTPUTTER_H_

#include <string>
#include <map>

#include "NiftiOutputterBase.h"
#include "NiftiOutputter.h"
#include "NiftiVolume.h"

namespace jcs {

  struct NiftiHeader;

  class FslNiftiOutputter : public NiftiOutputterBase {

    public :
    FslNiftiOutputter();

    virtual int ConvertSeries(SeriesHandler* handler);
    virtual void UpdateOutputForSeries(SeriesHandler* handler);


  protected:

  private:
    static Options CreateOptions();

  };

  template <class T>
  class FslNiftiConversion: public NiftiConversion <T> {

  public:
    FslNiftiConversion(NiftiOutputterBase* outputter, SeriesHandler* handler);
    ~FslNiftiConversion();

  protected:
    virtual void GetHeaderForSeries();
    virtual void ProcessSlice(std::vector<T>& slice);
    virtual std::vector<double> GetRotationMatrix(const VolId& id);

  private:

  };


  template <class T>
  FslNiftiConversion<T>::FslNiftiConversion(NiftiOutputterBase* outputter, SeriesHandler* handler)
  : NiftiConversion<T>(outputter, handler)
  {
  }

  template <class T>
  FslNiftiConversion<T>::~FslNiftiConversion()
  {
  }


  template <class T> void
  FslNiftiConversion<T>::GetHeaderForSeries()
  {

    NiftiConversion<T>::GetHeaderForSeries();

	if (this->mHeader->hdr.datatype == DT_UINT16) {
	 	int bits;
		this->mHandler->Find("BitsStored", bits);
		if (bits == 16) {
			// it would be better to name the actual output file but that is
			// more complicated to do than it should be and I'm not fixing it now
			std::string patient_name;
			this->mHandler->Find("PatientName", patient_name);
			int series_number;
			this->mHandler->Find("SeriesNumber", series_number);
			wxLogMessage(_("%s series %d: output will be unsigned 16 bit."),
				patient_name, series_number);
		}
		else this->mHeader->hdr.datatype = DT_INT16;
    }
  }


  template <class T> void
  FslNiftiConversion<T>::ProcessSlice(std::vector<T>& slice)
  {
    // rotate 180
    reverse(slice.begin(), slice.end());

    // flip L/R
    unsigned int row_size = this->mHeader->hdr.dim[1];
    if (this->mHeader->hdr.datatype == DT_RGB) {
      row_size *= 3;
    }

    typename std::vector<T>::iterator row_begin = slice.begin();

    while (row_begin < slice.end()) {
      typename std::vector<T>::iterator row_end = row_begin + row_size;
      reverse(row_begin, row_end);
      row_begin = row_end;
    }

  }

  template <class T> std::vector<double>
  FslNiftiConversion<T>::GetRotationMatrix(const VolId& id)
  {
    double voxel_size_y = this->mHandler->GetVoxelSize()[1];
    double image_size_y = this->mHandler->GetRows() * voxel_size_y;

    std::vector<double> ipp = this->mHandler->GetIppForFirstSlice(id);
    std::vector<double> rotation = this->mHandler->GetRotationMatrix(id);

    std::vector<double> r4;

    r4.push_back(-rotation.at(0));
    r4.push_back(-rotation.at(1));
    r4.push_back(rotation.at(2));
    r4.push_back(0);

    r4.push_back(rotation.at(3));
    r4.push_back(rotation.at(4));
    r4.push_back(-rotation.at(5));
    r4.push_back(0);

    r4.push_back(-rotation.at(6));
    r4.push_back(-rotation.at(7));
    r4.push_back(rotation.at(8));
    r4.push_back(0);

    r4.push_back(-rotation.at(3) * image_size_y - ipp.at(0));
    r4.push_back(-rotation.at(4) * image_size_y - ipp.at(1) + voxel_size_y);
    r4.push_back(rotation.at(5) * image_size_y + ipp.at(2));
    r4.push_back(1);

    return r4;
  }
}

#endif
