package mara.mybox.controller;

import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import mara.mybox.data.StringTable;
import mara.mybox.data2d.Data2D;
import mara.mybox.data2d.DataFileCSV;
import mara.mybox.data2d.example.Data2DExampleTools;
import mara.mybox.data2d.example.SoftwareTesting;
import mara.mybox.data2d.tools.Data2DConvertTools;
import mara.mybox.db.data.ColorData;
import mara.mybox.db.data.ColorDataTools;
import mara.mybox.db.data.DataNode;
import mara.mybox.db.table.BaseNodeTable;
import mara.mybox.dev.MyBoxLog;
import mara.mybox.fxml.FxFileTools;
import mara.mybox.fxml.FxTask;
import mara.mybox.fxml.HelpTools;
import static mara.mybox.fxml.HelpTools.imageStories;
import mara.mybox.fxml.WindowTools;
import mara.mybox.fxml.image.FxColorTools;
import mara.mybox.fxml.image.PaletteTools;
import mara.mybox.fxml.style.HtmlStyles;
import mara.mybox.fxml.style.NodeStyleTools;
import mara.mybox.fxml.style.StyleData;
import mara.mybox.tools.FileCopyTools;
import mara.mybox.tools.FileDeleteTools;
import mara.mybox.tools.FileTools;
import mara.mybox.tools.HtmlWriteTools;
import mara.mybox.tools.TextFileTools;
import mara.mybox.value.AppVariables;
import static mara.mybox.value.AppVariables.CurrentBundle;
import static mara.mybox.value.AppVariables.CurrentLangName;
import static mara.mybox.value.Colors.color;
import mara.mybox.value.Fxmls;
import mara.mybox.value.Languages;
import static mara.mybox.value.Languages.message;
import mara.mybox.value.UserConfig;

/**
 * @Author Mara
 * @CreateDate 2024-12-12
 * @License Apache License Version 2.0
 */
public class MyBoxDocumentsController extends BaseTaskController {

    protected File path;
    protected final SimpleBooleanProperty finishNotify;
    protected int index, dataIndex;
    protected String realLang;
    protected ResourceBundle realBoundle;
    protected List<Data> dataList, cnList, enList;
    protected Data currentData;
    protected final Object dataLock = new Object();

    @FXML
    protected CheckBox readmeCheck, functionsCheck, tipsCheck, shortcutsCheck,
            dataCheck, testCheck, treeCheck, paletteCheck, linksCheck, imagesCheck;

    public MyBoxDocumentsController() {
        baseTitle = message("MakeDocuments");
        finishNotify = new SimpleBooleanProperty(false);
    }

    @Override
    public void initControls() {
        try {
            super.initControls();
            path = new File(AppVariables.MyboxDataPath + "/doc/");

            readmeCheck.setSelected(UserConfig.getBoolean(baseName + "Readme", true));
            readmeCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Readme", nv);
                }
            });

            functionsCheck.setSelected(UserConfig.getBoolean(baseName + "Functions", true));
            functionsCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Functions", nv);
                }
            });

            tipsCheck.setSelected(UserConfig.getBoolean(baseName + "Tips", true));
            tipsCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Tips", nv);
                }
            });

            treeCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    if (treeCheck.isSelected()) {
                        treeCheck.setStyle(NodeStyleTools.darkRedTextStyle());
                    } else {
                        treeCheck.setStyle(null);
                    }
                    UserConfig.setBoolean(baseName + "Tree", nv);
                }
            });
            treeCheck.setSelected(UserConfig.getBoolean(baseName + "Tree", true));

            shortcutsCheck.setSelected(UserConfig.getBoolean(baseName + "Shortcuts", true));
            shortcutsCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Shortcuts", nv);
                }
            });

            dataCheck.setSelected(UserConfig.getBoolean(baseName + "Data", true));
            dataCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Data", nv);
                }
            });

            testCheck.setSelected(UserConfig.getBoolean(baseName + "Test", true));
            testCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Test", nv);
                }
            });

            paletteCheck.setSelected(UserConfig.getBoolean(baseName + "Palette", true));
            paletteCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Palette", nv);
                }
            });

            linksCheck.setSelected(UserConfig.getBoolean(baseName + "Links", true));
            linksCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Links", nv);
                }
            });

            imagesCheck.setSelected(UserConfig.getBoolean(baseName + "Images", true));
            imagesCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    UserConfig.setBoolean(baseName + "Images", nv);
                }
            });

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

    @Override
    public void startTask() {
        defaultStartTask();

        if (shortcutsCheck.isSelected()) {
            showLogs(message("Shortcuts") + "...");
            ShortcutsController.documents(this, path);
        }

        if (functionsCheck.isSelected()) {
            showLogs(message("FunctionsList") + "...");
            FunctionsListController.makeFunctionsList(this, path);
        }
    }

    @Override
    public boolean doTask(FxTask currentTask) {
        realLang = CurrentLangName;
        realBoundle = CurrentBundle;
        if (readmeCheck.isSelected()) {
            readme("zh");
            readme("en");
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (tipsCheck.isSelected()) {
            tips("zh");
            tips("en");
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (testCheck.isSelected()) {
            testing("zh");
            testing("en");
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (dataCheck.isSelected()) {
            data("zh");
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (linksCheck.isSelected()) {
            links("zh");
            links("en");
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (treeCheck.isSelected()) {
            trees();
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (imagesCheck.isSelected()) {
            images("zh");
            images("en");
        }
        if (task == null || !task.isWorking()) {
            return false;
        }
        if (paletteCheck.isSelected()) {
            palettes("zh");
            palettes("en");
        }
        return true;
    }

    protected boolean readme(String lang) {
        try {
            File file = HelpTools.makeReadMe(lang);
            if ("zh".equals(lang)) {
                FileCopyTools.copyFile(file, new File(path, "index.html"), true, true);
            }
            showLogs(file.getAbsolutePath());
            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    protected boolean tips(String lang) {
        try {
            File file = HelpTools.makeInterfaceTips(lang);
            FileTools.override(file, new File(path, file.getName()), true);
            showLogs(file.getAbsolutePath());
            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    protected boolean links(String lang) {
        try {
            File file = HelpTools.usefulLinks(lang);
            FileTools.override(file, new File(path, file.getName()), true);
            showLogs(file.getAbsolutePath());
            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    protected boolean images(String lang) {
        try {
            File file = imageStories(task, true, lang);
            FileTools.override(file, new File(path, file.getName()), true);
            showLogs(file.getAbsolutePath());
            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    public class Data {

        public String name, cvs, html;
        public DataFileCSV data2D;
        public MenuItem menu;

    }

    protected void dataMenu(MenuItem menu) {
        if (menu instanceof SeparatorMenuItem
                || menu instanceof CheckMenuItem
                || message("Matrix").equals(menu.getText())
                || menu.getStyle() != null) {
            return;
        }
        if (menu instanceof Menu) {
            for (MenuItem mitem : ((Menu) menu).getItems()) {
                dataMenu(mitem);
            }
        } else {
            Data d = new Data();
            d.menu = menu;
            dataList.add(d);
        }
    }

    protected boolean data(String lang) {
        Platform.runLater(() -> {
            FileDeleteTools.clearDir(null, AppVariables.MyBoxTempPath);
            CurrentLangName = lang;
            if ("zh".equals(lang)) {
                CurrentBundle = Languages.BundleZhCN;
                cnList = new ArrayList<>();
                dataList = cnList;
            } else {
                CurrentBundle = Languages.BundleEn;
                enList = new ArrayList<>();
                dataList = enList;
            }

            browse(path);

            Data2DManufactureController dataController = Data2DManufactureController.open();
            dataController.setIconified(true);
            dataController.forConvert = true;

            dataController.loadedNotify.addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> v, Boolean ov, Boolean nv) {
                    handleData(dataController, lang, dataController.data2D);
                }
            });

            FxTask<Void> listTask = new FxTask<Void>() {

                @Override
                protected boolean handle() {
                    for (MenuItem menu
                            : Data2DExampleTools.examplesMenu(dataController, lang)) {
                        dataMenu(menu);
                    }
                    dataIndex = 0;
                    currentData = dataList.get(dataIndex);
                    Platform.runLater(() -> {
                        currentData.menu.fire();
                    });
                    Platform.requestNextPulse();
                    return true;
                }
            };
            new Thread(listTask).start();

        });
        Platform.requestNextPulse();

        try {
            synchronized (dataLock) {
                dataLock.wait();
            }
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
        return true;

    }

    protected void handleData(Data2DManufactureController dataController,
            String lang, Data2D data2d) {
        if (data2d == null || currentData == null) {
            return;
        }
        File csvFile = data2d.getFile();
        if (csvFile == null) {
            return;
        }
        FxTask<Void> dataTask = new FxTask<Void>() {

            @Override
            protected boolean handle() {
                try {
                    currentData.data2D = (DataFileCSV) data2d;
                    String csvName = csvFile.getName();
                    csvName = csvName.substring(0, csvName.length() - 4)
                            + (csvName.contains("_" + lang) ? "" : ("_" + lang));
                    File htmlFile = new File(path, csvName + ".html");
                    Data2DConvertTools.toHtmlFile(null, currentData.data2D, htmlFile);
                    currentData.cvs = csvName + ".csv";
                    FileCopyTools.copyFile(csvFile, new File(path, currentData.cvs), true, false);
                    currentData.name = currentData.menu.getText();
                    currentData.html = htmlFile.getName();
                    if (dataIndex < dataList.size() - 1) {
                        currentData = dataList.get(++dataIndex);
                        Platform.runLater(() -> {
                            currentData.menu.fire();
                            dataController.setIconified(true);
                        });
                        Platform.requestNextPulse();
//                            updateLogs(htmlFile.getAbsolutePath());
                    } else {
                        if ("zh".equals(lang)) {
                            data("en");
                        } else {
                            CurrentLangName = realLang;
                            CurrentBundle = realBoundle;
                            String linkPrefix = "<a  target=_blank href=\"https://mara-mybox.sourceforge.io/data2d/";
                            String csvSuffix = "\">csv ";
                            String htmlSuffix = "\">html ";
                            StringTable cnTable = new StringTable().setTitle("吾匣数据示例");
                            StringTable enTable = new StringTable().setTitle("MyBox Data Examples");
                            List<String> row;
                            for (int i = 0; i < dataList.size(); i++) {
                                Data cndata = cnList.get(i);
                                Data endata = enList.get(i);
                                row = new ArrayList<>();
                                row.add(cndata.name);
                                row.add(linkPrefix + cndata.cvs + csvSuffix + message("cn", "Chinese"));
                                row.add(linkPrefix + cndata.html + htmlSuffix + message("cn", "Chinese"));
                                row.add(linkPrefix + endata.cvs + csvSuffix + message("cn", "English"));
                                row.add(linkPrefix + endata.html + htmlSuffix + message("cn", "English"));
                                cnTable.add(row);
                                row = new ArrayList<>();
                                row.add(endata.name);
                                row.add(linkPrefix + endata.cvs + csvSuffix + " in English");
                                row.add(linkPrefix + endata.html + htmlSuffix + " in English");
                                row.add(linkPrefix + cndata.cvs + csvSuffix + " in Chinese");
                                row.add(linkPrefix + cndata.html + htmlSuffix + " in Chinese");
                                enTable.add(row);
                            }
                            TextFileTools.writeFile(new File(path, "mybox_examples_data2d_cn.html"), cnTable.html());
                            TextFileTools.writeFile(new File(path, "mybox_examples_data2d_en.html"), enTable.html());
                            try {
                                synchronized (dataLock) {
                                    dataLock.notifyAll();
                                }
                            } catch (Exception e) {
                                MyBoxLog.error(e);
                            }
                        }
                        Platform.runLater(() -> {
                            dataController.close();
                        });
                        Platform.requestNextPulse();
                    }
                    return true;
                } catch (Exception e) {
                    MyBoxLog.error(e);
                    return false;
                }
            }
        };
        new Thread(dataTask).start();
    }

    protected boolean testing(String lang) {
        try {
            DataFileCSV data = SoftwareTesting.TestEnvironment(lang);
            data.setFile(FxFileTools.getInternalFile("/data/examples/ST_TestEnvironment_" + lang + ".csv"))
                    .setCharset(Charset.forName("UTF-8")).setDelimiter(",").setHasHeader(true);
            File htmlFile = new File(path, "mybox_TestEnvironment_" + lang + ".html");
            Data2DConvertTools.toHtmlFile(task, data, htmlFile);
            showLogs(htmlFile.getAbsolutePath());

            data = SoftwareTesting.CompatibilityTesting(lang);
            data.setFile(FxFileTools.getInternalFile("/data/examples/ST_CompatibilityTesting_" + lang + ".csv"))
                    .setCharset(Charset.forName("UTF-8")).setDelimiter(",").setHasHeader(true);
            htmlFile = new File(path, "mybox_CompatibilityTesting_" + lang + ".html");
            Data2DConvertTools.toHtmlFile(task, data, htmlFile);
            showLogs(htmlFile.getAbsolutePath());

            data = SoftwareTesting.DetailedTesting(lang);
            data.setFile(FxFileTools.getInternalFile("/data/examples/ST_DetailedTesting_" + lang + ".csv"))
                    .setCharset(Charset.forName("UTF-8")).setDelimiter(",").setHasHeader(true);
            htmlFile = new File(path, "mybox_DetailedTesting_" + lang + ".html");
            Data2DConvertTools.toHtmlFile(task, data, htmlFile);
            showLogs(htmlFile.getAbsolutePath());

            Platform.runLater(() -> {
                FunctionsListController.makeVerificationList(this, path, lang);
            });
            Platform.requestNextPulse();

            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    public class TreeCase {

        public String tableName, lang;

        public TreeCase(String tableName, String lang) {
            this.tableName = tableName;
            this.lang = lang;
        }
    }

    protected boolean trees() {
        try {
            List<TreeCase> cases = new ArrayList<>();
            cases.add(new TreeCase("TextTree", "zh"));
            cases.add(new TreeCase("TextTree", "en"));
            cases.add(new TreeCase("HtmlTree", "zh"));
            cases.add(new TreeCase("HtmlTree", "en"));
            cases.add(new TreeCase("MathFunction", "zh"));
            cases.add(new TreeCase("MathFunction", "en"));
            cases.add(new TreeCase("WebFavorite", "zh"));
            cases.add(new TreeCase("WebFavorite", "en"));
            cases.add(new TreeCase("DatabaseSQL", "zh"));
            cases.add(new TreeCase("DatabaseSQL", "en"));
            cases.add(new TreeCase("ImageScope", "zh"));
            cases.add(new TreeCase("ImageScope", "en"));
            cases.add(new TreeCase("JShell", "zh"));
            cases.add(new TreeCase("JShell", "en"));
            cases.add(new TreeCase("JEXL", "zh"));
            cases.add(new TreeCase("JEXL", "en"));
            cases.add(new TreeCase("JavaScript", "zh"));
            cases.add(new TreeCase("JavaScript", "en"));
            cases.add(new TreeCase("RowExpression", "zh"));
            cases.add(new TreeCase("RowExpression", "en"));
            cases.add(new TreeCase("DataColumn", "zh"));
            cases.add(new TreeCase("DataColumn", "en"));
            cases.add(new TreeCase("GeographyCode", "zh"));
            cases.add(new TreeCase("GeographyCode", "en"));
            cases.add(new TreeCase("MacroCommands", "zh"));
            cases.add(new TreeCase("MacroCommands", "en"));

            index = 0;
            finishNotify.addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (index < cases.size()) {
                        Platform.runLater(() -> {
                            TreeCase theCase = cases.get(index);
                            treeHtml(theCase.tableName, theCase.lang);
                            index++;
                        });
                        Platform.requestNextPulse();
                    }
                }
            });
            finishNotify();
            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    protected void finishNotify() {
        finishNotify.set(!finishNotify.get());
    }

    protected boolean treeHtml(String tableName, String lang) {
        try {
            CurrentLangName = lang;
            CurrentBundle = "zh".equals(lang) ? Languages.BundleZhCN : Languages.BundleEn;
            BaseNodeTable nodeTable = BaseNodeTable.create(tableName);
            nodeTable.truncate();
            DataNode rootNode = nodeTable.getRoot();
            if (rootNode == null) {
                finishNotify();
                return false;
            }

//            popInformation(message("Handling") + ": " + tableName);
            DataTreeImportController importController = (DataTreeImportController) WindowTools
                    .openStage(Fxmls.DataTreeImportFxml);
            importController.importExamples(nodeTable, rootNode, nodeTable.exampleFileLang(lang));

            importController.taskClosedNotify.addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    Platform.runLater(() -> {
                        importController.close();

                        DataTreeExportController exportController = (DataTreeExportController) WindowTools
                                .openStage(Fxmls.DataTreeExportFxml);
                        exportController.setData(nodeTable, rootNode);

                        exportController.isSettingValues = true;
                        exportController.selectAllFormat(false);
                        exportController.treeHtmlCheck.setSelected(true);
                        exportController.selectAllValue(false);
                        exportController.hierarchyCheck.setSelected(true);
                        exportController.tagsCheck.setSelected(true);
                        exportController.dataCheck.setSelected(true);
                        exportController.openCheck.setSelected(false);
                        exportController.isSettingValues = false;
                        exportController.startAction();
                        exportController.taskClosedNotify.addListener(new ChangeListener<Boolean>() {
                            @Override
                            public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                                Platform.runLater(() -> {
                                    File file = exportController.treeHtmlFile;
                                    if (file != null && file.exists()) {
                                        File htmlFile = new File(path,
                                                "mybox_examples_" + nodeTable.getTableName() + "_" + lang + ".html");
                                        FileTools.override(file, htmlFile, true);
                                        showLogs(htmlFile.getAbsolutePath());
                                    } else {
                                        showLogs(message("Failed"));
                                    }
                                    exportController.close();
                                    nodeTable.truncate();
                                    CurrentLangName = realLang;
                                    CurrentBundle = realBoundle;
                                    finishNotify();
                                });
                                Platform.requestNextPulse();
                            }
                        });
                        exportController.setIconified(true);
                    });
                    Platform.requestNextPulse();
                }
            });

            return true;
        } catch (Exception e) {
            error = e.toString();
            finishNotify();
            return false;
        }
    }

    protected boolean palettes(String lang) {
        try {
            File file = FxFileTools.getInternalFile("/data/examples/ColorsArtPaints.csv",
                    "data", "ColorsArtPaints_" + lang + ".csv", true);
            List<ColorData> colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "ArtPaints"), "art_paints");

            file = FxFileTools.getInternalFile("/data/examples/ColorsWeb.csv",
                    "data", "ColorsWeb_" + lang + ".csv", true);
            colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "WebCommonColors"), "web");

            file = FxFileTools.getInternalFile("/data/examples/ColorsChinese.csv",
                    "data", "ColorsChinese_" + lang + ".csv", true);
            colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "ChineseTraditionalColors"), "chinese");

            file = FxFileTools.getInternalFile("/data/examples/ColorsJapanese.csv",
                    "data", "ColorsJapanese_" + lang + ".csv", true);
            colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "JapaneseTraditionalColors"), "japanese");

            file = FxFileTools.getInternalFile("/data/examples/ColorsColorhexa.csv",
                    "data", "ColorsColorhexa_" + lang + ".csv", true);
            colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "HexaColors"), "colorhexa");

            colors = new ArrayList<>();
            for (StyleData.StyleColor style : StyleData.StyleColor.values()) {
                colors.add(new ColorData(color(style, true).getRGB(), message(lang, "MyBoxColor" + style.name() + "Dark")));
                colors.add(new ColorData(color(style, false).getRGB(), message(lang, "MyBoxColor" + style.name() + "Light")));
            }
            palettes(lang, colors, message(lang, "MyBoxColors"), "mybox");

            colors = PaletteTools.defaultColors(lang);
            palettes(lang, colors, message(lang, "DefaultPalette"), "default");

            file = FxFileTools.getInternalFile("/data/examples/ColorsRYB12_" + lang + ".csv",
                    "data", "ColorsRYB12_" + lang + ".csv", true);
            colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "ArtHuesWheel") + "-" + message(lang, "Colors12"), "ryb12");

            file = FxFileTools.getInternalFile("/data/examples/ColorsRYB24_" + lang + ".csv",
                    "data", "ColorsRYB24_" + lang + ".csv", true);
            colors = ColorDataTools.readCSV(task, file, true);
            palettes(lang, colors, message(lang, "ArtHuesWheel") + "-" + message(" + lang + ", "Colors24"), "ryb24");

            colors = PaletteTools.artHuesWheel(lang, 1);
            palettes(lang, colors, message(lang, "ArtHuesWheel") + "-" + message(lang, "Colors360"), "ryb360");

            colors = PaletteTools.opticalHuesWheel(lang, 30);
            palettes(lang, colors, message(lang, "OpticalHuesWheel") + "-" + message(lang, "Colors12"), "rgb12");

            colors = PaletteTools.opticalHuesWheel(lang, 15);
            palettes(lang, colors, message(lang, "OpticalHuesWheel") + "-" + message(lang, "Colors24"), "rgb24");

            colors = PaletteTools.opticalHuesWheel(lang, 1);
            palettes(lang, colors, message(lang, "OpticalHuesWheel") + "-" + message(lang, "Colors360"), "rgb360");

            colors = PaletteTools.greyScales(lang);
            palettes(lang, colors, message(lang, "GrayScale"), "gray");
            return true;
        } catch (Exception e) {
            error = e.toString();
            return false;
        }
    }

    protected void palettes(String lang, List<ColorData> colors, String title, String name) {
        try {
            if (colors == null || colors.isEmpty()) {
                return;
            }
            List<String> columns = new ArrayList<>();
            columns.addAll(Arrays.asList(message(lang, "Name"), message(lang, "Color"),
                    "HSBA", message(lang, "RGBInvertColor"), message(lang, "RGBInvertColor") + "-" + message(lang, "Value"),
                    message(lang, "RYBComplementaryColor"), message(lang, "RYBComplementaryColor") + "-" + message(lang, "Value"), "RGBA", "RGB",
                    message(lang, "RYBAngle"), message(lang, "Hue"), message(lang, "Saturation"), message(lang, "Brightness"), message(lang, "Opacity")));
            makeColorDoc(lang, colors, columns, title + " - RGBA", name + "_rgba_" + lang);

            columns = new ArrayList<>();
            columns.addAll(Arrays.asList(message(lang, "Name"), message(lang, "Color"), "RGBA", "RGB",
                    message(lang, "RYBAngle"), message(lang, "Hue"), message(lang, "Saturation"), message(lang, "Brightness"), message(lang, "Opacity"),
                    "HSBA", "sRGB", message(lang, "CalculatedCMYK"),
                    message(lang, "RGBInvertColor"), message(lang, "RGBInvertColor") + "-" + message(lang, "Value"),
                    message(lang, "RYBComplementaryColor"), message(lang, "RYBComplementaryColor") + "-" + message(lang, "Value"),
                    "Adobe RGB", "Apple RGB", "ECI RGB", "sRGB Linear", "Adobe RGB Linear", "Apple RGB Linear",
                    "ECI CMYK", "Adobe CMYK", "XYZ", "CIE-L*ab", "LCH(ab)", "CIE-L*uv", "LCH(uv)", message(lang, "Value")
            ));
            makeColorDoc(lang, colors, columns, title + " - " + message(lang, "All"), name + "_all_" + lang);

        } catch (Exception e) {
            error = e.toString();
        }
    }

    protected void makeColorDoc(String lang, List<ColorData> colors, List<String> columns, String title, String name) {
        try {
            if (colors == null || colors.isEmpty()) {
                return;
            }
            colors.addAll(PaletteTools.speicalColors(lang));
            StringTable table = new StringTable(columns, title);
            for (ColorData c : colors) {
                if (!task.isWorking()) {
                    return;
                }
                if (c.needConvert()) {
                    c.convert();
                }
                List<String> row = new ArrayList<>();
