#!/usr/bin/env python3
'''
unwrap convert SIEMENS physio MR dicom. re-implement https://github.com/CMRR-C2P/MB extractCMRRPhysio.m in python

Author: YingLi Lu
Email:  yinglilu@gmail.com
'''

import os
import sys
import logging
import struct

import pydicom
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s -%(message)s')


class Unwrapper:
    '''
    Extract physiological log files from encoded "_PHYSIO" DICOM file
    generated by CMRR MB sequences ( >= R015, >= VD13A)
        E. Auerbach, CMRR, 2016

    Inputs:
        filename = 'XXX.dcm'
        output_path = '/path/to/output/'

    Usage:
        unwrapper = Unwrapper(dicom_filename, output_path)
        unwrapper.apply()

    note:
        write *.log files to output_path


    '''

    def __init__(self, filename, output_path):
        self.filename = filename
        self.output_path = output_path
        self.logger = logging.getLogger(__name__)

    def apply(self):
        '''
        unwrap

        This function expects to find a single encoded "_PHYSIO" DICOM file
        generated by the CMRR C2P sequences >= R015. It will extract and write
        individual log files(*_ECG.log, *_RESP.log, *_PULS.log, *_EXT.log,
        *_Info.log) compatible with the CMRR C2P sequences >= R013. Only log
        files with nonzero traces will be written.
        '''
        try:
            dataset = pydicom.read_file(self.filename, stop_before_pixels=True)
            image_type = dataset.ImageType
            private_7fe1_0010_value = dataset[(0x7fe1, 0x0010)].value

            # is SIEMENS physio data?
            if image_type == ['ORIGINAL', 'PRIMARY', 'RAWDATA', 'PHYSIO'] and \
                    private_7fe1_0010_value.strip() == 'SIEMENS CSA NON-IMAGE':

                private_7fe1_1010_value = dataset[(0x7fe1, 0x1010)].value
                np = len(private_7fe1_1010_value)
                rows = int(dataset.AcquisitionNumber)
                columns = np/rows
                numFiles = columns/1024

                if np % rows != 0 or columns % 1024 != 0:
                    self.logger.error(
                        'Invalid image size ({} x{})!'.format(columns, rows))
                    return

                # debug

                parts = [private_7fe1_1010_value[i:i + int(np/numFiles)]
                         for i in range(0, np, int(np/numFiles))]

                full_log_filenames = []
                endian = sys.byteorder
                for part in parts:

                    if endian == 'little':
                        datalen = struct.unpack('<I', part[0:4])[0]
                        filenamelen = struct.unpack('<I', part[4:8])[0]
                    else:
                        datalen = struct.unpack('>I', part[0:4])[0]
                        filenamelen = struct.unpack('>I', part[4:8])[0]

                    log_filename = str(part[8:8+filenamelen], 'utf-8')
                    log_data = str(part[1024:1024+datalen], 'utf-8')

                    # write log file
                    # debug
                    # print(log_data)
                    full_log_filename = os.path.join(
                        self.output_path, log_filename)
                    self.logger.info('writing {}'.format(full_log_filename))
                    with open(full_log_filename, 'w') as f:
                        f.write(log_data)

                    full_log_filenames.append(full_log_filename)

            return full_log_filenames

        except KeyError as e:
            logging.exception(os.path.realpath(
                self.filename)+": tag " + str(e) + ' not found!')
            return
        except Exception as e:
            logging.exception(str(e))
            return
