from construct import (
                    Struct,GreedyRange,Array,Switch,Computed,FocusedSeq,Bytes,
                    Int16un,Int32un,PaddedString,Lazy,Float32b,Int8un,Int32sn, 
                    If,StopIf,IfThenElse,Tell,Pointer,Seek,
                    len_,this
                    )
import attr
from pathlib import Path
from ...utilities import get_file_time, printobj, timeit

spacer16 = Int16un
spacer32 = Int32un
header = Struct(
            'magic' / PaddedString(12, 'ascii'),
            'version' / Int32un,
            'unknown' / Array(9, Int16un),
            'spacer' / spacer16
        )

fileheader = header


frame_identifiers = Struct(
                    'magic' / Computed(this._._.magic),
                    'patient_id' / Computed(this._._.patient_id),
                    'study_id' / Computed(this._._.study_id),
                    'series_id' / Computed(this._._.series_id),
                    'slice_id' / Computed(this._._.slice_id),
                    'ind' / Computed(this._._.ind),
                    'type' / Computed(this._._.type)
                    )

contour_data = Struct(
    'parent_type' / Computed(this._.type),
    'unknown1' / Int32un,
    'id' / Int32un,
    'unknown2' / Int32un,
    'width' / Int32un,
    'depth' / Lazy(Array(this.width,Float32b))
    )

patient_data = Struct(
    'parent_type' / Computed(this._.type),
    'given_name' / PaddedString(31, 'ascii'),
    'surname' / PaddedString(66, 'ascii'),
    'birthdate' / Int32un,
    'sex' / PaddedString(1, 'ascii'),
    'frame_identifiers'/frame_identifiers
    )
image_types = {'fundus':33620481,
                'tomogram':35652097}

image_structure = Struct(
    'parent_type' / Computed(this._.type),
    'ind' / Computed(this._.ind),
    'size' / Int32un,
    'type' / Int32un,
    'fundus' / IfThenElse(this.type==image_types['fundus'],Computed(1),Computed(0)),
    'unknown' / Int32un,
    'width' / Int32un,
    'height' / Int32un,
    'pos' / Tell,
    'pixels'/ Switch(this.ind,{0:Lazy(Array(this.height*this.width, Int8un)),
                                1:Lazy(Array(this.height*this.width,Bytes(2)))})
    )

lat_structure = Struct(
    'parent_type' / Computed(this._.type),
    'unknown' / PaddedString(14, 'ascii'),
    'laterality' / Int8un,
    'unknown2' / Int8un,
    'unknown3' / spacer32
    )


frame_structure = FocusedSeq('data',
                            'magic' / PaddedString(12, 'ascii'),
                            'unknown' / spacer32,
                            'unknown2' / spacer32,
                            'pos' / Int32un,
                            'size' / Int32un,
                            'spacer' / spacer32,
                            'patient_id' / Int32un,
                            'study_id' / Int32un,
                            'series_id' / Int32un,
                            'slice_id' / Int32sn,
                            'ind' / Int16un,
                            'unknown4' / spacer16,
                            'type' / Int32un,
                            'spacer' / spacer32,
                            'data' / Switch(this.type, {11:lat_structure, 
                                                        1073741824:image_structure, 
                                                        9:patient_data,
                                                        10019:contour_data 
                                                        }
                                            )
                            )

chunk = FocusedSeq('frame',
    'pos' / Int32un,
    'start' / Int32un,
    'size' / Int32un,
    'spacer' / spacer32,
    'patient_id' / Int32un,
    'study_id' / Int32un,
    'series_id' / Int32un,
    'slice_id' / Int32sn,
    'unknown2' / spacer16,
    'spacer' / spacer16,
    'type' / Int32un,
    'unknown' / spacer32,
    'use' / IfThenElse(this.start>this.pos,Computed(1),Computed(0)),
    'frame'/ If(this.use==1,Pointer(this.start,frame_structure))
)

subdirectory = FocusedSeq('chunks',
    'header' / header,
    'num_entries' / Int32un,
    'unknown' / Int32un,
    'prev' / Int32un,
    'unknown' / spacer32,
    'chunks' / Array(this.num_entries,chunk),
    'use' / IfThenElse(this.prev==0,Computed(0),Computed(1)),
    StopIf(this.prev == 0),
    Seek(this.prev)
)
frame_details = Struct('totalframes'/ Computed(len_(this._.frames)),
                        'framecount'/Computed(1),
                        'scancount'/Computed(this.totalframes))

main_directory = Struct(
    'header' / header,
    'num_entries' / Int32un,
    'current' / Int32un,
    'prev' / Int32un,
    'unknown' / spacer32,
    Seek(this.current),
    'directories' / GreedyRange(subdirectory),
    'filteredDirectories' / Computed(lambda this: [y for x in this.directories for y in reversed(x) if y is not None]),
    'metadata' / Computed(lambda this: [x for x in this.filteredDirectories if x.parent_type in [9,11]]),
    'frames' / Computed(lambda this: [x for x in this.filteredDirectories if (x.parent_type==1073741824)]),
    'tomograms' / Computed(lambda this: [x for x in this.frames if x.fundus==0]),
    'fundus' / Computed(lambda this: [x for x in this.frames if x.fundus==1]),
    'frame_details'/frame_details
)


heidelberg_binary_reader = Struct(
    "mainheader" / fileheader,
    "main_directory" / main_directory,
    'frames' / Computed(this.main_directory.tomograms),
    'fundus' / Computed(this.main_directory.fundus),
    'header' / Struct('mainheader'/ Computed(this._.mainheader),
                        'metadata'/ Computed(this._.main_directory.metadata),
                        'frame_details' / Computed(this._.main_directory.frame_details)
                        )

    ) 
 
@attr.s(slots=True,kw_only=True)
class HOCT:
    binary_format:Struct = attr.ib()
    path:Path = attr.ib()
    ID = attr.ib()
    eye = attr.ib()
    scan_type = attr.ib()
    scan_number = attr.ib()
    date = attr.ib()
    
    @classmethod
    def from_path(cls,path_str:str):
        path = Path(path_str)
        path.resolve()
        if path.suffix.upper() != '.E2E':
            return
            
        fileName  = path.stem
        ID = fileName
        date = get_file_time(path)
        eye = ''
        scan_type = ''
        scan_number = ''

        return cls(binary_format = heidelberg_binary_reader,
                    path=path,
                    ID=ID,
                    eye=eye,
                    scan_type=scan_type,
                    scan_number = scan_number,
                    date=date)

if __name__=="__main__":
    pass