/**
 * Copyright (c) 2025 SoftCorporation LLC. All rights reserved.
 *
 * The Software License, Version 1.0
 *
 * SoftCorporation LLC. grants you ("Licensee") a non-exclusive, royalty free,
 * license to use, modify and redistribute this software in source and binary
 * code form, provided that the following conditions are met:
 *
 * 1. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        SoftCorporation LLC. (http://www.softcorporation.com)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 2. This software and SoftCorporation name must not be used to
 *    promote products derived from the software without prior
 *    written permission. For written permission, please contact
 *    info@softcorporation.com.
 *
 * This software is provided "AS IS," without a warranty of any kind.
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 * IN NO EVENT SHALL THE SOFTCORPORATION BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION).
 *
 */
package com.softcorporation.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class StringUtil {

    /**
     * Max length of log String.
     */
    public static final int MAX_LOG_LENGTH = 1000;

    /**
     * DB_DATE_FORMAT String.
     */
    public static final String DB_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS zzz";

    /**
     * SIMPLE_DATE_FORMAT SimpleDateFormat.
     */
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmssSSS",
            Locale.ENGLISH);

    /**
     * TIMESTAMP String.
     */
    public static final String TIMESTAMP = SIMPLE_DATE_FORMAT.format(Calendar.getInstance().getTime());

    /**
     * UTF8 String.
     */
    public static final String UTF8 = "UTF-8";

    /**
     * ENCODING_DEFAULT String
     */
    public static final String ENCODING_DEFAULT = UTF8;

	/*
	 * Word delimiters.
	 */
	public static final String DELIMITERS = " \n\r\t,.()!{}[]<>#$%*_|\\/?|:;=+-";

	/**
     * Short text length
     */
    public static final int LENGTH_SHORTEN = 500;

    /**
     * Get current timestamp.
     *
     * @return Date
     */
    public static String getCurrentTime() {
        return new SimpleDateFormat(DB_DATE_FORMAT, Locale.ENGLISH).format(Calendar.getInstance().getTime());
    }

    /**
     * Get MD5 for string.
     *
     * @param input String
     * @return MD5 as String
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     * @throws IndexerException             exception
     */
    public static String getMD5(final String input) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        byte[] bytes;

        bytes = input.getBytes(ENCODING_DEFAULT);
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.update(bytes);
        return bytesToString(md5.digest());
    }

    /**
     * Convert bytes to a string.
     *
     * @param array bytes
     * @return String
     */
    public static String bytesToString(final byte[] array) {
        StringBuilder hexString = new StringBuilder(32);
        for (int i = 0; i < array.length; i++) {
            hexString.append("0123456789abcdef".charAt(0xf & array[i] >> 4));
            hexString.append("0123456789abcdef".charAt(0xf & array[i]));
        }
        return hexString.toString();
    }

    /**
     * Format the String for logging
     *
     * @param data String
     * @return formatted String
     */
    public static String formatForLogging(final String data) {
        return shorten(data, MAX_LOG_LENGTH);
    }

    /**
     * Format the String for logging
     *
     * @param data   String
     * @param length int
     * @return formatted String
     */
    public static String formatForLogging(final String data, int length) {
        return shorten(data, length).replace('\r', ' ').replace('\n', ' ');
    }

    /**
     * Reduce the length of a string.
     *
     * @param data String
     * @return short String
     */
    public static String shorten(final String data) {
        return shorten(data, LENGTH_SHORTEN);
    }

    /**
     * Reduce the length of a string.
     *
     * @param data   String
     * @param length the length
     * @return short String
     */
    public static String shorten(final String data, final int length) {
        if (data == null) {
            return "null";
        }
        if (data.length() > length) {
            return data.substring(0, length) + "...";
        }
        return data;
    }


	/**
	 * Escape Quotes
	 * 
	 * @param text The text
	 * @return Escaped text
	 */
	public static String escapeQuotes(final String text) {
		String output = text.replaceAll("[\\\\]", "\\\\\\\\");
		output = output.replaceAll("[\"]", "\\\\\"");
		return output;
	}

	/**
	 * Clean quotes
	 * 
	 * @param text The text
	 * @return Cleaned text
	 */
	public static String cleanQuotes(final String text) {
		return cleanSpaces(text.replaceAll("[\"]", " "));
	}

	/**
	 * Clean special chars and spaces
	 * 
	 * @param text The text
	 * @return Cleaned chars text
	 */
	public static String cleanText(final String text) {
		return cleanQuotes(removeSpecialChars(text));
	}

	/**
	 * Remove special chars
	 * except '&- without spaces
	 * 
	 * @param text The text
	 * @return Removed chars text
	 */
	public static String removeSpecialChars(final String text) {
	      String result = text.replaceAll("[^a-zA-Z0-9 '&-]", " ");
	      return result.replaceAll("(?<!\\S)['&-]|['&-](?!\\S)", " ");
	}
	
	/**
	 * Clean text extra spaces
	 * 
	 * @param text The text
	 * @return Cleaned text
	 */
	public static String cleanSpaces(final String text) {
		return text.replaceAll("\\s+", " ").trim();
	}

	/**
	 * Check if the same text
	 * 
	 * @param text1
	 * @param text2
	 * @return true if equals
	 */
	public static boolean equalTexts(final String text1, final String text2) {
		String cleanText1 = cleanText(text1);
		String cleanText2 = cleanText(text2);
		if (cleanText1.length() > 0 && cleanText2.length() > 0) {
			return cleanText1.toLowerCase().equals(cleanText2.toLowerCase());
		}
		return text1.equals(text2);
	}

	/*
	 * Cuts text based on spaces between words.
	 * 
	 * @param source the source string
	 * 
	 * @param maxLength max length of the source
	 * 
	 * @param minLength min length of the output
	 * 
	 * @return the string
	 */
	public static String shortText(String source, int maxLength, int minLength) {
		if (maxLength >= source.length()) {
			return source;
		}
		int i = maxLength;
		while (i-- > minLength) {
			char c = source.charAt(i);
			if (DELIMITERS.indexOf(c) >= 0) {
				maxLength = i;
				break;
			}
		}
		return source.substring(0, maxLength).trim();
	}

	public static boolean containsNonWordChars(String text) {
        if (text == null || text.isEmpty()) {
            return true;
        }
        return !text.matches("\\w+");
	}

	public static boolean containsWordChars(String text) {
        Pattern p = Pattern.compile("[^a-zA-Z0-9_]");
        Matcher m = p.matcher(text);
        return m.find();
    }

	public static boolean containsSpecialChars(String text) {
        for (int i = 0; i < text.length(); i++) {
        	char c = text.charAt(i);
			if (DELIMITERS.indexOf(c) >= 0) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Get temp directory
	 * 
	 * @return the Path
	 * @throws IOException 
	 */
	public static Path getTempDir(String subDir) throws IOException {
		Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"));
		if (subDir != null && subDir.length() > 0) {
			tmpDir = Files.createTempDirectory(tmpDir, subDir);
		}
        return tmpDir;
	}

	/*
	 * Check that word has numbers
	 * 
	 * @param word input word
	 * 
	 * @return true if word contains numbers
	 */
	public static boolean containsNumbers(String word) {
		for (int i = 0; i < word.length(); i++) {
			char c = word.charAt(i);
			if (c >= '0' && c <= '9') {
			    return true;
			}
		}
		return false;
	}

	/*
	 * Check that word has many digits (more then half)
	 * 
	 * @param word input word
	 * 
	 * @return true if word contains more digits then letters
	 */
	public static boolean isNumeric(String word) {
		int i0 = 0;
		int i1 = 0;
		for (int i = 0; i < word.length(); i++) {
			char c = word.charAt(i);
			if (c >= '0' && c <= '9') {
				i1++;
			} else if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '.' || c == ',')) {
				i0++;
			}
		}
		if (i1 * 2 > i0) {
			return true;
		}
		return false;
	}

	/**
	 * Checks if the word is ASCII.
	 * 
	 * @param word search word
	 * @return true if word is ASCII
	 */
	public static boolean isASCII(String word) {
		int i0 = word.length();
		for (int i = 0; i < i0; i++) {
			char c = word.charAt(i);
			if (c > 127) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Add string lists excluding duplicates.
	 * 
	 * @param array1 List
	 * @param array2 List
	 * @return array
	 */
	public static List<String> joinStringArrays(final List<String> array1, final List<String> array2) {
		ArrayList<String> array = new ArrayList<String>();
		for (String value : array1) {
			if (!array.contains(value)) {
				array.add(value);
			}
		}
		for (String value : array2) {
			if (!array.contains(value)) {
				array.add(value);
			}
		}
		return array;
	}

}
