package Common.Utils;
import Common.CommonConstants;
import Common.Passes.PassException;
import Common.Properties;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.io.FileUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Vector;
import java.util.concurrent.Semaphore;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
public class Utils_ {
    //ГЛОБАЛЬНЫЙ ЖУРНАЛ
    public static Loggable MainLog;
    public static Semaphore date_semaphore = new Semaphore(1);
    public static long last_ticks = CommonConstants.Nan;
    //--
    //JSON
    //--
    //    public static String jsonToPrettyFormat(String packed) {
    //        JsonParser parser = new JsonParser();
    //        JsonObject json = parser.parse(packed).getAsJsonObject();
    //        Gson gson = new GsonBuilder().setPrettyPrinting().create();
    //        return gson.toJson(json);
    //    }
    //--
    public static Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create();
    static String HomePath = System.getProperty("user.dir");
    //Текущая оперционная система
    public static boolean isWindows() {
        return System.getProperty("os.name").startsWith("Windows");
    }
    public static String getHomePath() {
        return HomePath;
    }
    public static void setHomePath(String path_in) {
        HomePath = path_in;
    }
    public static File getHomeDirectory() {
        return new File(getHomePath());
    }
    //--------------------------------------------------
    public static Object requireNonNullElse(Object value, Object default_value) {
        return (value != null) ? value : default_value;
    }
    //------------------------------------------------------------------
    //-
    public static <T> T jsonFromFile(File file, Class<T> json_class) throws Exception {
        return gson.fromJson(FileUtils.readFileToString(file, Charset.defaultCharset()), json_class);
    }
    public static void jsonToFile(Object json_object, File file) throws Exception {
        FileUtils.writeStringToFile(file, gson.toJson(json_object));
    }
    public static JsonObject getPropertiesAsJsonObject() throws Exception {
        File propertiesFile = new File(System.getProperty("user.dir"), "properties");
        if (!propertiesFile.exists()) {
            return null;
        }
        String packed = FileUtils.readFileToString(propertiesFile, Charset.defaultCharset());
        JsonParser parser = new JsonParser();
        JsonObject json = parser.parse(packed).getAsJsonObject();
        return json;
    }
    //БУФЕР ОБМЕНА
    public static void CopyToClipboard(String text) {
        Toolkit.getDefaultToolkit()
                .getSystemClipboard()
                .setContents(
                        new StringSelection(text),
                        null
                );
    }
    public static String getFromClipboard() {
        String res = "";
        try {
            res = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
        } catch (Exception ex) {
            MainLog.PrintException(ex);
        }
        return res;
    }
    public static String strikeThrough(String s) {
        StringBuilder res = new StringBuilder();
        for (char c : s.toCharArray()) {
            res.append(c);
            if (c != CommonConstants.toStrike)
                res.append(CommonConstants.toStrike);
        }
        return res.toString();
    }
    public static String noStrikeThrough(String s) {
        StringBuilder res = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (c != (CommonConstants.toStrike))
                res.append(c);
        }
        return res.toString();
    }
    public static boolean validateEmail(String address, TextLog log) {
        Matcher matcher = CommonConstants.VALID_EMAIL_ADDRESS_REGEX.matcher(address);
        if (!matcher.find()) {
            log.Writeln_("введённый адрес электронной почты некорректен.");
            return false;
        }
        String match = address.substring(matcher.start(), matcher.end());
        if (!match.equals(address)) {
            log.Writeln_("введённый адрес электронной почты некорректен.");
            return false;
        }
        return true;
    }
    public static String hideRegularMetasymbols(String word) {
        String res = word.replace("\\", "\\\\");
        for (char c : CommonConstants.regular_metasymbols)
            res = res.replace(String.valueOf(c), "\\" + c);
        return res;
    }
    public static String DQuotes(Object o) {
        return "\"" + o.toString() + "\"";
    }
    public static String Quotes(Object o) {
        return "'" + o.toString() + "'";
    }
    public static String Brackets(Object o) {
        return "[" + o.toString() + "]";
    }
    public static String RBrackets(Object o) {
        return "(" + o.toString() + ")";
    }
    public static String TBrackets(Object o) {
        return "<" + o.toString() + ">";
    }//FortranSPFTokenMaker
    public static boolean ContainsForbiddenName(String string) {
        char[] chars = string.toCharArray();
        for (char c : chars)
            if (isForbiddenCharacter(c)) return true;
        return false;
    }
    public static boolean isForbiddenCharacter(char c) {
        return CommonConstants.forbidden_file_name_characters.contains(c);
    }
    public static String ReplaceForbiddenCharacters(String name) {
        StringBuilder res = new StringBuilder();
        for (char c : name.toCharArray())
            res.append(isForbiddenCharacter(c) ? '_' : c);
        return res.toString();
    }
    //Синтаксис и регулярные выражения
    public static String printAllForbiddenCharacters() {
        Vector<String> res = new Vector<>();
        for (char c : CommonConstants.forbidden_file_name_characters)
            res.add(String.valueOf(c));
        return String.join(" ", res);
    }
    public static boolean ContainsCyrillic(String string) {
        return string.chars()
                .mapToObj(Character.UnicodeBlock::of)
                .anyMatch(b -> b.equals(Character.UnicodeBlock.CYRILLIC));
    }
    public static boolean isIntegerValue(String s) {
        try {
            Integer.parseInt(s);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    public static boolean isEnglishLetter(char c) {
        return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
    }
    public static boolean isRussianLetter(char c) {
        return ((c >= 'а') && (c <= 'я'))
                || ((c >= 'А') && (c <= 'Я'))
                || (c == 'Ё')
                || (c == 'ё');
    }
    public static boolean isSign(char c) {
        switch (c) {
            //арифметика.
            case '+':
            case '-':
            case '*':
            case '/':
            case '<':
            case '>':
            case '&':
            case '=':
            case '%':
            case '^':
                //- обр слеш
            case '\\':
                //препинание
            case ' ':
            case '_':
            case '.':
            case ',':
            case '!':
            case '?':
            case ';':
            case ':':
                //escape последовательности
            case '\t':
            case '\n':
            case '\r':
                //кавычки
            case '\'':
            case '"':
                //- скобки
            case '(':
            case ')':
            case '[':
            case ']':
            case '{':
            case '}':
                //прочее
            case '~':
            case '`':
            case '|':
            case '@':
            case '$':
            case '#':
            case '№':
                return true;
        }
        return false;
    }
    public static char Translit(char c) {
        switch (c) {
            case 'А':
            case 'а':
            case 'Я':
            case 'я':
                return 'A';
            //
            case 'Б':
            case 'б':
                return 'B';
            //-
            case 'В':
            case 'в':
                return 'V';
            //
            case 'Г':
            case 'г':
                return 'G';
            //
            case 'Д':
            case 'д':
                return 'D';
            //
            case 'Е':
            case 'е':
            case 'Ё':
            case 'ё':
            case 'Э':
            case 'э':
                return 'E';
            //
            case 'Ж':
            case 'ж':
                return 'J';
            //
            case 'З':
            case 'з':
                return 'Z';
            //
            case 'И':
            case 'и':
            case 'Й':
            case 'й':
                return 'I';
            //
            case 'К':
            case 'к':
                return 'K';
            //
            case 'Л':
            case 'л':
                return 'L';
            //
            case 'М':
            case 'м':
                return 'M';
            //
            case 'Н':
            case 'н':
                return 'N';
            //
            case 'О':
            case 'о':
                return 'O';
            //
            case 'П':
            case 'п':
                return 'P';
            //
            case 'Р':
            case 'р':
                return 'R';
            //
            case 'С':
            case 'с':
                return 'S';
            case 'Т':
            case 'т':
                return 'T';
            //
            case 'У':
            case 'у':
            case 'Ю':
            case 'ю':
                return 'U';
            case 'Х':
            case 'х':
            case 'Щ':
            case 'щ':
            case 'Ш':
            case 'ш':
                return 'H';
            //
            case 'Ф':
            case 'ф':
                return 'F';
            //
            case 'Ч':
            case 'ч':
            case 'Ц':
            case 'ц':
                return 'C';
            //
            case 'Ы':
            case 'ы':
                return 'Y';
            //
        }
        return ' ';
    }
    public static String ending(boolean flag) {
        return flag ? ")" : ",";
    }
    public static boolean isRBracketsBalanced(String fragment) {
        int cc = 0;
        for (char c : fragment.toCharArray()) {
            if (c == '(')
                cc++;
            if (c == ')')
                cc--;
            if (cc < 0)
                return false;
        }
        return (cc == 0);
    }
    public static String removeRedundantSpaces(String s_in) {
        return String.join(" ",
                Arrays.stream(s_in.split(" ")).filter(d -> !d.isEmpty()).collect(Collectors.toCollection(Vector::new)));
    }
    public static String removeCharacters(String string, String... to_remove) {
        String res = string;
        for (String c : to_remove)
            res = res.replace(c, "");
        return res;
    }
    //ФАЙЛЫ
    public static String getExtension(File file) {
        String fn = file.getName();
        int di = fn.lastIndexOf(".");
        return (di >= 0) ? fn.substring(di + 1).toLowerCase() : "";
    }
    public static String getExtensionFromName(String fn) {
        int di = fn.lastIndexOf(".");
        return (di >= 0) ? fn.substring(di + 1).toLowerCase() : "";
    }
    public static String getFileNameWithoutExtension(File file) {
        return getNameWithoutExtension(file.getName());
    }
    public static String getNameWithoutExtension(String fn) {
        return (fn.contains(".")) ? fn.substring(0, fn.lastIndexOf(".")).toLowerCase() : fn.toLowerCase();
    }
    public static String toU(String path) {
        return path.replace('\\', '/');
    }
    public static String toW(String path) {
        return path.replace('/', '\\');
    }
    public static double getFileSizeMegaBytes(File file) {
        return ((double) file.length()) / (1024 * 1024);
    }
    public static byte[] fileToBytes(File src) throws Exception {
        byte[] dst = Files.readAllBytes(src.toPath());
        return dst;
    }
    public static void bytesToFile(byte[] bytes, File dst) throws Exception {
        FileOutputStream os = new FileOutputStream(dst);
        os.write(bytes);
        os.close();
    }
    public static void CheckDirectory(File dir) {
        if (!dir.exists()) {
            try {
                FileUtils.forceMkdir(dir);
            } catch (Exception e) {
                MainLog.PrintException(e);
            }
        }
    }
    public static void CheckAndCleanDirectory(File dir) {
        if (dir.exists()) {
            File[] files = dir.listFiles();
            if (files != null)
                for (File f : files) {
                    try {
                        forceDeleteWithCheck(f);
                    } catch (Exception e) {
                        MainLog.PrintException(e);
                    }
                }
        } else {
            try {
                FileUtils.forceMkdir(dir);
            } catch (Exception e) {
                MainLog.PrintException(e);
            }
        }
    }
    public static void forceDeleteWithCheck(File file) throws Exception {
        int attempts = 0;
        while (attempts < 10) {
            if (file.exists()) {
                try {
                    FileUtils.forceDelete(file);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } else return;
            if (file.exists()) {
                attempts++;
                System.out.println("неудачная попытка удаления: файл " + Brackets(file.getAbsolutePath()) + " занят");
                sleep(2000);
            } else return;
        }
        throw new PassException("Не удалось удалить файл " + Brackets(file.getAbsolutePath()) + " за " + attempts + " попыток");
    }
    //Иконки
    public static ImageIcon getIcon(String path) {
        URL imageUrl = Utils_.class.getResource(path);
        if (imageUrl == null) {
            return null;
        }
        return new ImageIcon(imageUrl);
    }
    public static ImageIcon getTabIcon(String path) {
        URL imageUrl = Utils_.class.getResource(path);
        if (imageUrl == null) {
            return null;
        }
        ImageIcon icon = new ImageIcon(imageUrl);
        return new ImageIcon(icon.getImage().getScaledInstance(
                18,
                18,
                Image.SCALE_DEFAULT));
    }
    //Даты
    public static String print_date(Date date) {
        return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date);
    }
    public static String printSplittedDateInterval(long milliseconds) {
        //--
        long seconds = milliseconds / 1000;
        long hours = seconds / 3600;
        seconds = seconds - hours * 3600;
        long minutes = (seconds) / 60;
        seconds = seconds - minutes * 60;
        //--
        return hours + " часов, " + minutes + " минут, " + seconds + " секунд";
    }
    public static String printSplittedDateIntervalShort(long milliseconds) {
        //--
        long seconds = milliseconds / 1000;
        long hours = seconds / 3600;
        seconds = seconds - hours * 3600;
        long minutes = (seconds) / 60;
        seconds = seconds - minutes * 60;
        //--
        return hours + ":" + minutes + ":" + seconds;
    }
    //Синхронизация
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception ignore) {
        }
    }
    //ГЕНЕРАЦИЯ ИМЕН
    public static long getDateNumber() {
        //-
        try {
            date_semaphore.acquire();
        } catch (Exception ignore) {
        }
        //-
        long ticks = new Date().toInstant().getEpochSecond();
        while (ticks == last_ticks) {
            sleep(1);
            ticks = new Date().toInstant().getEpochSecond();
        }
        last_ticks = ticks;  // Это и есть разделяемый ресурс.
        date_semaphore.release();
        return ticks;
    }
    public static String getDateName(String name_) {
        return name_ + "_" + getDateNumber();
    }
    public static int fromBoolean(boolean flag) {
        return flag ? 1 : 0;
    }
    //--
    public static <T extends Properties> T SynschronizeProperties(File propertiesFile, Class<T> properties_class) {
        T res = null;
        try {
            res = properties_class.newInstance();
            if (propertiesFile.exists()) {
                //файл существует. нужно его ссчитать.
                res = (T) Utils_.jsonFromFile(propertiesFile, properties_class);
            }
            res.setFile(propertiesFile);
            //перезаписываем файл в любом случае, так как может измениться формат.
            Utils_.jsonToFile(res, propertiesFile);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return res;
    }
    public static <T extends Properties> T ReadProperties(File propertiesFile, Class<T> properties_class) {
        T res = null;
        try {
            res = (T) Utils_.jsonFromFile(propertiesFile, properties_class);
            res.setFile(propertiesFile);
            Utils_.jsonToFile(res, propertiesFile);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return res;
    }
    public static File getFile(String... paths) {
        if (paths.length == 0)
            return null;
        File res = new File(paths[0]);
        for (int i = 1; i < paths.length; ++i)
            res = new File(res, paths[i]);
        return res;
    }
}
