package mara.mybox.controller;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import mara.mybox.color.IccHeader;
import mara.mybox.color.IccProfile;
import mara.mybox.color.IccTag;
import mara.mybox.color.IccTagType;
import mara.mybox.color.IccTags;
import mara.mybox.color.IccXML;
import mara.mybox.db.data.FileBackup;
import mara.mybox.db.data.VisitHistory;
import mara.mybox.db.data.VisitHistoryTools;
import mara.mybox.dev.MyBoxLog;
import mara.mybox.fxml.FxSingletonTask;
import mara.mybox.fxml.menu.MenuTools;
import mara.mybox.fxml.NodeTools;
import mara.mybox.fxml.RecentVisitMenu;
import mara.mybox.fxml.ValidationTools;
import mara.mybox.fxml.style.NodeStyleTools;
import mara.mybox.tools.ByteTools;
import static mara.mybox.tools.ByteTools.bytesToHexFormat;
import mara.mybox.tools.DateTools;
import mara.mybox.tools.FileNameTools;
import mara.mybox.tools.TextFileTools;
import mara.mybox.value.AppValues;
import mara.mybox.value.AppVariables;
import mara.mybox.value.FileFilters;
import static mara.mybox.value.Languages.message;
import mara.mybox.value.UserConfig;

/**
 * @Author Mara
 * @CreateDate 2019-5-13
 * @Description
 * @License Apache License Version 2.0
 */
public class IccProfileEditorController extends ChromaticityBaseController {

    protected SourceType sourceType;
    protected String embedICCName, externalDataName;
    protected boolean isIccFile, inputsValid;
    protected IccProfile profile;
    private IccHeader header;
    private IccTags tags;
    protected ObservableList<IccTag> tagsTable = FXCollections.observableArrayList();

    @FXML
    protected ComboBox<String> embedBox, cmmTypeBox, deviceClassBox, colorSpaceBox, PCSTypeBox,
            platformBox, manufacturerBox, intentBox, creatorBox;
    @FXML
    protected TextField profileVersionInput, createTimeInput, profileFileInput, deviceModelInput,
            xInput, yInput, zInput, profileIdInput, spectralPCSInput, spectralPCSRangeInput, bispectralPCSRangeInput,
            mcsInput, subClassInput, subclassVersionInput,
            xOutput, yOutput, zOutput,
            tagDisplay, tagNameDisplay, tagTypeDisplay, tagOffsetDisplay, tagSizeDisplay,
            maxDecodeInput;
    @FXML
    protected CheckBox embedCheck, independentCheck, subsetCheck,
            transparentcyCheck, matteCheck, negativeCheck, bwCheck, paperCheck, texturedCheck,
            isotropicCheck, selfLuminousCheck, idAutoCheck, lutNormalizeCheck, openExportCheck;
    @FXML
    protected Label infoLabel, cmmTypeMarkLabel, deviceClassMarkLabel, colorSpaceMarkLabel, PCSTypeMarkLabel,
            platformMarkLabel, manufacturerMarkLabel, intentMarkLabel, creatorMarkLabel,
            profileVersionMarkLabel, createTimeMarkLabel, profileFileMarkLabel, deviceModelMarkLabel,
            xMarkLabel, yMarkLabel, zMarkLabel, embedMarkLabel, independentMarkLabel, subsetMarkLabel,
            transparentcyMarkLabel, matteMarkLabel, negativeMarkLabel, bwMarkLabel, paperMarkLabel, texturedMarkLabel,
            isotropicMarkLabel, selfLuminousMarkLabel, cieDataLabel;
    @FXML
    protected TextArea summaryArea, xmlArea, tagDescDisplay, tagDataDisplay, tagBytesDisplay;
    @FXML
    protected TableView<IccTag> tagsTableView;
    @FXML
    protected TableColumn<IccTag, String> tagColumn, nameColumn, typeColumn;
    @FXML
    protected TableColumn<IccTag, Integer> offsetColumn, sizeColumn;
    @FXML
    protected VBox headerBox, tagDataBox, csInputBox, chromaticDiagramBox;
    @FXML
    protected TabPane displayPane;
    @FXML
    protected Tab tagDataTab;
    @FXML
    protected Button refreshHeaderButton, refreshXmlButton, exportXmlButton, backupButton;
    @FXML
    protected FlowPane opsPane;

    protected enum SourceType {
        Embed, Internal_File, External_File, External_Data
    };

    public IccProfileEditorController() {
        baseTitle = message("IccProfileEditor");
        TipsLabelKey = "IccProfileTips";
    }

    @Override
    public void setFileType() {
        setFileType(VisitHistory.FileType.Icc);
    }

    @Override
    public void initControls() {
        try {
            super.initControls();
            sourceFile = null;
            embedICCName = null;

            initToolbar();
            initHeaderControls();
            initTagsTable();
            initOptions();

            opsPane.setDisable(true);

        } catch (Exception e) {
            MyBoxLog.error(e);
        }

    }

    @Override
    public void setControlsStyle() {
        try {
            super.setControlsStyle();
            NodeStyleTools.setTooltip(maxDecodeInput, new Tooltip(message("MaxDecodeComments")));
        } catch (Exception e) {
            MyBoxLog.debug(e);
        }
    }

    protected void initToolbar() {
        try {
            embedBox.getItems().addAll(Arrays.asList(
                    "sRGB", "XYZ", "PYCC", "GRAY", "LINEAR_RGB",
                    "ECI_RGB_v2_ICCv4.icc", "ECI_CMYK.icc",
                    "AdobeRGB_1998.icc", "Adobe_AppleRGB.icc", "Adobe_ColorMatchRGB.icc",
                    "AdobeCMYK_CoatedFOGRA27.icc", "AdobeCMYK_CoatedFOGRA39.icc", "AdobeCMYK_UncoatedFOGRA29.icc", "AdobeCMYK_WebCoatedFOGRA28.icc",
                    "AdobeCMYK_JapanColor2001Coated.icc", "AdobeCMYK_JapanColor2001Uncoated.icc", "AdobeCMYK_JapanColor2002Newspaper.icc",
                    "AdobeCMYK_JapanWebCoated.icc", "AdobeCMYK_USSheetfedCoated.icc", "AdobeCMYK_USSheetfedUncoated.icc",
                    "AdobeCMYK_USWebCoatedSWOP.icc", "AdobeCMYK_USWebUncoated.icc"
            ));
            embedBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    iccChanged();
                }
            });
//            embedBox.getSelectionModel().select(0);

//            saveButton.disableProperty().bind(profileVersionInput.styleProperty().isEqualTo(badStyle)
//                    .or(createTimeInput.styleProperty().isEqualTo(badStyle))
//                    .or(xInput.styleProperty().isEqualTo(badStyle))
//                    .or(yInput.styleProperty().isEqualTo(badStyle))
//                    .or(zInput.styleProperty().isEqualTo(badStyle))
//            );
//            saveAsButton.disableProperty().bind(saveButton.disableProperty()
//            );
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    protected void initHeaderControls() {
        try {
            List<String> manuList = new ArrayList<>();
            for (String[] item : IccHeader.DeviceManufacturers) {
                manuList.add(item[0] + AppValues.Indent + item[1]);
            }
            cmmTypeBox.getItems().addAll(manuList);
            cmmTypeBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (!cmmTypeBox.getItems().contains(newValue)) {
                        ValidationTools.setEditorWarnStyle(cmmTypeBox);
                    } else {
                        ValidationTools.setEditorNormal(cmmTypeBox);
                    }
                    if (isSettingValues) {
                        return;
                    }
                    cmmTypeMarkLabel.setText("*");
                    profileChanged();
                }
            });

            profileVersionInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    profileVersionMarkLabel.setText("*");
                    profileChanged();
                }
            });

            List<String> classList = new ArrayList<>();
            for (String[] item : IccHeader.ProfileDeviceClasses) {
                classList.add(item[0] + AppValues.Indent + message(item[1]));
            }
            deviceClassBox.getItems().addAll(classList);
            deviceClassBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    deviceClassMarkLabel.setText("*");
                    profileChanged();
                }
            });

            List<String> csList = Arrays.asList(IccHeader.ColorSpaceTypes);
            colorSpaceBox.getItems().addAll(csList);
            colorSpaceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (!colorSpaceBox.getItems().contains(newValue)) {
                        ValidationTools.setEditorWarnStyle(colorSpaceBox);
                    } else {
                        ValidationTools.setEditorNormal(colorSpaceBox);
                    }
                    if (isSettingValues) {
                        return;
                    }
                    colorSpaceMarkLabel.setText("*");
                    profileChanged();
                }
            });

            PCSTypeBox.getItems().addAll(Arrays.asList(IccHeader.PCSTypes));
            PCSTypeBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    PCSTypeMarkLabel.setText("*");
                    profileChanged();
                }
            });

            createTimeInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (IccTagType.dateTimeBytes(newValue) != null) {
                        createTimeInput.setStyle(null);
                    } else {
                        createTimeInput.setStyle(UserConfig.badStyle());
                    }
                    if (isSettingValues) {
                        return;
                    }
                    createTimeMarkLabel.setText("*");
                    profileChanged();
                }
            });

            profileFileInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    profileFileMarkLabel.setText("*");
                    profileChanged();
                }
            });

            List<String> platformsList = new ArrayList<>();
            for (String[] item : IccHeader.PrimaryPlatforms) {
                platformsList.add(item[0] + AppValues.Indent + item[1]);
            }
            platformBox.getItems().addAll(platformsList);
            platformBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    platformMarkLabel.setText("*");
                    profileChanged();
                }
            });

            embedCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    embedMarkLabel.setText("*");
                    profileChanged();
                }
            });

            independentCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    independentMarkLabel.setText("*");
                    profileChanged();
                }
            });

            subsetCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    subsetMarkLabel.setText("*");
                    profileChanged();
                }
            });

            manufacturerBox.getItems().addAll(manuList);
            manufacturerBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    if (!manufacturerBox.getItems().contains(newValue)) {
                        ValidationTools.setEditorWarnStyle(manufacturerBox);
                    } else {
                        ValidationTools.setEditorNormal(manufacturerBox);
                    }
                    manufacturerMarkLabel.setText("*");
                    profileChanged();
                }
            });

            deviceModelInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    deviceModelMarkLabel.setText("*");
                    profileChanged();
                }
            });

            transparentcyCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    transparentcyMarkLabel.setText("*");
                    profileChanged();
                }
            });

            matteCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    matteMarkLabel.setText("*");
                    profileChanged();
                }
            });

            negativeCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    negativeMarkLabel.setText("*");
                    profileChanged();
                }
            });

            bwCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    bwMarkLabel.setText("*");
                    profileChanged();
                }
            });

            paperCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    paperMarkLabel.setText("*");
                    profileChanged();
                }
            });

            texturedCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    texturedMarkLabel.setText("*");
                    profileChanged();
                }
            });

            isotropicCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    isotropicMarkLabel.setText("*");
                    profileChanged();
                }
            });

            selfLuminousCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    selfLuminousMarkLabel.setText("*");
                    profileChanged();
                }
            });

            List<String> intents = new ArrayList<>();
            for (String item : IccHeader.RenderingIntents) {
                intents.add(message(item));
            }
            intentBox.getItems().addAll(intents);
            intentBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (isSettingValues) {
                        return;
                    }
                    intentMarkLabel.setText("*");
                    profileChanged();
                }
            });

            xInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    try {
                        double v = Double.parseDouble(newValue);
                        xInput.setStyle(null);
                    } catch (Exception e) {
                        xInput.setStyle(UserConfig.badStyle());
                    }
                    if (isSettingValues) {
                        return;
                    }
                    xMarkLabel.setText("*");
                    profileChanged();
                }
            });

            yInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    try {
                        double v = Double.parseDouble(newValue);
                        yInput.setStyle(null);
                    } catch (Exception e) {
                        yInput.setStyle(UserConfig.badStyle());
                    }
                    if (isSettingValues) {
                        return;
                    }
                    yMarkLabel.setText("*");
                    profileChanged();
                }
            });

            zInput.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    try {
                        double v = Double.parseDouble(newValue);
                        zInput.setStyle(null);
                    } catch (Exception e) {
                        zInput.setStyle(UserConfig.badStyle());
                    }
                    if (isSettingValues) {
                        return;
                    }
                    zMarkLabel.setText("*");
                    profileChanged();
                }
            });

            creatorBox.getItems().addAll(manuList);
            creatorBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue ov, String oldValue, String newValue) {
                    if (!creatorBox.getItems().contains(newValue)) {
                        ValidationTools.setEditorWarnStyle(creatorBox);
                    } else {
                        ValidationTools.setEditorNormal(creatorBox);
                    }
                    if (isSettingValues) {
                        return;
                    }
                    creatorMarkLabel.setText("*");
                    profileChanged();
                }
            });
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    protected void initTagsTable() {
        tagColumn.setCellValueFactory(new PropertyValueFactory<>("tag"));
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        typeColumn.setCellValueFactory(new PropertyValueFactory<>("type"));
        offsetColumn.setCellValueFactory(new PropertyValueFactory<>("offset"));
        sizeColumn.setCellValueFactory(new PropertyValueFactory<>("size"));

        tagsTableView.setItems(tagsTable);
        tagsTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue ov, Object t, Object t1) {
                IccTag selected = tagsTableView.getSelectionModel().getSelectedItem();
                displayTagData(selected);
            }
        });
    }

    @Override
    public void initOptions() {
        maxDecodeInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                checkMaxDecode();
            }
        });
        maxDecodeInput.setText(UserConfig.getInt("ICCMaxDecodeNumber", 500) + "");

        lutNormalizeCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                UserConfig.setBoolean("LutNormalize", newValue);
            }
        });
        lutNormalizeCheck.setSelected(UserConfig.getBoolean("LutNormalize", true));
    }

    private void checkMaxDecode() {
        try {
            String s = maxDecodeInput.getText().trim();
            if (s.isEmpty()) {
                xInput.setStyle(null);
                UserConfig.setInt("ICCMaxDecodeNumber", Integer.MAX_VALUE);
                return;
            }
            int v = Integer.parseInt(s);
            if (v > 0) {
                UserConfig.setInt("ICCMaxDecodeNumber", v);
                xInput.setStyle(null);
            } else {
                xInput.setStyle(UserConfig.badStyle());
            }
        } catch (Exception e) {
            xInput.setStyle(UserConfig.badStyle());
        }
    }

    @Override
    public boolean afterSceneLoaded() {
        if (!super.afterSceneLoaded()) {
            return false;
        }
        String name = getCurrentName();
        if (name != null) {
            myStage.setTitle(getBaseTitle() + "  " + name);
        }
        return true;
    }

    private String getCurrentName() {
        if (null != sourceType) {
            switch (sourceType) {
                case Internal_File:
                case External_File:
                    if (sourceFile != null) {
                        return sourceFile.getAbsolutePath();
                    }
                    break;
                case Embed:
                    if (embedICCName != null) {
                        return message("JavaEmbeddedColorModel") + ": " + embedICCName;
                    }
                    break;
                case External_Data:
                    if (externalDataName != null) {
                        return externalDataName;
                    }
                    break;
                default:
                    break;
            }
        }
        return "";
    }

    @Override
    public void selectSourceFileDo(File file) {
        if (file == null) {
            return;
        }
        recordFileOpened(file);
        sourceType = SourceType.External_File;
        openProfile(file.getAbsolutePath());

    }

    private void iccChanged() {
        if (isSettingValues) {
            return;
        }
        String name = embedBox.getSelectionModel().getSelectedItem();
        if (name == null || name.trim().isEmpty()) {
            return;
        }
        switch (name) {
            case "sRGB":
            case "XYZ":
            case "PYCC":
            case "GRAY":
            case "LINEAR_RGB":
                sourceType = SourceType.Embed;
                openProfile(name);
                break;
            default:
                sourceType = SourceType.Internal_File;
                openProfile(name);
        }

    }

    public void externalData(String name, byte[] data) {
        try {
            if (data == null) {
                return;
            }
            sourceType = SourceType.External_Data;
            sourceFile = null;
            externalDataName = name;
            embedBox.getSelectionModel().clearSelection();
            profile = new IccProfile(data);
            displayProfileData();
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    private void openProfile(final String name) {
        if (name == null || name.trim().isEmpty()) {
            return;
        }
        if (task != null) {
            task.cancel();
        }
        final String inputName;
        if (sourceType == SourceType.Embed) {
            inputName = message("JavaEmbeddedColorModel") + ": " + name;
        } else {
            inputName = message("File") + ": " + name;
        }
        task = new FxSingletonTask<Void>(this) {

            private File file = null;
            private IccProfile p;

            @Override
            protected boolean handle() {
                try {
                    switch (sourceType) {
                        case Embed:
                            p = new IccProfile(name);
                            break;
                        case Internal_File:
                            file = mara.mybox.fxml.FxFileTools.getInternalFile("/data/ICC/" + name, "ICC", name);
                            p = new IccProfile(file);
                            break;
                        case External_File:
                            file = new File(name);
                            p = new IccProfile(file);
                    }
                } catch (Exception e) {
                    error = e.toString();
                }
                return p != null && p.getHeader() != null;
            }

            @Override
            protected void whenSucceeded() {
                isIccFile = sourceType != SourceType.Embed;
                if (isIccFile) {
                    sourceFile = file;
                    isSettingValues = true;
                    embedICCName = null;
                    isSettingValues = false;
                } else {
                    embedICCName = name;
                    sourceFile = null;
                }
                if (sourceType == SourceType.External_File) {
                    embedBox.getSelectionModel().clearSelection();
                }
                profile = p;
                displayProfileData();
            }

            @Override
            protected void whenFailed() {
                if (error == null) {
                    if (p != null && p.getError() != null) {
                        error = p.getError();
                    } else {
                        error = message("Invalid");
                    }
                }
                popError(inputName + " " + error);
            }

        };
        start(task, inputName + " " + message("Loading..."));
    }

    private void displayProfileData() {
        if (profile == null || !profile.isIsValid()) {
            popError(message("IccInvalid"));
            return;
        }
        if (task != null) {
            task.cancel();
        }
        profile.setNormalizeLut(lutNormalizeCheck.isSelected());
        infoLabel.setText(getCurrentName());
        if (myStage != null) {
            myStage.setTitle(getBaseTitle() + "  " + getCurrentName());
        }
        resetMarkLabel(headerBox);
        backupButton.setDisable(sourceFile == null);
        inputsValid = true;
        task = new FxSingletonTask<Void>(this) {

            private String xml;

            @Override
            protected boolean handle() {
                header = profile.getHeader();
                header.readFields();
                tags = profile.getTags();
                tags.readTags();
                xml = IccXML.iccXML(header, tags);
                if (xml == null) {
                    error = message("IccInvalid");
                    return false;
                } else {
                    return true;
                }
            }

            @Override
            protected void whenSucceeded() {
                displaySummary();
                initHeaderInputs();
                makeTagsInputs();
                displayTagsTable();
                displayXML(xml);
                opsPane.setDisable(false);
            }

        };
        start(task);
    }

    private void displaySummary() {
        summaryArea.clear();
        if (header == null) {
            return;
        }

        try {
            LinkedHashMap<String, IccTag> fields = header.getFields();
            String s = message("ProfileSize") + ": " + header.value("ProfileSize");
            List<IccTag> tagsList = tags.getTags();
            if (tagsList != null) {
                s += "   " + message("TagsNumber") + ": " + tagsList.size();
            }
            String name = getCurrentName();
            infoLabel.setText(name + "\n" + message("ProfileSize") + ": " + header.value("ProfileSize")
                    + ((tagsList != null) ? "\n" + message("TagsNumber") + ": " + tagsList.size() : ""));

            s = name + "\n" + s + "\n\n";
            for (String key : fields.keySet()) {
                IccTag field = fields.get(key);
                switch (key) {
                    case "ProfileFlagIndependently":
                    case "ProfileFlagMCSSubset":
                    case "DeviceAttributeMatte":
                    case "DeviceAttributeNegative":
                    case "DeviceAttributeBlackOrWhite":
                    case "DeviceAttributePaperBased":
                    case "DeviceAttributeTextured":
                    case "DeviceAttributeIsotropic":
                    case "DeviceAttributeSelfLuminous":
                    case "PCCIlluminantY":
                    case "PCCIlluminantZ":
