//
// https://www.ramecera.it/myscrewdriver.html
// e-mail: screwdriver@ramecera.it
//
/* This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies this
 * distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html */
package it.ramecera.salad.everyday;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import it.ramecera.salad.file.ReadMyFile;
import it.ramecera.salad.graphic.Dynamics;

/**
 * <p>
 * Static methods of various utility.
 * </p>
 * <p>
 * Company: Ramecera™<br>
 * Creation date: 2020-04-09<br>
 * Last updated: 2020-04-09
 * </p>
 * <p>
 * Changes:<br>
 * </p>
 * 
 * @author M. Maglioni
 * @since 1.2.3 */

public class Strings {
	//
	/** XML and HTML entities */
	// public static final String[] tokenE = { "quot", "amp", "apos",
	// "lt",
	// "gt",
	// "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar",
	// "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg",
	// "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro",
	// "para",
	// "middot", "cedil", "sup1", "ordm", "raquo", "frac14", "frac12",
	// "frac34", "iquest", "Agrave", "Aacute", "Acirc", "Atilde",
	// "Auml",
	// "Aring", "AElig", "Ccedil", "Egrave", "Eacute", "Ecirc", "Euml",
	// "Igrave", "Iacute", "Icirc", "Iuml", "ETH", "Ntilde", "Ograve",
	// "Oacute", "Ocirc", "Otilde", "Ouml", "times", "Oslash", "Ugrave",
	// "Uacute", "Ucirc", "Uuml", "Yacute", "THORN", "szlig", "agrave",
	// "aacute", "acirc", "atilde", "auml", "aring", "aelig", "ccedil",
	// "egrave", "eacute", "ecirc", "euml", "igrave", "iacute", "icirc",
	// "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde",
	// "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", "uuml",
	// "yacute", "thorn", "yuml", "OElig", "oelig", "Scaron", "scaron",
	// "Yuml", "fnof", "circ", "tilde", "Alpha", "Beta", "Gamma",
	// "Delta",
	// "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda",
	// "Mu",
	// "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon",
	// "Phi", "Chi", "Psi", "Omega", "alpha", "beta", "gamma", "delta",
	// "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda",
	// "mu",
	// "nu", "xi", "omicron", "pi", "rho", "sigmaf", "sigma", "tau",
	// "upsilon", "phi", "chi", "psi", "omega", "thetasym", "upsih",
	// "piv", "ensp", "emsp", "thinsp", "zwnj", "zwj", "lrm", "rlm",
	// "ndash", "mdash", "lsquo", "rsquo", "sbquo", "ldquo", "rdquo",
	// "bdquo", "dagger", "Dagger", "bull", "hellip", "permil", "prime",
	// "Prime", "lsaquo", "rsaquo", "oline", "frasl", "euro", "image",
	// "weierp", "real", "trade", "alefsym", "larr", "uarr", "rarr",
	// "darr", "harr", "crarr", "lArr", "uArr", "rArr", "dArr", "hArr",
	// "forall", "part", "exist", "empty", "nabla", "isin", "notin",
	// "ni",
	// "prod", "sum", "minus", "lowast", "radic", "prop", "infin",
	// "ang",
	// "and", "or", "cap", "cup", "int", "there4", "sim", "cong",
	// "asymp",
	// "ne", "equiv", "le", "ge", "sub", "sup", "nsub", "sube", "supe",
	// "oplus", "otimes", "perp", "sdot", "vellip", "lceil", "rceil",
	// "lfloor", "rfloor", "lang", "rang", "loz", "spades", "clubs",
	// "hearts", "diams" };
	//
	// public static final String[] tradE = { "quot", "amp", "apos",
	// "lt",
	// "gt",
	// "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar",
	// "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg",
	// "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro",
	// "para",
	// "middot", "cedil", "sup1", "ordm", "raquo", "frac14", "frac12",
	// "frac34", "iquest", "Agrave", "Aacute", "Acirc", "Atilde",
	// "Auml",
	// "Aring", "AElig", "Ccedil", "Egrave", "Eacute", "Ecirc", "Euml",
	// "Igrave", "Iacute", "Icirc", "Iuml", "ETH", "Ntilde", "Ograve",
	// "Oacute", "Ocirc", "Otilde", "Ouml", "times", "Oslash", "Ugrave",
	// "Uacute", "Ucirc", "Uuml", "Yacute", "THORN", "szlig", "agrave",
	// "aacute", "acirc", "atilde", "auml", "aring", "aelig", "ccedil",
	// "è", "é", "ecirc", "euml", "igrave", "iacute", "icirc", "iuml",
	// "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", "ouml",
	// "divide", "oslash", "ugrave", "uacute", "ucirc", "uuml",
	// "yacute",
	// "thorn", "yuml", "OElig", "oelig", "Scaron", "scaron", "Yuml",
	// "fnof", "circ", "tilde", "Alpha", "Beta", "Gamma", "Delta",
	// "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda",
	// "Mu",
	// "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon",
	// "Phi", "Chi", "Psi", "Omega", "alpha", "beta", "gamma", "delta",
	// "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda",
	// "mu",
	// "nu", "xi", "omicron", "pi", "rho", "sigmaf", "sigma", "tau",
	// "upsilon", "phi", "chi", "psi", "omega", "thetasym", "upsih",
	// "piv", "ensp", "emsp", "thinsp", "zwnj", "zwj", "lrm", "rlm",
	// "ndash", "mdash", "lsquo", "rsquo", "sbquo", "ldquo", "rdquo",
	// "bdquo", "dagger", "Dagger", "bull", "hellip", "permil", "prime",
	// "Prime", "lsaquo", "rsaquo", "oline", "frasl", "euro", "image",
	// "weierp", "real", "trade", "alefsym", "larr", "uarr", "rarr",
	// "darr", "harr", "crarr", "lArr", "uArr", "rArr", "dArr", "hArr",
	// "forall", "part", "exist", "empty", "nabla", "isin", "notin",
	// "ni",
	// "prod", "sum", "minus", "lowast", "radic", "prop", "infin",
	// "ang",
	// "and", "or", "cap", "cup", "int", "there4", "sim", "cong",
	// "asymp",
	// "ne", "equiv", "le", "ge", "sub", "sup", "nsub", "sube", "supe",
	// "oplus", "otimes", "perp", "sdot", "vellip", "lceil", "rceil",
	// "lfloor", "rfloor", "lang", "rang", "loz", "spades", "clubs",
	// "hearts", "diams" };

	/** entities to convert */
	public static final String[] tokenE = { "quot", "amp", "apos", "lt",
			"gt", "agrave", "egrave", "eacute", "igrave", "ograve",
			"ugrave", "rsquo", "ndash" };

	/** converted entities */
	public static final String[] tradE = { "\"", "&", "'", "<", ">",
			"à", "è", "é", "ì", "ò", "ù", "'", "-" };

	/**
	 * <p>
	 * Replaces the entities with what is indicated in the second table.
	 * array
	 * </p>
	 * <p>
	 * version: 1.0.0<br>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param s the sentence to be treated
	 * @return the array of words */
	public static String sostituisciEntities(final String s) {
		if (s == null) {
			return null;
		}
		String t = s;
		String p = null;
		String q = null;
		int j = t.indexOf(38);
		while (j > -1) {
			p = t.substring(j + 1);
			q = t.substring(0, j);
			int l = 0;
			for (; l < tokenE.length; l++) {
				if (p.startsWith(tokenE[l] + ";")) {
					break;
				}
			}
			if (l < tokenE.length) {
				t = q + tradE[l] + p.substring(tokenE[l].length() + 1);
			}
			j = t.indexOf(38, j + 1);
		}
		return t;
	}

	/**
	 * <p>
	 * Starting from a sentence, it produces the detached words and
	 * packed in an array.
	 * </p>
	 * <p>
	 * version: 1.0.0<br>
	 * Creation date: 2020-04-09<br>
	 * last modified date : 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param s the sentence to be treated
	 * @return the array of words */
	public static String[] parole_A(final String s) {
		String t = compact_A(s);
		return t.split(Candies.SPACE);
	}

	/**
	 * <p>
	 * Starting from a sentence, compact the multiple spaces between
	 * words by replacing them with a single space; it also eliminates
	 * head and tail spaces.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param s the sentence to be treated
	 * @return the compacted sentence */
	public static String compact_A(final String s) {
		if (s == null)
			return null;
		String t = s.replaceAll("[\\t\\n\\x0B\\f\\r]", Candies.SPACE); // 2020-04-09
		t = t.trim();
		while (t.indexOf(Mix.DOUBLEBLANK) > -1)
			t = t.replaceAll(Mix.DOUBLEBLANK, Candies.SPACE);
		return t;
	}

	/**
	 * <p>
	 * Starting from a sentence, compact the multiple spaces between
	 * words by replacing them with a single space; it also eliminates
	 * head and tail spaces.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param s the sentence to be treated
	 * @return the compacted sentence */
	public static String compatta_B(final String s) {
		if (s == null)
			return null;
		String t = s.replaceAll("[\\t\\n\\x0B\\f\\r]", Candies.SPACE); // 2020-04-09
		t = t.trim();
		while (t.indexOf(Mix.DOUBLEBLANK) > -1)
			t = t.replaceAll(Mix.DOUBLEBLANK, Candies.SPACE);
		/* refinement for java sources:). it becomes ). */
		t = t.replaceAll("\\x29\\x20\\x2e", ").");
		return t;
	}

	/**
	 * <p>
	 * Search for a string within a list-array of strings. If it finds
	 * it, it gives the row-column coordinates; if it does not find it,
	 * it gives coordinates -1 and -1.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param z the array list to search for
	 * @param s the string to search for
	 * @return the coordinates of the found string, or the pair of
	 *         values ​​(-1, -1) */
	public static Point2D cercaStringa_A(List<String> z,
			final String s) {

		int zQ = z.size();
		int j = 0;
		String riga = null;
		for (; j < zQ; j++) {
			riga = z.get(j);
			if (riga.indexOf(s) > -1)
				break;
		}
		if (j == zQ)
			return new Point2D(-1, -1);
		else
			return new Point2D(j, z.get(j).indexOf(s));
	}

	/**
	 * <p>
	 * Search for two ordered strings within an array list of strings.
	 * If it finds them, it gives the row-column coordinates of the
	 * first string; if it does not find it, it gives coordinates -1 and
	 * -1. Variant of the previous one.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param z the array list to search for
	 * @param s0 the 1st string to search
	 * @param s1 the 2nd string to search
	 * @return the coordinates of the string found, or the pair of
	 *         values ​​(-1, -1) */
	public static Point2D cercaStringa_A(List<String> z,
			final String s0, final String s1) {

		int zQ = z.size();
		int j = 0;
		String riga = null;
		for (; j < zQ; j++) {
			riga = z.get(j);
			if (riga.indexOf(s0) < riga.indexOf(s1) & riga.indexOf(
					s0) > -1)
				break;
		}
		if (j == zQ)
			return new Point2D(-1, -1);
		else
			return new Point2D(j, z.get(j).indexOf(s0));
	}

	/**
	 * <p>
	 * Searches for three strings sorted within a list-array of strings.
	 * If it finds them, it gives the row-column coordinates of the
	 * first string; if it does not find it, it gives coordinates -1 and
	 * -1. Variant of the previous one.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param z the array list to search for
	 * @param s0 the 1st string to search
	 * @param s1 the 2nd string to search
	 * @param s2 the 3rd string to search
	 * @return the coordinates of the found string, or the pair of
	 *         values (-1, -1) */
	public static Point2D cercaStringa_A(List<String> z,
			final String s0, final String s1, final String s2) {

		int zQ = z.size();
		int j = 0;
		String riga = null;
		for (; j < zQ; j++) {
			riga = z.get(j);
			if (riga.indexOf(s0) < riga.indexOf(s1) & riga.indexOf(
					s1) < riga.indexOf(s2) & riga.indexOf(s0) > -1)
				break;
		}
		if (j == zQ)
			return new Point2D(-1, -1);
		else
			return new Point2D(j, z.get(j).indexOf(s0));
	}

	/**
	 * <p>
	 * Searches for a specific occurrence of a string within an array
	 * list of strings. If it finds it, it gives the row-column
	 * coordinates; if it does not find it, it gives coordinates -1 and
	 * -1. Variant of the previous one.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param z the array list to search for
	 * @param s the string to search for
	 * @param occo occurrence of the string to search for
	 * @return the coordinates of the found string, or the pair of
	 *         values ​​(-1, -1) */
	public static Point2D cercaStringa_A(List<String> z, final String s,
			final int occo) {

		int zQ = z.size();
		int j = 0;
		int k = 0;
		String riga = null;
		for (; j < zQ; j++) {
			riga = z.get(j);
			if (riga.indexOf(s) > -1)
				k++;
			if (k == occo)
				break;
		}
		if (j == zQ)
			return new Point2D(-1, -1);
		else
			return new Point2D(j, z.get(j).indexOf(s));
	}

	/**
	 * <p>
	 * Extracts tokens from a line of CSV files. If the line ends with
	 * the delimiting character, the delimiting character is not taken
	 * into account.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param t the line to be processed
	 * @param pv the separation character, usually;
	 * @param v2 the delimiting character, usually "
	 * @return the resulting string array */
	public static String[] gettoni_A(final String t, final String pv,
			final String v2) {
		if (t == null | pv.length() != 1 | v2.length() != 1) {
			return null;
		}
		String[] s = t.split(pv);
		for (int j = 0; j < s.length; j++) {
			/* I have to treat each token (hypothesis that it is
			 * quotation marks): 1) I trim it, 2) I remove the first and
			 * last character quotation marks, 3) I replace the double
			 * character quotation marks with the single character */
			s[j] = s[j].trim();
			if (s[j].length() > 1 && s[j].startsWith(v2) && s[j]
					.endsWith(v2)) {
				s[j] = s[j].substring(1, s[j].length() - 1);
				s[j] = s[j].replaceAll(v2 + "{2}", v2);
			}
		}
		return s;
	}

	/**
	 * <p>
	 * Extracts the tokens from a line of CSV files, but (assuming that
	 * the characters are the semicolon and the double quotation mark)
	 * it takes into account the possibility that inside a token
	 * delimited by quotes there is a semicolon, which therefore should
	 * not be considered a delimiter. If a line ends with the delimiting
	 * character, the delimiting character is not taken into account.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param t the line to be processed
	 * @param pv the separation character, usually;
	 * @param v2 the delimitation character, usually "
	 * @return the resulting string array
	 * @throws ExactException standard exception */
	public static String[] tokens_B(final String t, final String pv,
			final String v2)

			throws ExactException {

		if (t == null || t.length() == 0) {
			throw new ExactException(
					"The line to analyze is null or empty.", 101500,
					"Strings", "gettoni_B");
		} else if (pv == null || pv.length() != 1) {
			throw new ExactException("The separation character"
					+ " is null, empty or it is one.", 101501,
					"Strings", "gettoni_B");
		} else if (v2 == null || v2.length() != 1) {
			throw new ExactException("The delimitation character"
					+ " is null, empty or it is one.", 101502,
					"Strings", "gettoni_B");
		} else if (v2.equals(pv)) {
			throw new ExactException("The delimitation character"
					+ " must be different from the separation one.",
					101502, "Strings", "tokens_B");
		}
		/* now I find a string that is certainly absent from the line to
		 * be examined, and that is suitable in reference to the
		 * separation and delimitation characters; I only use digits */
		int k = 0;
		String w = "";
		String w1 = null;
		for (; k < Integer.MAX_VALUE; k++) {
			w1 = w + k;
			if (t.indexOf(w1) == -1 & w1.indexOf(v2) == -1 & w1.indexOf(
					pv) == -1) {
				break;
			}
		}
		/* the string w1 is used in place of the semicolons found inside
		 * strings marked with quotes. A restore operation follows */
		/* with this change I take into account that the split operation
		 * does not take into account a possible separation character
		 * that is exactly at the bottom of the line; this would be a
		 * mistake when I go to calculate the tokens quantities */
		String t0 = t + " ";
		String[] b = t0.split(v2);
		/* first check that the double quotes are in even quantities */
		int bL = b.length;
		if (bL % 2 == 0) {
			throw new ExactException(
					"The quantity of the delimitation characters"
							+ " is an odd number, it is incorrect: "
							+ t, 101503, "Strings", "gettoni_B");
		}
		String t1 = "";
		for (int i = 0; i < b.length; i++) {
			if (i % 2 == 1 & b[i].indexOf(pv) > -1) {
				b[i] = b[i].replaceAll(pv, w1);
			}
			t1 += b[i] + v2;
		}
		t1 = t1.substring(0, t1.length() - v2.length());
		String[] s = t1.split(pv);
		for (int j = 0; j < s.length; j++) {
			/* I have to treat each token (hypothesis that it is
			 * quotation marks): 1) I trim it, 2) I remove the first and
			 * last character quotation marks, 3) I replace the double
			 * character quotation marks with the single character */
			s[j] = s[j].trim();
			if (s[j].length() > 1 && s[j].startsWith(v2) && s[j]
					.endsWith(v2)) {
				s[j] = s[j].substring(1, s[j].length() - 1);
				s[j] = s[j].replaceAll(v2 + "{2}", v2);
				s[j] = s[j].replaceAll(w1, pv); // restoration
			}
		}
		return s;
	}

	/**
	 * <p>
	 * Extracts the tokens from a line of CSV files, but (assuming that
	 * the characters are the semicolon and the double quotation mark)
	 * it takes into account the possibility that inside a token
	 * delimited by quotes there must be a semicolon, which therefore
	 * should not be considered a delimiter
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * 
	 * @param g the file input
	 * @param pv the separation character, usually;
	 * @param v2 the delimitation character, usually "
	 * @param din the progress bar with its neighboring labels
	 * @return the resulting string array
	 * @throws ExactException standard exception
	 * @throws IOException standard exception
	 * @throws FileNotFoundException standard exception */
	public static List<List<String>> tokens_C(final String g,
			final String pv, final String v2, Dynamics din)

			throws ExactException, FileNotFoundException, IOException {

		if (g == null || g.length() == 0) {
			throw new ExactException("The given path is null or empty.",
					101504, "Strings", "tokens_C");
		}
		List<String> s = ReadMyFile.zebra_B(g);
		List<List<String>> tE = new ArrayList<List<String>>();
		/* cycle on each row */
		String[] q = null;
		List<String> tokenE = null;
		din.head.setText("0");
		din.head.paintImmediately(din.access.sinistra);
		din.pb.setMinimum(0);
		din.pb.setMaximum(s.size());
		for (int i = 0; i < s.size(); i++) {
			din.head.setText(String.valueOf(i));
			din.head.paintImmediately(din.access.sinistra);
			din.pb.setValue(i + 1);
			din.pb.paintImmediately(din.access.bar);
			q = tokens_B(s.get(i), pv, v2);
			tokenE = new ArrayList<String>();
			for (String f : q) {
				tokenE.add(f);
			}
			tE.add(tokenE);
		}
		return tE;
	}

	/**
	 * <p>
	 * Clear all tags and spaces; therefore all words are sewn together.
	 * </p>
	 * 
	 * @param s my text
	 * @return the normalized string */
	public static String normalizzaTestoTaggato(final String s) {
		if (s == null) {
			return null;
		}
		String t = s.replaceAll("<\\p{ASCII}+?>", "");
		t = t.replaceAll("\\p{Space}+", "");
		return t;
	}

	/**
	 * <p>
	 * Searches for the first occurrence of a substring in a string and
	 * replaces it with something else.
	 * </p>
	 * <p>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * <p>
	 * Changes:<br>
	 * </p>
	 * 
	 * @param s the starting string
	 * @param target the substring to search for
	 * @param replace the string to replace
	 * @return the resulting string */
	public static String replaceFirst(final String s,
			final String target, final String replace) {
		if (s == null) {
			return null;
		}
		int i = s.indexOf(target);
		if (i == -1) {
			return s;
		} else {
			return s.substring(0, i) + replace + s.substring(i + target
					.length());
		}
	}
}
