#!/usr/bin/env python3

'''
2d 0.4610997436727624
3d_fullres 0.5022740762294419
3d_lowres 0.5957028945994233
3d_cascade_fullres 0.517286480153028
ensemble_2d__nnUNetTrainerV2__nnUNetPlansv2.1--3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1 0.5243185547220239
ensemble_2d__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1 0.552552255340162
ensemble_2d__nnUNetTrainerV2__nnUNetPlansv2.1--3d_cascade_fullres__nnUNetTrainerV2CascadeFullRes__nnUNetPlansv2.1 0.531701751318307
ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1 0.6105215496684026
ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_cascade_fullres__nnUNetTrainerV2CascadeFullRes__nnUNetPlansv2.1 0.5343679184080806
ensemble_3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_cascade_fullres__nnUNetTrainerV2CascadeFullRes__nnUNetPlansv2.1 0.6000630104223947
Task222_ICTS2022 submit model ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1 0.6105215496684026

Here is how you should predict test cases. Run in sequential order and replace all input and output folder names with your personalized ones

nnUNet_predict -i FOLDER_WITH_TEST_CASES -o OUTPUT_FOLDER_MODEL1 -tr nnUNetTrainerV2 -ctr nnUNetTrainerV2CascadeFullRes -m 3d_fullres -p nnUNetPlansv2.1 -t Task222_ICTS2022
nnUNet_predict -i FOLDER_WITH_TEST_CASES -o OUTPUT_FOLDER_MODEL2 -tr nnUNetTrainerV2 -ctr nnUNetTrainerV2CascadeFullRes -m 3d_lowres -p nnUNetPlansv2.1 -t Task222_ICTS2022
nnUNet_ensemble -f OUTPUT_FOLDER_MODEL1 OUTPUT_FOLDER_MODEL2 -o OUTPUT_FOLDER -pp /workspace/nnUNet_trained_models/nnUNet/ensembles/Task222_ICTS2022/ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1/postprocessing.json
'''


import difflib
import os
import shutil
import subprocess
import sys
import time

from nipype.interfaces.dcm2nii import Dcm2niix
from pydicom import dcmread
from pynetdicom import AE, debug_logger
from pynetdicom.sop_class import CTImageStorage, RTStructureSetStorage
from rt_utils import RTStructBuilder

import numpy as np
import SimpleITK as sitk

# import itk_elastix
from registration.best_reg import reg_transform

def dcm2nii(source_dir, output_dir):

    # print(source_dir)
    # print(output_dir)

    converter = Dcm2niix()
    converter.inputs.source_dir = source_dir
    # converter.inputs.compression = 5
    converter.inputs.output_dir = output_dir
    print(converter.cmdline)
    converter.run() 


def inference(DCM_CT, DCM_MR):

    matcher = difflib.SequenceMatcher(a=DCM_CT, b=DCM_MR)
    match = matcher.find_longest_match(0, len(matcher.a), 0, len(matcher.b))
    ROOT_DIR   = DCM_CT[:match.size]
    NII_DIR    = os.path.join(ROOT_DIR, 'nii')
    INPUT_DIR  = os.path.join(ROOT_DIR, 'input')

    OUTPUT_3d_fullres = os.path.join(ROOT_DIR, '3d_fullres')
    OUTPUT_3d_lowres = os.path.join(ROOT_DIR, '3d_lowres')
    
    OUTPUT_DIR = os.path.join(ROOT_DIR, 'output')

    head, tail = os.path.split(DCM_CT)
    rtss_file = os.path.join(head, tail+'-rtss.dcm')

    shutil.rmtree(NII_DIR, ignore_errors=True)
    os.makedirs(NII_DIR)
    shutil.rmtree(INPUT_DIR, ignore_errors=True)
    os.makedirs(INPUT_DIR)
    shutil.rmtree(OUTPUT_3d_fullres, ignore_errors=True)
    shutil.rmtree(OUTPUT_3d_lowres, ignore_errors=True)
    shutil.rmtree(OUTPUT_DIR, ignore_errors=True)
    # os.makedirs(OUTPUT_DIR)

    nCT = os.path.basename(DCM_CT)
    nMR = os.path.basename(DCM_MR)   

    # print(nCT, nMR)
    # exit()
    
    dcm2nii(DCM_CT, NII_DIR)
    dcm2nii(DCM_MR, NII_DIR)

    for e in os.scandir(NII_DIR):
        if e.name.endswith('.nii.gz'):
            if e.name.startswith(nCT+'_'):
                NII_CT = e.path
            elif e.name.startswith(nMR+'_'):
                NII_MR = e.path

    basename = os.path.basename(NII_MR)
    old = '_'+basename.split('_')[-1]
    input_file = os.path.join(INPUT_DIR, basename.replace(old, '_0000.nii.gz'))
    output_file = os.path.join(OUTPUT_DIR, basename.replace(old, '.nii.gz'))


    basename_ct = os.path.basename(NII_CT)
    old_ct = '_'+basename_ct.split('_')[-1]
    label_file = os.path.join(NII_DIR, basename_ct.replace(old_ct, '.label.nii.gz'))
    
    shutil.copy(NII_MR, input_file)

    print(NII_CT, NII_MR, input_file)

    # nnUNet_predict -i INPUT_FOLDER -o OUTPUT_FOLDER -t 222 -m 3d_lowres --save_npz
    '''
    subprocess.run(["nnUNet_predict", 
        "-i", INPUT_DIR,
        "-o", OUTPUT_DIR,
        "-t", "222",
        "-m", "3d_lowres",
        "--save_npz",
        ])
    '''

    
# nnUNet_predict -i FOLDER_WITH_TEST_CASES -o OUTPUT_FOLDER_MODEL1 -tr nnUNetTrainerV2 -ctr nnUNetTrainerV2CascadeFullRes -m 3d_fullres -p nnUNetPlansv2.1 -t Task222_ICTS2022
# nnUNet_predict -i FOLDER_WITH_TEST_CASES -o OUTPUT_FOLDER_MODEL2 -tr nnUNetTrainerV2 -ctr nnUNetTrainerV2CascadeFullRes -m 3d_lowres -p nnUNetPlansv2.1 -t Task222_ICTS2022
# nnUNet_ensemble -f OUTPUT_FOLDER_MODEL1 OUTPUT_FOLDER_MODEL2 -o OUTPUT_FOLDER -pp /workspace/nnUNet_trained_models/nnUNet/ensembles/Task222_ICTS2022/ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1/postprocessing.json


    subprocess.run(["nnUNet_predict", 
        "-i", INPUT_DIR,
        "-o", OUTPUT_3d_fullres,
        "-tr", "nnUNetTrainerV2",
        "-ctr", "nnUNetTrainerV2CascadeFullRes",
        "-m", "3d_fullres",
        "-p", "nnUNetPlansv2.1",
        "-t", "Task222_ICTS2022",         
        "--save_npz",
        ])

    subprocess.run(["nnUNet_predict", 
        "-i", INPUT_DIR,
        "-o", OUTPUT_3d_lowres,
        "-tr", "nnUNetTrainerV2",
        "-ctr", "nnUNetTrainerV2CascadeFullRes",
        "-m", "3d_lowres",
        "-p", "nnUNetPlansv2.1",
        "-t", "Task222_ICTS2022",         
        "--save_npz",
        ])

    a = ["nnUNet_ensemble", 
        "-f", OUTPUT_3d_fullres, OUTPUT_3d_lowres,
        "-o", OUTPUT_DIR,
        "-pp", "/workspace/nnUNet_trained_models/nnUNet/ensembles/Task222_ICTS2022/ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1/postprocessing.json",
        ]
    
    print (' '.join(a))

    subprocess.run(["nnUNet_ensemble", 
        "-f", OUTPUT_3d_fullres, OUTPUT_3d_lowres,
        "-o", OUTPUT_DIR,
        "-pp", "/workspace/nnUNet_trained_models/nnUNet/ensembles/Task222_ICTS2022/ensemble_3d_fullres__nnUNetTrainerV2__nnUNetPlansv2.1--3d_lowres__nnUNetTrainerV2__nnUNetPlansv2.1/postprocessing.json",
        ])   
    
    print(output_file)

#     r2 = itk_elastix.register(NII_CT, NII_MR)
#     itk_elastix.transform_write(output_file, r2['fwdtransforms'], label_file, is_label=True)
    
    reg_transform(NII_CT, NII_MR, output_file, label_file)

    reader = sitk.ImageSeriesReader()
    dicom_names = reader.GetGDCMSeriesFileNames(DCM_CT)
    reader.SetFileNames(dicom_names)
    reader.MetaDataDictionaryArrayUpdateOn()
    reader.LoadPrivateTagsOn()
    image = reader.Execute()

    nnU = sitk.ReadImage(label_file)
    nnU = sitk.Resample(nnU, image, sitk.Transform(), sitk.sitkNearestNeighbor)
    
    ccfilter  = sitk.ConnectedComponentImageFilter ()
    nnUCC = ccfilter.Execute(nnU)
    ObjectCount1 = ccfilter.GetObjectCount()

    rtstruct = RTStructBuilder.create_new(dicom_series_path=DCM_CT)

    for j1 in range(ObjectCount1):
        label1  = sitk.BinaryThreshold(nnUCC, j1+1, j1+1)
        # label1  = sitk.AntiAliasBinary(label1)

        mask = sitk.GetArrayFromImage(label1).astype(bool)
        mask = np.transpose(mask, (1, 2, 0))
        # continue
        if mask.any():
            print(j1)
            rtstruct.add_roi(
                mask=mask,
                # use_pin_hole=True,
                # name="n%d"%n,
                )

    print(rtss_file)
    rtstruct.save(rtss_file)
    
    return rtss_file


# incorporate send_c_store

def SendDCM(fp):
    debug_logger()

    # Initialise the Application Entity
    ae = AE()
    ae.ae_title = 'OUR_STORE_SCP'

    # Add a requested presentation context
    # ae.add_requested_context(CTImageStorage)
    ae.add_requested_context(RTStructureSetStorage)

    # Read in our DICOM CT dataset
    ds = dcmread(fp)

    # Associate with peer AE at IP 127.0.0.1 and port 11112
    assoc = ae.associate("127.0.0.1", 11112)
    assoc = ae.associate("172.16.40.36", 104, 
        ae_title = 'N1000_STORAGE',
        )
    if assoc.is_established:
        # Use the C-STORE service to send the dataset
        # returns the response status as a pydicom Dataset
        status = assoc.send_c_store(ds)

        # Check the status of the storage request
        if status:
            # If the storage request succeeded this will be 0x0000
            print('C-STORE request status: 0x{0:04x}'.format(status.Status))
        else:
            print('Connection timed out, was aborted or received invalid response')

        # Release the association
        assoc.release()
    else:
        print('Association rejected, aborted or never connected')


def main():
    if len(sys.argv) < 2:
        print('Usage:', sys.argv[0], 'DCM_CT', 'DCM_MR')
        sys.exit()
    print('hello')
    print(sys.argv[0])
    print(sys.argv[1])

    start = time.time()
    rtss_file = inference(sys.argv[1], sys.argv[2])
    SendDCM(rtss_file)
    end = time.time()
    print(end - start, 'seconds')


if __name__ == '__main__':
    main()
