/*-
 * ~~~~~~licensing~~~~~~
 * base-support
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.base.support;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * 
 * Common util for all bus components
 * 
 * @author fav
 *
 */
public class CommonUtils {

    public static final String GUID_0 = "00000000-0000-0000-0000-000000000000";
    public static final String GUID_f = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
    public static final String NULL_GUID_ = GUID_0;

    public static final String PACKET_TYPE_PARAM_NAME = "packetType";

    public static class Path {

        public static final String DEFAULT_SEPARATOR = "/";

        protected String separator = DEFAULT_SEPARATOR;
        protected List<String> data = new ArrayList<>();

        public static Path create() {
            return new Path();
        }

        public static boolean isAbsolute(String path) {
            return isAbsolute(path, DEFAULT_SEPARATOR);
        }

        public static boolean isAbsolute(String path, String separator) {
            if (!isValid(path))
                return false;
            return path.trim().startsWith(separator);
        }

        public Path() {
            super();
        }

        public Path separator(String newSeparator) {
            if (newSeparator != null)
                this.separator = newSeparator;
            return this;
        }

        public Path construct(String... fragments) {
            data.clear();
            return append(fragments);
        }

        public Path append(String... fragments) {
            if (fragments == null)
                return this;
            for (int i = 0; i < fragments.length; i++) {
                String string = fragments[i];
                if (isValid(string)) {
                    String[] splitted = StringUtils.split(string, separator);
                    for (int j = 0; j < splitted.length; j++) {
                        String string2 = splitted[j];
                        if (isValid(string2) && isValid(string2.trim()))
                            data.add(string2.trim());
                    }
                }
            }
            return this;
        };

        public String relational() {
            return StringUtils.join(data, separator);
        }

        public String absolute() {
            return separator + relational();
        }

        public Iterator<String> fragmentsIterator() {
            return data.iterator();
        }

        public ListIterator<String> fragmentsListIterator() {
            return data.listIterator();
        }

        public List<String> pathHierarchy() {
            return pathHierarchy(true);
        }

        public List<String> pathHierarchy(boolean absolute) {
            List<String> result = new ArrayList<>();
            if (data.isEmpty())
                return result;
            result.add((absolute ? separator : "") + data.get(0));
            for (int i = 1; i < data.size(); i++)
                result.add(result.get(i - 1) + separator + data.get(i));
            return result;
        }

        public Iterator<String> iterator() {
            return pathHierarchy().iterator();
        }

        public ListIterator<String> listIterator() {
            return pathHierarchy().listIterator();
        }

    }

    /**
     * Generates UUID
     * 
     * @return
     */
    public static String getUUID() {
        return UUID.randomUUID().toString().toLowerCase().replace("-", "");
    }

    /**
     * Generates UUID consisting of specified char
     * 
     * @param c
     * @return
     */
    public static String getUUID(char c) {
        return StringUtils.leftPad("", 32, c);
    }

    /**
     * Generates GUID
     * 
     * @return
     */
    public static String getGUID() {
        return UUID.randomUUID().toString().toUpperCase();
    }

    /**
     * Generates GUID consisting of specified char
     * 
     * @param c
     * @return
     */
    public static String getGUID(char c) {
        return uid2guid(StringUtils.leftPad("", 32, c));
    }

    /**
     * Converts UUID to GUID
     * 
     * @param uid
     * @return
     */
    public static String uid2guid(String uid) {
        return uid.replaceFirst("(.{8})(.{4})(.{4})(.{4})(.{8})", "$1-$2-$3-$4-$5").toUpperCase();
    }

    /**
     * Converts GUID to UUID
     * 
     * @param uid
     * @return
     */
    public static String guid2uid(String guid) {
        return guid.toLowerCase().replace("-", "");
    }

    /**
     * Checks if the string is not null and has something inside after trim
     * 
     * @param s
     * @return
     */
    public static boolean isValid(String s) {
        if (s == null)
            return false;
        return s.trim().length() > 0;
    }

    /**
     * 
     * @param s string to examine
     * @param def default value
     * @return s if isValid(s), otherwise def
     */
    public static String getValid(String s, String def) {
        return isValid(s) ? s : def;
    }

    public static String padLeft(String data, int length) {
        return padLeft(data, length, ' ');
    }

    public static String padRight(String data, int length) {
        return padRight(data, length, ' ');
    }

    public static String padBoth(String data, int length) {
        return padBoth(data, length, ' ');
    }

    public static String padLeft(String data, int length, Character c) {
        String val = data;
        if (data == null)
            val = "";
        while (val.length() < length)
            val = c + val;
        return val;
    }

    public static String padRight(String data, int length, Character c) {
        String val = data;
        if (data == null)
            val = "";
        while (val.length() < length)
            val = val + c;
        return val;
    }

    public static String padBoth(String data, int length, Character c) {
        String val = data;
        boolean left = true;
        if (data == null)
            val = "";
        while (val.length() < length) {
            if (left)
                val = c + val;
            else
                val = val + c;
            left = !left;
        }
        return val;
    }

    public static void stream2file(InputStream input, String file) throws Exception {
        File f = new File(file);
        try (FileOutputStream output = new FileOutputStream(f)) {
            IOUtils.copy(input, output);
        }
    }

    public static <K, V> Map<K, V> addDictionaryToMap(Dictionary<K, V> source, Map<K, V> sink) {
        for (Enumeration<K> keys = source.keys(); keys.hasMoreElements();) {
            K key = keys.nextElement();
            sink.put(key, source.get(key));
        }
        return sink;
    }

    public static <K, V> Map<K, V> getDictionaryAsMap(Dictionary<K, V> source) {
        Map<K, V> result = new HashMap<>();
        CommonUtils.addDictionaryToMap(source, result);
        return result;
    }

    public static <V> Map<String, V> prefixMap(Map<String, V> map, String prefix) {
        Map<String, V> result = new LinkedHashMap<>();
        for (Map.Entry<String, V> entry : map.entrySet())
            result.put(String.format("%s%s", prefix, entry.getKey()), entry.getValue());
        return result;
    }
}
