<script>
import { mapState } from "vuex"
import CopyToClipboardButton from "./CopyToClipboardButton.vue";
import ResourceDetailText from "./ResourceDetailText.vue";
import resourceHelpers from "../helpers/resource-helpers"
import dateHelpers from "../helpers/date-helpers"
import clipboardHelpers from "../helpers/clipboard-helpers"
import api from "../orthancApi"
import { v4 as uuidv4 } from "uuid"
import labels from "../store/modules/labels";

// these tags can not be removed
document.requiredTags = [
    'PatientID', 'PatientBirthDate', 'PatientSex', , 'PatientName',
    'StudyInstanceUID', 'StudyDate', 'StudyTime', 'AccessionNumber', 'ReferringPhysicianName', 'StudyID',
    'SeriesInstanceUID', 'Modality', 'ContrastBolusAgent', 'SeriesNumber', 'ImageOrientationPatient', 'SeriesType'
];

// list of actions: 
// - 'attach-study-to-existing-patient', 
// - 'modify-any-tags-in-one-study',
// - 'modify-patient-tags-in-all-studies',
// - 'anonymize-study',
// - 'attach-series-to-existing-study',
// - 'modify-series-tags',
// - 'create-new-study-from-series',
// - 'anonymize-series'



export default {
    props: ["orthancId", "studyMainDicomTags", "patientMainDicomTags", "seriesMainDicomTags", "isAnonymization", "resourceLevel"],
    data() {
        return {
            tags: {},                                      // the tag values entered in the dialog
            dateTags: {},                                  // same as tags but only for dates (for the DatePicker)
            originalTags: {},                              // the original tag values
            removedTags: {},
            insertedTags: new Set(),
            samePatientStudiesCount: 0,
            hasLoadedSamePatientsStudiesCount: false,
            errorMessageId: null,
            warningMessageId: null,
            jobProgressComplete: 0,
            jobProgressFailed: 0,
            jobProgressRemaining: 100,
            jobRefreshTimeout: 200,
            jobIsComplete: false,
            jobIsRunning: false,
            jobIsSuccess: false,
            showModifiedResourceKey: null,
            showModifiedResourceValue: null,
            modificationMode: null,
            step: 'init', // allowed values: 'init', 'tags', 'warning', 'error', 'progress', 'done'
            action: 'none',
            selectablePatientTags: [],
            selectedPatientTags: {},
            selectableStudies: []
        }
    },
    async mounted() {
        this.$refs['modal-main-div'].addEventListener('show.bs.modal', (e) => {
            // move the modal to body to avoid z-index issues: https://weblog.west-wind.com/posts/2016/sep/14/bootstrap-modal-dialog-showing-under-modal-background
            document.querySelector('body').appendChild(e.target);
            this.reset();
        });
    },
    methods: {
        setTagsFromDicomTags(mainDicomTags) {
            for (const [k, v] of Object.entries(mainDicomTags)) {
                this.tags[k] = v;
                if (dateHelpers.isDateTag(k)) {
                    this.dateTags[k] = dateHelpers.fromDicomDate(v);
                }
            }
        },
        async reset() {
            this.step = 'init';
            this.selectablePatientTags = [];
            this.selectedPatientTags = {};
            this.selectableStudies = [];
            if (!this.hasLoadedSamePatientsStudiesCount) {
                // console.log("loading", this.hasLoadedSamePatientsStudiesCount);
                this.samePatientStudiesCount = (await api.getSamePatientStudies(this.patientMainDicomTags, ['PatientID'], false)).length;  // here, we only use the PatientID since this is the only tag used by Orthanc when modifying the resources
                this.hasLoadedSamePatientsStudiesCount = true;
            }

            this.tags = {};
            this.dateTags = {};
            this.originalTags = {};
            this.removedTags = {};
            this.insertedTags = new Set();

            for (const [k, v] of Object.entries(this.patientMainDicomTags)) {
                this.originalTags[k] = v;
                this.removedTags[k] = false;
            }
            for (const [k, v] of Object.entries(this.studyMainDicomTags)) {
                this.originalTags[k] = v;
                this.removedTags[k] = false;
            }

            if (this.resourceLevel == "series") {
                for (const [k, v] of Object.entries(this.seriesMainDicomTags)) {
                    this.originalTags[k] = v;
                    this.removedTags[k] = false;
                }
            }

            this.jobProgressComplete = 0;
            this.jobProgressFailed = 0;
            this.jobProgressRemaining = 100;

            if (this.resourceLevel == 'study') {
                this.modificationMode = this.uiOptions.Modifications.DefaultMode;
            } else {
                this.modificationMode = this.uiOptions.Modifications.SeriesDefaultMode;
            }

            if (this.isAnonymization) {
                if (this.resourceLevel == 'study') {
                    this.goToNextStep('init', 'anonymize-study');
                } else {
                    this.goToNextStep('init', 'anonymize-series');
                }
            }
        },
        back() {
            if (this.step == 'tags' && !this.isAnonymization) {
                this.step = 'init';
            } else if (['warning', 'error', 'select-patient', 'select-studies'].indexOf(this.step) != -1) {
                this.step = 'tags';
            } else {
                console.error("unknown step for back function ", this.step);
            }
        },
        setWarning(warningMessageId) {
            this.warningMessageId = warningMessageId;
            this.step = 'warning';
        },
        setError(errorMessageId) {
            this.errorMessageId = errorMessageId;
            this.step = 'error';
        },
        startMonitoringJob(jobId) {
            this.step = 'progress';
            this.jobRefreshTimeout = 200;  // refresh quickly at the beginnning !
            setTimeout(this.monitorJob, this.jobRefreshTimeout, [jobId]);
        },
        async monitorJob(jobId) {
            const jobStatus = await api.getJobStatus(jobId);
            this.jobIsComplete = (jobStatus.State == "Success" || jobStatus.State == "Failure");
            this.jobIsRunning = (jobStatus.State == "Running");
            this.jobIsSuccess = (jobStatus.State == "Success");

            if (this.jobIsComplete) {

                this.jobProgressRemaining = 0;

                if (this.jobIsSuccess) {
                    this.jobProgressComplete = 100;
                    this.jobProgressFailed = 0;
                } else {
                    this.jobProgressComplete = 0;
                    this.jobProgressFailed = 100;
                }
            } else {
                this.jobProgressFailed = 0;
                this.jobProgressComplete = jobStatus.Progress;
                this.jobProgressRemaining = 100 - this.jobProgressComplete;
            }
            if (!this.jobIsComplete) {
                this.jobRefreshTimeout = Math.min(this.jobRefreshTimeout + 200, 2000);  // refresh quickly at the beginnning !
                setTimeout(this.monitorJob, this.jobRefreshTimeout, [jobId]);
            } else {
                this.step = 'done';
                const jobType = jobStatus['Type'];
                if (jobType == 'MergeStudy') {
                    const modifiedStudy = await api.getStudy(jobStatus['Content']['TargetStudy']);
                    this.showModifiedResourceValue = modifiedStudy['MainDicomTags']['StudyInstanceUID'];
                    this.showModifiedResourceKey = "StudyInstanceUID";
                } else if (jobType == 'ResourceModification') {
                    if (jobStatus['Content']['Type'] == 'Patient') {
                        this.showModifiedResourceKey = "PatientID";
                        const modifiedPatient = await api.getPatient(jobStatus['Content']['ID']);
                        this.showModifiedResourceValue = modifiedPatient['MainDicomTags']['PatientID'];
                    } else if (jobStatus['Content']['Type'] == 'Study') {
                        this.showModifiedResourceKey = "StudyInstanceUID";
                        const modifiedStudy = await api.getStudy(jobStatus['Content']['ID']);
                        this.showModifiedResourceValue = modifiedStudy['MainDicomTags']['StudyInstanceUID'];
                    } else if (jobStatus['Content']['Type'] == 'Series') {
                        this.showModifiedResourceKey = "StudyInstanceUID";
                        const modifiedStudy = await api.getSeriesParentStudy(jobStatus['Content']['ID']);
                        this.showModifiedResourceValue = modifiedStudy['MainDicomTags']['StudyInstanceUID'];
                    } else if ('UserData' in jobStatus && 'PatientID' in jobStatus['UserData']) {
                        this.showModifiedResourceKey = "PatientID";
                        this.showModifiedResourceValue = jobStatus['UserData']['PatientID'];
                    } else {
                        console.error("not handled yet")
                    }
                }
            }
        },
        async selectPatientToAttachTo(patientTags) {
            this.selectedPatientTags = patientTags;
            this.modify(true);
        },
        async modify(hasAcceptedWarning = false) {
            try {
                // perform checks before modification
                if (this.action == 'attach-study-to-existing-patient') {
                    if (!hasAcceptedWarning) {
                        // make sure we use an existing PatientID
                        const newPatientIdStudies = await api.getSamePatientStudies(this.tags, ['PatientID'], true);
                        if (newPatientIdStudies.length == 0) {
                            console.error("Error while changing patient, the new PatientID does not exist ", this.tags['PatientID']);
                            this.setError('modify.error_attach_study_to_existing_patient_target_does_not_exist_html');
                            return;
                        } else {
                            let uniquePatientsSet = new Set();
                            for (const np of newPatientIdStudies) {
                                uniquePatientsSet.add(JSON.stringify(np.RequestedTags));
                            }
                            this.selectablePatientTags = [];
                            for (const np of uniquePatientsSet) {
                                this.selectablePatientTags.push(JSON.parse(np));
                            }
                            this.step = 'select-patient';
                            return;
                        }
                    } else {
                        let removedTagsList = [... this.removedTagsList];
                        if ('OtherPatientIDs' in this.originalTags && !('OtherPatientIDs' in this.selectedPatientTags)) {
                            console.warn('Removing OtherPatientIDs since it is not defined in target patient');
                            removedTagsList.push('OtherPatientIDs');
                        }

                        const jobId = await api.modifyResource({
                            resourceLevel: 'study',
                            orthancId: this.orthancId,
                            replaceTags: this.selectedPatientTags,
                            keepTags: (this.keepDicomUids ? ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID'] : []),
                            keepSource: this.keepSource,
                            removeTags: removedTagsList
                        });
                        console.log("attach-study-to-existing-patient: created job ", jobId);
                        this.startMonitoringJob(jobId);
                    }
                } else if (this.action == 'modify-any-tags-in-one-study') {

                    if ('PatientID' in this.modifiedTags) {
                        console.log("modify-any-tags-in-one-study: PatientID has changed");

                        const newPatientIdStudies = await api.getSamePatientStudies(this.modifiedTags, ['PatientID'], true);
                        if (newPatientIdStudies.length > 0) {
                            const futureStudyPatientTags = Object.fromEntries(
                                Object.entries({ ... this.originalTags, ... this.modifiedTags }).filter(([key]) => ['PatientID', 'PatientName', 'PatientBirthDate', 'PatientSex', 'OtherPatientIDs'].includes(key)));

                            console.error("modify-any-tags-in-one-study: Error while modifying patient-study tags, another patient with the same PatientID already exists ", this.tags['PatientID'], newPatientIdStudies);
                            this.setError('modify.error_modify_any_study_tags_patient_exists_html');
                            return;
                        }
                    } else if ('PatientName' in this.modifiedTags || 'PatientBirthDate' in this.modifiedTags || 'PatientSex' in this.modifiedTags) {
                        console.log("modify-any-tags-in-one-study: Patient tags have changed");
                        this.samePatientStudiesCount = (await api.getSamePatientStudies(this.originalTags, ['PatientID'], false)).length;  // here, we only use the PatientID since this is the only tag used by Orthanc when modifying the resources
                        if (this.samePatientStudiesCount > 1) {
                            console.error("modify-any-tags-in-one-study: Error while modifying patient-study tags, this patient has other studies, you can not modify patient tags ", this.originalTags['PatientID'], this.modifiedTags);
                            this.setError('modify.error_modify_any_study_tags_can_not_modify_patient_tags_because_of_other_studies_html');
                            return;
                        } else {
                            console.log("modify-any-tags-in-one-study: this is the only patient study, we can modify all tags");
                        }
                    }

                    // at this point, it is safe to modify any tags
                    const jobId = await api.modifyResource({
                        resourceLevel: 'study',
                        orthancId: this.orthancId,
                        replaceTags: this.modifiedTags,
                        keepTags: (this.keepDicomUids ? ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID'] : []),
                        keepSource: this.keepSource,
                        removeTags: this.removedTagsList
                    });
                    console.log("modify-any-tags-in-one-study: created job ", jobId);
                    this.startMonitoringJob(jobId);

                } else if (this.action == 'modify-patient-tags-in-all-studies') {
                    if (this.labels.hasAccessToAllLabels && this.system.PatientLevelEnabled) { // in this case, we can work at 'Patient' level
                        // if we try to change the PatientID, make sure we do not reuse an existing PatientID
                        if ('PatientID' in this.modifiedTags) {
                            const targetPatient = await api.findPatient(this.tags['PatientID']);
                            if (targetPatient) {
                                console.error("modify-patient-tags-in-all-studies: Error while modifying patient tags, another patient with the same PatientID already exists ", this.tags['PatientID'], targetPatient);
                                this.setError('modify.error_modify_patient_tags_another_patient_exists_with_same_patient_id_html');
                                return;
                            }
                        }

                        const originalPatient = await api.findPatient(this.originalTags['PatientID']);
                        this.updateDateTags();
                        const jobId = await api.modifyResource({
                            resourceLevel: 'patient',
                            orthancId: originalPatient['ID'],
                            replaceTags: this.tags,
                            keepTags: (this.keepDicomUids ? ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID'] : []),
                            removeTags: this.removedTagsList,
                            keepSource: this.keepSource
                        });
                        console.log("modify-patient-tags-in-all-studies: created job ", jobId);
                        this.startMonitoringJob(jobId);
                    } else { // we can not work at patient level -> work at study level
                        if (!hasAcceptedWarning) {
                            // if we try to change the PatientID, make sure we do not reuse an existing PatientID
                            if ('PatientID' in this.modifiedTags) {
                                const newPatientIdStudies = await api.getSamePatientStudies(this.modifiedTags, ['PatientID'], true);
                                if (newPatientIdStudies.length > 0) {
                                    console.error("modify-patient-tags-in-all-studies: Error while modifying patient tags, another patient with the same PatientID already exists ", this.tags['PatientID'], newPatientIdStudies[0]);
                                    this.setError('modify.error_modify_patient_tags_another_patient_exists_with_same_patient_id_html');
                                    return;
                                }
                            }

                            // find all studies with the old patient id
                            this.selectableStudies = await api.getSamePatientStudies(this.patientMainDicomTags, ['PatientID'], true);
                            if (this.selectableStudies.length == 0) {
                                console.error("Error while changing patient, the old PatientID does not exist anymore ", this.patientMainDicomTags['PatientID']);
                                this.setError('modify.error_modify_unexpected_error_html');
                                return;
                            } else {
                                for (let study of this.selectableStudies) {
                                    study.selected = true;
                                }
                                this.step = 'select-studies';
                                return;
                            }
                        } else {
                            // bulk modify studies
                            let selectedStudiesIds = [];
                            for (const study of this.selectableStudies) {
                                if (study.selected) {
                                    selectedStudiesIds.push(study.ID);
                                }
                            }

                            const jobId = await api.bulkModifyResources({
                                resourceLevel: 'Study',
                                orthancIds: selectedStudiesIds,
                                replaceTags: this.tags,
                                keepTags: (this.keepDicomUids ? ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID'] : []),
                                removeTags: this.removedTagsList,
                                keepSource: this.keepSource,
                                userData: {
                                    "PatientID": this.tags['PatientID']
                                }
                            });
                            console.log("modify-patient-tags-in-all-studies: created job (bulk)", jobId);
                            this.startMonitoringJob(jobId);
                        }
                    }
                } else if (this.action == 'anonymize-study') {
                    this.updateDateTags();
                    const jobId = await api.anonymizeResource({
                        resourceLevel: 'study',
                        orthancId: this.orthancId,
                        replaceTags: this.tags,
                        removeTags: this.removedTagsList
                    });
                    console.log("anonymize-study: created job ", jobId);
                    this.startMonitoringJob(jobId);
                } else if (this.action == 'attach-series-to-existing-study') {
                    if (this.tags['StudyInstanceUID'] == this.originalTags['StudyInstanceUID']) {
                        console.error("attach-series-to-existing-study: Error target study is same as current study ", this.tags['StudyInstanceUID']);
                        this.setError('modify.error_attach_series_to_existing_study_target_unchanged_html');
                        return;
                    } else {
                        const targetStudy = await api.findStudy(this.tags['StudyInstanceUID']);
                        if (targetStudy == null) {
                            console.error("attach-series-to-existing-study: target study not found ", this.tags['StudyInstanceUID']);
                            this.setError('modify.error_attach_series_to_existing_study_target_does_not_exist_html');
                            return;
                        } else {
                            const jobId = await api.mergeSeriesInExistingStudy({
                                seriesIds: [this.orthancId],
                                targetStudyId: targetStudy['ID'],
                                keepSource: false
                            });
                            console.log("attach-series-to-existing-study: created job ", jobId);
                            this.startMonitoringJob(jobId);
                        }
                    }
                } else if (this.action == 'create-new-study-from-series') {
                    if (!hasAcceptedWarning) {
                        const newPatientIdStudies = await api.getSamePatientStudies(this.tags, ['PatientID'], true);
                        if (newPatientIdStudies.length > 0) {  // another patient exists with the same ID, reuse ?
                            let uniquePatientsSet = new Set();
                            for (const np of newPatientIdStudies) {
                                uniquePatientsSet.add(JSON.stringify(np.RequestedTags));
                            }
                            this.selectablePatientTags = [];
                            for (const np of uniquePatientsSet) {
                                this.selectablePatientTags.push(JSON.parse(np));
                            }
                            this.step = 'select-patient';
                            return;
                        } else { // modify directly
                            // generate a new StudyInstanceUID (since we perform modification at series level, orthanc would keep even if not listed in keepTags)
                            this.tags['StudyInstanceUID'] = await api.generateUid('study');
                            this.updateDateTags();
                            const jobId = await api.modifyResource({
                                resourceLevel: 'series',
                                orthancId: this.orthancId,
                                replaceTags: this.tags,
                                keepTags: ['SeriesInstanceUID', 'SOPInstanceUID'],
                                keepSource: false
                            });
                            console.log("create-new-study-from-series: created job ", jobId);
                            this.startMonitoringJob(jobId);
                        }
                    } else {
                        console.log("create-new-study-from-series: warning accepted, copying existing Patient tags");
                        this.tags['PatientName'] = this.selectablePatientTags['PatientName'];
                        this.tags['PatientBirthDate'] = this.selectablePatientTags['PatientBirthDate'];
                        this.tags['PatientBirthDate'] = this.selectablePatientTags['PatientBirthDate'];
                        this.tags['PatientSex'] = this.selectablePatientTags['PatientSex'];
                        // generate a new StudyInstanceUID (since we perform modification at series level, orthanc would keep even if not listed in keepTags)
                        this.tags['StudyInstanceUID'] = await api.generateUid('study');

                        const jobId = await api.modifyResource({
                            resourceLevel: 'series',
                            orthancId: this.orthancId,
                            replaceTags: this.tags,
                            keepTags: ['SeriesInstanceUID', 'SOPInstanceUID'],
                            keepSource: false
                        });
                        console.log("create-new-study-from-series: created job ", jobId);
                        this.startMonitoringJob(jobId);
                    }
                } else if (this.action == 'modify-series-tags') {
                    const jobId = await api.modifyResource({
                        resourceLevel: 'series',
                        orthancId: this.orthancId,
                        replaceTags: this.modifiedTags,
                        keepTags: (this.keepDicomUids ? ['SeriesInstanceUID', 'SOPInstanceUID'] : []),
                        keepSource: this.keepSource,
                    });
                    console.log("modify-series-tags: created job ", jobId);
                    this.startMonitoringJob(jobId);
                } else if (this.action == 'anonymize-series') {
                    this.updateDateTags();
                    const jobId = await api.anonymizeResource({
                        resourceLevel: 'series',
                        orthancId: this.orthancId,
                        replaceTags: this.tags,
                        removeTags: this.removedTagsList
                    });
                    console.log("anonymize-study: created job ", jobId);
                    this.startMonitoringJob(jobId);
                } else {
                    console.error('unhandled');
                }
            } catch (err) {
                this.setError('modify.error_modify_unexpected_error_html');
                console.error(err);
            }
        },
        goToNextStep(step, action) {
            if (step == 'init') {
                this.step = 'tags';
                this.action = action;
                if (action == 'anonymize-study') {
                    const uuid = uuidv4();

                    this.tags = {};
                    this.dateTags = {};
                    this.tags['PatientID'] = uuid;
                    this.tags['PatientName'] = 'Anonymized ' + uuid.substr(0, 8);
                    this.tags['PatientBirthDate'] = '';
                    this.dateTags['PatientBirthDate'] = null;
                    this.tags['PatientSex'] = '';
                    if ('StudyDescription' in this.studyMainDicomTags) {
                        this.tags['StudyDescription'] = this.studyMainDicomTags['StudyDescription'];
                    }
                } else if (action == 'attach-study-to-existing-patient') {
                    this.tags = {};
                    this.dateTags = {};
                    this.tags['PatientID'] = this.patientMainDicomTags['PatientID'];
                    this.selectablePatientTags = [];
                    this.selectedPatientTags = {};
                } else if (action == 'modify-any-tags-in-one-study') {
                    this.tags = {};
                    this.dateTags = {};
                    this.setTagsFromDicomTags(this.patientMainDicomTags);
                    this.setTagsFromDicomTags(this.studyMainDicomTags);
                } else if (action == 'modify-patient-tags-in-all-studies') {
                    this.tags = {};
                    this.dateTags = {};
                    this.setTagsFromDicomTags(this.patientMainDicomTags);
                    this.selectableStudies = [];
                } else if (action == 'attach-series-to-existing-study') {
                    this.tags = {};
                    this.dateTags = {};
                    this.tags['StudyInstanceUID'] = "";
                } else if (action == 'create-new-study-from-series') {
                    this.tags = {};
                    this.dateTags = {};
                    this.tags['PatientID'] = '';
                    this.tags['PatientName'] = '';
                    this.tags['PatientBirthDate'] = '';
                    this.dateTags['PatientBirthDate'] = null;
                    this.tags['PatientSex'] = '';
                    this.tags['StudyDescription'] = '';
                } else if (action == 'modify-series-tags') {
                    this.tags = {};
                    this.dateTags = {};
                    this.setTagsFromDicomTags(this.seriesMainDicomTags);
                } else if (action == 'anonymize-series') {
                    const uuid = uuidv4();

                    this.tags = {};
                    this.dateTags = {};
                    this.tags['PatientID'] = uuid;
                    this.tags['PatientName'] = 'Anonymized ' + uuid.substr(0, 8);
                    this.tags['PatientBirthDate'] = '';
                    this.dateTags['PatientBirthDate'] = null;
                    this.tags['PatientSex'] = '';
                    if ('StudyDescription' in this.studyMainDicomTags) {
                        this.tags['StudyDescription'] = this.studyMainDicomTags['StudyDescription'];
                    }
                } else {
                    console.error('unknown action ', action);
                }
            } else if (this.step == 'warning') {
                this.modify();
            }
        },
        async showModifiedResources() {
            let newUrl = "";
            let query = {
                'forceRefresh': Date.now() // to force refresh
            };
            if (this.showModifiedResourceKey) {
                query[this.showModifiedResourceKey] = this.showModifiedResourceValue;
            }

            this.$router.push({ path: newUrl, query: query });
            // location.reload();
        },
        isFrozenTag(tag) {
            if (this.isRemovedTag(tag)) {
                return true;
            }
            if (this.isDicomUid(tag)) {
                return this.keepDicomUids;
            } else {
                return false;
            }
        },
        isEditableTag(tag) {
            if (this.isRemovedTag(tag)) {
                return false;
            }
            if (this.resourceLevel == 'series') {
                if (this.action == 'attach-series-to-existing-study' && tag == 'StudyInstanceUID') {
                    return true;
                }
            }

            if (this.isDicomUid(tag)) {
                return false;
            }
            return true;
        },
        isDateTag(tag) {
            return dateHelpers.isDateTag(tag);
        },
        isDicomUid(tag) {
            return ['StudyInstanceUID', 'SeriesInstanceUID', 'SOPInstanceUID'].indexOf(tag) != -1;
        },
        isAutogeneratedDicomUid(tag) {
            if (this.resourceLevel == 'series' && this.action == 'attach-series-to-existing-study') {
                return false;
            }
            if (this.isDicomUid(tag)) {
                return !this.keepDicomUids;
            }
            return false;
        },
        isRemovable(tag) {

            if (document.requiredTags.indexOf(tag) != -1) {
                return false;
            } else {
                return true;
            }
        },
        isRemovedTag(tag) {
            if (this.removedTags[tag] === undefined) {
                console.warn("isRemovedTag: undefined tag", tag);
            }
            return this.removedTags[tag];
        },
        toggleRemovedTag(tag) {
            this.removedTags[tag] = !this.removedTags[tag];
            if (this.removedTags[tag] && this.insertedTags.has(tag)) {
                // the tag has been inserted, remove it completely
                delete this.tags[tag];
                delete this.dateTags[tag];
                delete this.originalTags[tag];
                delete this.removedTags[tag];
                this.insertedTags.delete(tag);
            }

            if (this.removedTags) {
                console.log(tag + " will be removed")
            } else {
                console.log(tag + " will not be removed")
            }
        },
        insertTag(ev, tag) {
            ev.preventDefault();
            ev.stopPropagation();

            console.log("insert tag " + tag, this.tags, this.removedTags);
            this.tags[tag] = "";
            if (dateHelpers.isDateTag(tag)) {
                this.dateTags[tag] = null;
            }
            this.removedTags[tag] = false;
            this.originalTags[tag] = null;
            this.insertedTags.add(tag);
            console.log("insert tag " + tag, this.tags, this.removedTags);
        },
        isModeAllowed(mode) {
            if (this.resourceLevel == 'series' && this.uiOptions.Modifications.SeriesAllowedModes.indexOf(mode) != -1) {
                return true;
            } else if (this.resourceLevel == 'study' && this.uiOptions.Modifications.AllowedModes.indexOf(mode) != -1) {
                return true;
            }
            return false;
        },
        canSelectModes() {
            if (this.isAnonymization) {
                return false;
            }
            if (this.resourceLevel == 'series' && this.uiOptions.Modifications.SeriesAllowedModes.length > 1) {
                return this.action == 'modify-series-tags';
            } else if (this.resourceLevel == 'study' && this.uiOptions.Modifications.AllowedModes.length > 1) {
                return true;
            }
            return false;
        },
        updateDateTags() {
            for (const [k,] of Object.entries(this.tags)) {
                if (dateHelpers.isDateTag(k)) {
                    this.tags[k] = dateHelpers.dicomDateFromDatePicker(this.dateTags[k]);

                    if (this.tags[k] == null && this.originalTags[k] == '') {
                        // for https://github.com/orthanc-server/orthanc-explorer-2/issues/72
                        // this way, we do not detect a false change from '' to null
                        this.tags[k] = '';
                    }
                }
            }
        },
    },
    computed: {
        ...mapState({
            uiOptions: state => state.configuration.uiOptions,
            labels: state => state.labels,
            system: state => state.configuration.system,
        }),
        resourceTitle() {
            return resourceHelpers.getResourceTitle(this.resourceLevel, this.patientMainDicomTags, this.studyMainDicomTags, this.seriesMainDicomTags, null);
        },
        keepSource() {
            if (this.modificationMode == "duplicate") {
                return true;
            } else if (this.modificationMode == "modify-new-uids") {
                return false;
            } else if (this.modificationMode == "modify-keep-uids") {
                if (this.action == "attach-study-to-existing-patient") { // PatientID changes -> orthancID changes -> delete original
                    return false;
                } else if (this.action == "modify-any-tags-in-one-study" || this.action == "modify-patient-tags-in-all-studies") {
                    if ('PatientID' in this.modifiedTags) {
                        return false; // PatientID changes -> orthancID changes -> delete original
                    } else {
                        return true; // PatientID does not change -> orthancID does not change -> do not delete original
                    }
                } else {
                    console.error("unhandled");
                }
            } else {
                console.error("unhandled");
            }
        },
        keepDicomUids() {
            if (this.modificationMode == "modify-keep-uids") {
                return true;
            } else {
                return false;
            }
        },
        areTagsModified() {
            let areTagsRemoved = false;
            for (const [, v] of Object.entries(this.removedTags)) {
                if (v) {
                    areTagsRemoved = true;
                }
            }
            return (Object.keys(this.modifiedTags).length > 0 || areTagsRemoved)
        },
        modifiedTags() {
            this.updateDateTags();

            let modifiedTags = {};
            for (const [k,] of Object.entries(this.tags)) {
                if (this.tags[k] != this.originalTags[k] && !this.removedTags[k]) {
                    modifiedTags[k] = this.tags[k];
                }
            }
            return modifiedTags;
        },
        insertableTags() {
            let tags = [];
            if (this.action == 'modify-any-tags-in-one-study') {
                tags = tags.concat(this.uiOptions.StudyMainTags);
                tags = tags.concat(this.uiOptions.PatientMainTags);
            }
            const that = this;
            tags = tags.filter(function (value) {
                return !(value in that.originalTags);
            })

            return tags.sort();
        },
        removedTagsList() {
            let l = [];
            for (const [k, v] of Object.entries(this.removedTags)) {
                if (v) {
                    l.push(k);
                }
            }
            return l;
        },
        modifyButtonTitle() {
            if (this.isAnonymization) {
                return 'modify.anonymize_button_title';
            } else {
                return 'modify.modify_button_title';
            }
        },
        modifyModeNewUidText() {
            if (this.resourceLevel == 'series') {
                return 'modify.modify_series_mode_new_uids_html'
            } else {
                return 'modify.modify_study_mode_new_uids_html'
            }
        },
        modifyModeKeepUidText() {
            if (this.resourceLevel == 'series') {
                return 'modify.modify_series_mode_keep_uids_html'
            } else {
                return 'modify.modify_study_mode_keep_uids_html'
            }
        },
        modifyModeDuplicateText() {
            if (this.resourceLevel == 'series') {
                return 'modify.modify_series_mode_duplicate_html'
            } else {
                return 'modify.modify_study_mode_duplicate_html'
            }
        },
        modifyButtonEnabled() {
            if (this.isAnonymization) {
                return true;
            } else {
                if (this.action == 'modify-patient-tags-in-all-studies') { // specific use case for https://github.com/orthanc-server/orthanc-explorer-2/issues/37
                    return true;
                } else {
                    return this.areTagsModified;
                }
            }
        },
        isDarkMode() {
            // hack to switch the theme: get the value from our custom css
            let bootstrapTheme = document.documentElement.getAttribute("data-bs-theme"); // for production
            bootstrapTheme = getComputedStyle(document.documentElement).getPropertyValue('--bootstrap-theme');  // for dev
            console.log("DatePicker color mode is ", bootstrapTheme);
            return bootstrapTheme == "dark";
        },
        datePickerFormat() {
            return this.uiOptions.DateFormat;
        }

    },
    components: { CopyToClipboardButton, ResourceDetailText }

};
</script>

<template>
    <div class="modal fade" :id="this.orthancId" tabindex="-1" aria-labelledby="modalLabel" ref="modal-main-div">
        <!-- aria-hidden="true" -->
        <div class="modal-dialog modal-lg modal-dialog-scrollable" >
            <!-- style="max-width: 80vw; width: auto" -->
            <div class="modal-content">
                <div class="modal-header">
                    <h5 v-if="!isAnonymization" class="modal-title" id="modalLabel">{{ $t("modify.modify_modal_title") +
                        " " + resourceTitle }} </h5>
                    <h5 v-if="isAnonymization" class="modal-title" id="modalLabel">{{ $t("modify.anonymize_modal_title")
                        + " " + resourceTitle }} </h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>

                <!-- ------------------------------------------ step 'init' --------------------------------------------------->
                <div v-if="step == 'init'" class="modal-body">
                    <div class="container">
                        <div v-if="resourceLevel == 'study' && !isAnonymization" class="row border-bottom pb-3">
                            <div class="col-md-9"
                                v-html="$t('modify.study_step_0_attach_study_to_existing_patient_html')">
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.study_step_0_attach_study_to_existing_patient_button_title_html')"
                                    @click="goToNextStep(step, 'attach-study-to-existing-patient')"></button>
                            </div>
                        </div>
                        <div v-if="resourceLevel == 'study' && !isAnonymization && samePatientStudiesCount > 1"
                            class="row border-bottom border-3 py-3">
                            <div class="col-md-9"
                                style="white-space: pre-line" v-html="$t('modify.study_step_0_patient_has_other_studies_html', { count: samePatientStudiesCount })">
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.study_step_0_patient_has_other_studies_button_title_html')"
                                    @click="goToNextStep(step, 'modify-patient-tags-in-all-studies')"></button>
                            </div>
                        </div>
                        <div v-if="resourceLevel == 'study' && !isAnonymization && samePatientStudiesCount >= 1"
                            class="row pt-3">
                            <div class="col-md-9"
                                v-html="$t('modify.study_step_0_modify_study_html', { count: samePatientStudiesCount })">
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.study_step_0_modify_study_button_title_html')"
                                    @click="goToNextStep(step, 'modify-any-tags-in-one-study')"></button>
                            </div>
                        </div>
                        <div v-if="resourceLevel == 'series' && !isAnonymization" class="row border-bottom pb-3">
                            <div class="col-md-9"
                                v-html="$t('modify.series_step_0_attach_series_to_existing_study_html')">
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.series_step_0_attach_series_to_existing_study_button_title_html')"
                                    @click="goToNextStep(step, 'attach-series-to-existing-study')"></button>
                            </div>
                        </div>
                        <div v-if="resourceLevel == 'series' && !isAnonymization"
                            class="row border-bottom border-3 py-3">
                            <div class="col-md-9" v-html="$t('modify.series_step_0_create_new_study_html')">
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.series_step_0_create_new_study_button_title_html')"
                                    @click="goToNextStep(step, 'create-new-study-from-series')"></button>
                            </div>
                        </div>
                        <div v-if="resourceLevel == 'series' && !isAnonymization" class="row pt-3">
                            <div class="col-md-9" v-html="$t('modify.series_step_0_modify_series_html')">
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.series_step_0_modify_series_button_title_html')"
                                    @click="goToNextStep(step, 'modify-series-tags')"></button>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-if="step == 'init'" class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("cancel") }}</button>
                </div>

                <!-- ------------------------------------------ step 'tags' --------------------------------------------------->
                <div v-if="step == 'tags'" class="modal-body">
                    <div class="container" style="min-height: 50vh"> <!-- min height for the date picker-->
                        <div v-for="(item, key) in tags" :key="key" class="row">
                            <!----  label  ---->
                            <div class="col-md-5" :class="{ 'striked-through': isRemovedTag(key) }">
                                {{ key }}
                            </div>

                            <!----  edit text  ---->
                            <div v-if="isFrozenTag(key)" class="col-md-6">
                                <input v-if="true" type="text" class="form-control" disabled
                                    :class="{ 'striked-through': !isDicomUid(key) }" v-model="originalTags[key]" />
                            </div>
                            <div v-if="isEditableTag(key) && !isDateTag(key)" class="col-md-6">
                                <input v-if="true" type="text" class="form-control" v-model="tags[key]" />
                            </div>

                            <div v-if="isEditableTag(key) && isDateTag(key)" class="col-md-6">
                                <Datepicker v-model="dateTags[key]" :range="false" :enable-time-picker="false"
                                    :format="datePickerFormat" :preview-format="datePickerFormat" hide-input-icon
                                    text-input arrow-navigation :highlight="{ weekdays: [6, 0] }" :dark="isDarkMode">
                                </Datepicker>
                            </div>

                            <div v-if="isAutogeneratedDicomUid(key)" class="col-md-6">
                                <input v-if="true" type="text" class="form-control" disabled
                                    :value="$t('modify.autogenerated_dicom_uid')" />
                            </div>

                            <!----  buttons  ---->
                            <div v-if="true" class="col-md-1">
                                <div class="d-flex flex-row-reverse">
                                    <button v-if="isRemovable(key) && !isRemovedTag(key)" class="btn-small"
                                        type="button" data-bs-toggle="tooltip" :title="$t('modify.remove_tag_tooltip')"
                                        @click="toggleRemovedTag(key)">
                                        <i v-if="!isRemovedTag(key)" class="bi-trash"></i>
                                    </button>
                                    <button v-if="isRemovable(key) && isRemovedTag(key)" class="btn-small" type="button"
                                        data-bs-toggle="tooltip" :title="$t('modify.remove_tag_undo_tooltip')"
                                        @click="toggleRemovedTag(key)">
                                        <i v-if="isRemovedTag(key)" class="bi-recycle"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                        <div v-if="action == 'modify-any-tags-in-one-study'" class="row pt-3">
                            <div class="col-md-8"></div>
                            <div class="col-md-2">
                                <div class="dropdown">
                                    <button class="btn btn-sm btn-secondary dropdown-toggle" type="button"
                                        id="dropdownInsertTag" data-bs-toggle="dropdown" aria-expanded="false">
                                        {{ $t('modify.insert_tag') }}
                                    </button>
                                    <ul class="dropdown-menu">
                                        <li v-for="key of insertableTags" :key="key" :value="key"><a
                                                class="dropdown-item" href="#" @click="insertTag($event, key)">{{ key
                                                }}</a>
                                        </li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                        <div v-if="canSelectModes()" class="row pt-3">
                            <div v-if="isModeAllowed('modify-new-uids')" class="form-check">
                                <input class="form-check-input" type="radio" name="modificationMode" id="modifyNewUids"
                                    value="modify-new-uids" v-model="modificationMode">
                                <label class="form-check-label" for="modifyNewUids" v-html="$t(modifyModeNewUidText)">
                                </label>
                            </div>
                            <div v-if="isModeAllowed('modify-keep-uids')" class="form-check">
                                <input class="form-check-input" type="radio" name="modificationMode" id="modifyKeepUids"
                                    value="modify-keep-uids" v-model="modificationMode">
                                <label class="form-check-label" for="modifyKeepUids" v-html="$t(modifyModeKeepUidText)">
                                </label>
                            </div>
                            <div v-if="isModeAllowed('duplicate')" class="form-check">
                                <input class="form-check-input" type="radio" name="modificationMode"
                                    id="modifyDuplicate" value="duplicate" v-model="modificationMode">
                                <label class="form-check-label" for="modifyDuplicate"
                                    v-html="$t(modifyModeDuplicateText)">
                                </label>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-if="step == 'tags'" class="modal-footer">
                    <button v-if="!isAnonymization" type="button" class="btn btn-secondary" @click="back()">{{
                        $t("modify.back_button_title")
                    }}</button>
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("cancel") }}</button>
                    <button type="button" class="btn btn-primary" :disabled="!modifyButtonEnabled" @click="modify()">{{
                        $t(modifyButtonTitle)
                    }}</button>
                </div>

                <!-- ------------------------------------------ step 'warning' or 'error' --------------------------------------------------->
                <div v-if="step == 'warning' || step == 'error'" class="modal-body">
                    <div class="container">
                        <div v-if="step == 'error'" class="alert alert-danger" role="alert" v-html="$t(errorMessageId)">
                        </div>
                        <div v-if="step == 'warning'" class="alert alert-warning" role="alert"
                            v-html="$t(warningMessageId)">
                        </div>
                    </div>
                </div>
                <div v-if="step == 'warning'" class="modal-footer">
                    <button type="button" class="btn btn-secondary" @click="back()">{{
                        $t("modify.back_button_title")
                    }}</button>
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("cancel") }}</button>
                    <button type="button" class="btn btn-primary" @click="modify(true)">{{
                        $t(modifyButtonTitle)
                    }}</button>
                </div>
                <div v-if="step == 'error'" class="modal-footer">
                    <button type="button" class="btn btn-secondary" @click="back()">{{
                        $t("modify.back_button_title")
                    }}</button>
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("cancel") }}</button>
                </div>

                <!-- ------------------------------------------ step 'select-patient' --------------------------------------------------->
                <div v-if="step == 'select-patient'" class="modal-body">
                    <div class="container">
                        <div class="row pb-2">
                            {{ $t("modify.validate_select_patient") }}
                        </div>
                        <div v-for="patientTags in selectablePatientTags" class="row border-top py-2">
                            <div class="col-md-9">
                                <table class="table table-responsive table-sm patient-tags-table table-borderless">
                                    <tbody>
                                        <tr>
                                            <td width="100%" class="cut-text">
                                                <ul>
                                                    <ResourceDetailText v-for="tag in uiOptions.PatientMainTags"
                                                        :key="tag" :tags="patientTags" :tag="tag" :showIfEmpty="true" :copy="false">
                                                    </ResourceDetailText>
                                                </ul>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                            <div class="col-md-3">
                                <button type="button" class="btn btn-primary w-100"
                                    v-html="$t('modify.select_this_patient')"
                                    @click="selectPatientToAttachTo(patientTags)"></button>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-if="step == 'select-patient'" class="modal-footer">
                    <button type="button" class="btn btn-secondary" @click="back()">{{
                        $t("modify.back_button_title")
                    }}</button>
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("cancel") }}</button>
                </div>

                <!-- ------------------------------------------ step 'select-studies' --------------------------------------------------->
                <div v-if="step == 'select-studies'" class="modal-body">
                    <div class="container">
                        <div class="row pb-2">
                            {{ $t("modify.select_the_studies_to_apply_the_changes") }}
                        </div>
                        <div v-for="study in selectableStudies" class="row border-top py-2">
                            <div class="col-md-6">
                                <table class="table table-responsive table-sm patient-tags-table table-borderless">
                                    <tbody>
                                        <tr>
                                            <td width="100%" class="cut-text">
                                                <ul class="px-0">
                                                    <ResourceDetailText v-for="tag in uiOptions.StudyMainTags"
                                                        :key="tag" :tags="study.MainDicomTags" :tag="tag" :showIfEmpty="true" :copy="false">
                                                    </ResourceDetailText>
                                                </ul>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                            <div class="col-md-5">
                                <table class="table table-responsive table-sm patient-tags-table table-borderless">
                                    <tbody>
                                        <tr>
                                            <td width="100%" class="cut-text">
                                                <ul class="px-0">
                                                    <ResourceDetailText v-for="tag in uiOptions.PatientMainTags"
                                                        :key="tag" :tags="study.PatientMainDicomTags" :tag="tag" :showIfEmpty="true" :copy="false">
                                                    </ResourceDetailText>
                                                </ul>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                            <div class="col-md-1">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" v-model="study.selected">
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-if="step == 'select-studies'" class="modal-footer">
                    <button type="button" class="btn btn-secondary" @click="back()">{{
                        $t("modify.back_button_title")
                    }}</button>
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("cancel") }}</button>
                    <button type="button" class="btn btn-primary" @click="modify(true)">{{
                        $t(modifyButtonTitle)
                    }}</button>
                </div>

                <!-- ------------------------------------------ step 'progress' and 'done' --------------------------------------------------->

                <div v-if="step == 'progress' || step == 'done'" class="modal-body">
                    <div class="container">
                        <div class="row pb-3">{{ $t('modify.job_in_progress') }}</div>
                        <div class="row">
                            <div class="progress" style="width:100%">
                                <div class="progress-bar bg-success" role="progressbar"
                                    v-bind:style="'width: ' + this.jobProgressComplete + '%'"></div>
                                <div class="progress-bar bg-secondary" role="progressbar"
                                    v-bind:style="'width: ' + this.jobProgressRemaining + '%'"></div>
                                <div class="progress-bar bg-danger" role="progressbar"
                                    v-bind:style="'width: ' + this.jobProgressFailed + '%'"></div>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-if="step == 'progress' || step == 'done'" class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("close") }}</button>
                    <button type="button" class="btn btn-primary" :disabled="step != 'done'" data-bs-dismiss="modal"
                        @click="showModifiedResources()">{{ $t("modify.show_modified_resources") }}</button>
                </div>

            </div>
        </div>
    </div>
</template>

<style>
/* The switch - the box around the slider */
.switch {
    position: relative;
    display: inline-block;
    width: 60px;
    height: 34px;
}

/* Hide default HTML checkbox */
.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

/* The slider */
.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    -webkit-transition: .4s;
    transition: .4s;
}

.slider:before {
    position: absolute;
    content: "";
    height: 26px;
    width: 26px;
    left: 4px;
    bottom: 4px;
    background-color: white;
    -webkit-transition: .4s;
    transition: .4s;
}

input:checked+.slider {
    background-color: #2196F3;
}

input:focus+.slider {
    box-shadow: 0 0 1px #2196F3;
}

input:checked+.slider:before {
    -webkit-transform: translateX(26px);
    -ms-transform: translateX(26px);
    transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
    border-radius: 34px;
}

.slider.round:before {
    border-radius: 50%;
}

.disabled-text {
    color: #888;
}

.btn-small {
    line-height: var(--bs-body-line-height);
    border: 0;
    background-color: transparent;
    border-radius: 0.2rem;
}

.striked-through {
    text-decoration: line-through;
}

.patient-tags-table {
    font-size: 0.8rem;
}
</style>