import pydicom
import numpy
import sys
import os
import nibabel
import json
import datetime
import re

# load the DICOM files
def load(dir):
    files = []
    print('Loading: {}'.format(dir))
    for fname in os.listdir(dir):
        print("loading: {}".format(fname))
        files.append(pydicom.dcmread(os.path.join(dir,fname)))

    print("file count: {}".format(len(files)))

    # skip files with no SliceLocation (eg scout views)
    slices = []
    skipcount = 0
    for f in files:
        if hasattr(f, 'SliceLocation'):
            slices.append(f)
        else:
            skipcount = skipcount + 1

    print("skipped, no SliceLocation: {}".format(skipcount))

    # ensure they are in the correct order
    slices = sorted(slices, key=lambda s: s.SliceLocation)
    return slices;

    # pixel aspects, assuming all slices are the same

    for s in slices:
        print("Pixel spacing: {}".format(s.PixelSpacing))

    return;

def convertToNIfTI(slices):

    # create 3D array
    img_shape = list(slices[0].pixel_array.shape)
    img_shape.append(len(slices))
    img3d = numpy.zeros(img_shape)

    # get position and orientation parameters

    x0=numpy.array([float(f) for f in slices[0].ImagePositionPatient])
    x1=numpy.array([float(f) for f in slices[-1].ImagePositionPatient])
    n=(x1-x0)/(len(slices)-1.);

    f=numpy.array([float(f) for f in slices[0].ImageOrientationPatient])

    s=numpy.array([float(f) for f in slices[0].PixelSpacing])
    
    #create affine 
    a=numpy.zeros((4,4))
    f1=numpy.zeros(4)
    f1[0:3]=f[0:3]
    #swap to account for row/column to (c,r) position mismatch
    a[:,1]=f1*s[1]
    f2=numpy.zeros(4)
    f2[0:3]=f[3:6]
    a[:,0]=f2*s[0]
    nn=numpy.zeros(4)
    nn[0:3]=n[0:3]
    a[:,2]=nn

    xn=numpy.ones(4)
    xn[0:3]=x0[0:3]
    a[:,3]=xn

     

    # fill 3D array with the images from the files
    for i, s in enumerate(slices):
        img2d = s.pixel_array
        img3d[:, :, i] = img2d
    #orientation and pixel spacing
    img = nibabel.Nifti1Image(img3d, a)
    img.header.set_slope_inter(float(slices[0].RescaleSlope),float(slices[0].RescaleIntercept))

    return img

def writeAnonymousSeries(slices,path,patientID, studyUID, fields):
    uid=uuid()
    seriesUID=uid.generateSeriesUUID('volume')
    i=0;
    for s in slices:
        outFile="DCM{:04d}.dcm".format(i)
        outFile=os.path.join(path,outFile)
        writeAnonymousDicomFile(s,patientID,studyUID,seriesUID,outFile,fields)
        i=i+1




def writeAnonymousDicomFile(slice, patientID, studyUID, seriesUID, outFile, fields):
    uid=uuid()
    instanceUID=uid.generateSOPInstanceUUID('volume')


    file_meta = pydicom.Dataset()
    file_meta.MediaStorageSOPClassUID = slice.file_meta.MediaStorageSOPClassUID
    file_meta.MediaStorageSOPInstanceUID = instanceUID
    file_meta.ImplementationClassUID = slice.file_meta.ImplementationClassUID
    file_meta.TransferSyntaxUID=pydicom.uid.ImplicitVRLittleEndian

    print("Setting dataset values...")
    # Create the FileDataset instance (initially no data elements, but file_meta
    # supplied)
    ds = pydicom.FileDataset(outFile, {},
                         file_meta=file_meta, preamble=b"\0" * 128)

    # Add the data elements -- not trying to set all required here. Check DICOM
    # standard
    ds.PatientName = patientID
    ds.PatientID = patientID
    ds.StudyInstanceUID=studyUID
    ds.SeriesInstanceUID=seriesUID
    ds.SOPInstanceUID=instanceUID

    # Set the transfer syntax
    ds.is_little_endian = True
    ds.is_implicit_VR = True

    # Set creation date/time
    #dt = datetime.datetime.now()
    #ds.ContentDate = dt.strftime('%Y%m%d')
    #timeStr = dt.strftime('%H%M%S.%f')  # long format with micro seconds
    ds.ContentTime = slice.ContentTime
    #add variables from Daniel's list (fields)
    for f in fields:
        try:
            ds[f]=slice[f];
        except:
            print("Field {} missing".format(f))

    ds.PixelData=slice.PixelData
    print("Writing test file", outFile)
    ds.save_as(outFile)
    print("File saved.")

class uuid:

    def __init__(self):
        fhome=os.path.expanduser('~')
        fconfig=os.path.join(fhome,'.uuid','config.json')
        self.basePath=os.path.join(fhome,'.uuid')
        with open(fconfig,'r') as f:
            self.config=json.load(f)
        self.labelUUID={'series':'1','study':'2','instance':'3','frameOfReference':4}
        self.dataUUID={'volume':'1','segmentation':'2','transformation':'3'}

    def getTimeCode(x):
        hour=x.strftime("%H")
        hour=re.sub('^0','',hour)
        return hour+x.strftime("%M%S")
 
    def generateStudyUUID(self,type):

        x=datetime.datetime.now()
        date=x.strftime("%Y%m%d")
        studyFile=os.path.join(self.basePath,'studyCount'+date+'.txt')

        try:
            f=open(studyFile,"r")
            id=int(f.readline())
            id=id+1
            f.close()
        except:
            id=0

        studyId="{}.{}.{}.{}.{}".format(self.config["baseUUID"],self.labelUUID['study'],
                        self.dataUUID[type],date,id)

        f=open(studyFile,"w")
        f.write("{}".format(id))
        f.close()
        return studyId


    def generateSOPInstanceUUID(self,type):

        baseUUID=self.config["baseUUID"]
        x=datetime.datetime.now()
        date=x.strftime("%Y%m%d")
        instanceFile=os.path.join(self.basePath,'instanceCount'+date+'.txt')

        try:
            f=open(instanceFile,"r")
            id=int(f.readline())
            id=id+1
            f.close()
        except:
            id=0

        instanceId="{}.{}.{}.{}.{}".format(baseUUID,self.labelUUID['instance'],
                        self.dataUUID[type],date,id)

        f=open(instanceFile,"w")
        f.write("{}".format(id))
        f.close()
        return instanceId


    def generateFrameOfReferenceUUID(self,type):

        baseUUID=self.config["baseUUID"]
        basePath=self.basePath
        x=datetime.datetime.now()
        date=x.strftime("%Y%m%d")
        forFile=os.path.join(basePath,'frameCount'+date+'.txt')

        try:
            f=open(studyFile,"r")
            id=int(f.readline())
            id=id+1
            f.close()
        except:
            id=0

        forId="{}.{}.{}.{}.{}".format(baseUUID,self.labelUUID['frameOfReference'],
                        self.dataUUID[type],date,id)

        f=open(forFile,"w")
        f.write("{}".format(id))
        f.close()
        return forId
  
    def generateSeriesUUID(self,type):

        baseUUID=self.config["baseUUID"]
        x=datetime.datetime.now()
        seriesInstanceUid=baseUUID+'.'+self.labelUUID['series']+'.'
        seriesInstanceUid+=self.dataUUID[type]+'.'+x.strftime("%Y%m%d")+'.'+uuid.getTimeCode(x)
        return seriesInstanceUid



