//
// 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.screwdriver;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import it.ramecera.salad.everyday.ExactException;
import it.ramecera.salad.everyday.Mix;
import it.ramecera.salad.everyday.Universal;

/**
 * <p>
 * Markup objects are simply 5-character words, called <b>stub</b> (stub
 * = "mother" in a bank check book) placed at the beginning of each
 * line; in the public properties we also have the type of markup,
 * indicated for each of them, and the list of <b>occurrencesE</b>, used
 * only later, after reading the ele file.
 * </p>
 * <p>
 * Company: Ramecera™<br>
 * Creation date: 2020-04-09<br>
 * Last updated: 2020-04-09
 * </p>
 * <p>
 * Modifications:<br>
 * </p>
 * 
 * @author M. Maglioni */

final public class Markup {

	//
	// PUBLIC PROPERTIES
	//

	/** the type of markup, according to their use */
	public int p;
	/** the prescription of its frequency */
	public int presenza;
	/** the name of the markup, of 5 lowercase alphabetic characters */
	public String stub;
	/** the name of the other markups that must be present or not
	 * present when there is this, and any allowed or necessary
	 * values */
	public Imply[] implyE;
	/** in reference to the directly preceding markups, the list of the
	 * possible ones */
	public String[] precedingE;
	/** if the markup can only be a wagon */
	public boolean wagon;
	/** list of mandatory wagons that must follow */
	public String[] mandatoryWagE;
	/** list of optional wagons that may follow */
	public String[] optionalWagE;
	/** list of compulsory and optional wagons that can follow
	 * together */
	public String[] unioneE;
	/** list of possible data values */
	public String[] expectedValueE;
	/** the various points where the recurrence of this markup was found
	 * in trains; these numbers are the progressive in the train file,
	 * not in the input file, which in general is longer */
	public List<Train> recurrenceE = new ArrayList<Train>();

	/** ordinary logger */
	private static final Logger logger = Logger.getLogger(Markup.class);

	// CONSTANT

	/** minimum length of a markup */
	final public static int MAR_LP = 2;
	/** maximum length of a markup */
	final public static int MAR_LM = 16;
	/** sign +, as a string */
	final public static String PIU = "+";
	/** sign +, as hexadecimal */
	final public static String PIUX = "2b";
	/** sign +, as char */
	final public static char PIUC = PIU.charAt(0);

	//
	// PUBLIC METHODS
	//

	/**
	 * <p>
	 * This constructor is used to acquire the list of available
	 * markups. The atom array in question receives the data defined at
	 * the beginning of the <b>Screwdriver</b> class.
	 * </p>
	 * <p>
	 * Company: Ramecera™<br>
	 * Creation date: 2020-04-09<br>
	 * Last updated: 2020-04-09
	 * </p>
	 * 
	 * @param atomi the array of strings to load both the names of the
	 *        markups, and each type
	 * @param k the index of the pair to be used
	 * @throws ExactException standard error */
	final public void markupFactory(String[][] atomi, int k)

			throws ExactException {

		// maximum value the markup type can take
		final int TIPO_M = 24;
		// maximum value the presence code can take
		final int PRESENCE_M = 5;
		ExactException ce = new ExactException();
		stub = atomi[k][0].toLowerCase();
		String r = atomi[k][1];
		if (!Pattern.matches("\\d+", r)) {
			ce.datum = "The 2nd element of the markup definition «"
					+ stub + "» is not a number, it is «" + r + "».";
			ce.className = this.getClass().getName();
			ce.code = 2300;
			throw ce;
		}
		String q = atomi[k][2];
		if (!q.matches("\\d")) {
			ce.datum = "The 3rd element of the markup definition «"
					+ stub + "» is not a number, it is «" + q + "».";
			ce.className = this.getClass().getName();
			ce.code = 2301;
			throw ce;
		} else {
			presenza = Integer.valueOf(q).intValue();
		}
		if (presenza > PRESENCE_M) {
			ce.datum = "The 3rd element of the markup «" + stub
					+ "» is greater than " + PRESENCE_M + ".";
			ce.className = this.getClass().getName();
			ce.code = 2301;
			throw ce;
		}
		p = Integer.valueOf(r);
		/* check that the type takes a value between 0 and the value
		 * defined as maximum */
		if (p < 0 | p > TIPO_M) {
			ce.code = 2303;
			ce.datum = "Markup: the number «" + p + "» in the " + k
					+ "th markup definition is incorrect.";
			ce.className = this.getClass().getName();
			throw ce;
		}
		if (!stub.matches("\\p{Alnum}{" + MAR_LP + "," + MAR_LM
				+ "}")) {
			ce.code = 2304;
			ce.datum = "The markup " + Mix.dress_A(stub)
					+ " is incorrect, it must be from " + MAR_LP
					+ " to " + MAR_LM + " alphanumeric characters.";
			ce.className = this.getClass().getName();
			throw ce;
		}
		/* checks on presupposeE must be postponed until after the
		 * acquisition of all markups */
		if (!atomi[k][3].equals("")) { // x2c is the comma
			String[] s = atomi[k][3].toLowerCase().split("\\x2c");
			implyE = new Imply[s.length];
			for (int j = 0; j < implyE.length; j++) {
				implyE[j] = new Imply();
				implyE[j].stub = s[j].trim();
				implyE[j].direct = !implyE[j].stub.startsWith("!");
				if (!implyE[j].direct) {
					implyE[j].stub = implyE[j].stub.substring(1);
				}
				implyE[j].isWagon = implyE[j].stub.startsWith(
						Markup.PIU);
				if (implyE[j].isWagon) {
					/* I remove the initial + */
					implyE[j].stub = implyE[j].stub.substring(1);
				}
				String h[] = implyE[j].stub.split("\\x" + Markup.PIUX);
				if (h.length == 1) {
					implyE[j].value = "";
				} else {
					implyE[j].stub = h[0];
					implyE[j].value = h[1];
				}
				if (implyE[j].stub.equals(stub)) {
					ce.code = 2305;
					ce.datum = "The markup " + Mix.dress_A(
							implyE[j].stub)
							+ "» (supposed) cannot be equal to markup "
							+ Mix.dress_A(stub) + " (main).";
					ce.className = this.getClass().getName();
					throw ce;
				}
			}
		}
		/* in position [4] we find the compulsory wagons */
		int totale = 0;
		String s = atomi[k][4].trim();
		if (s.isEmpty()) {
			mandatoryWagE = null;
		} else {
			mandatoryWagE = s.split("\\x2c");
			totale += mandatoryWagE.length;
			for (int i = 0; i < mandatoryWagE.length; i++) {
				mandatoryWagE[i] = mandatoryWagE[i].trim()
						.toLowerCase();
				if (!Pattern.matches("\\p{Alnum}+", mandatoryWagE[i])) {
					Mix.discloseError("In the definition of " + Mix
							.dress_A(stub) + " the mandatory wagon "
							+ Mix.dress_A(mandatoryWagE[i])
							+ " is incorrect.", "Alarm", logger);
				}
			}
		}
		/* in position [5] we find the optional wagons */
		s = atomi[k][5].trim();
		if (s.isEmpty()) {
			optionalWagE = null;
		} else {
			optionalWagE = s.split("\\x2c");
			totale += optionalWagE.length;
			for (int i = 0; i < optionalWagE.length; i++) {
				optionalWagE[i] = optionalWagE[i].trim().toLowerCase();
				if (!Pattern.matches("\\p{Alnum}+", optionalWagE[i])) {
					Mix.discloseError("In the definition of " + Mix
							.dress_A(stub) + " the optional wagon "
							+ Mix.dress_A(optionalWagE[i])
							+ " is incorrect.", "Alarm", logger);
				}
			}
		}
		if (totale > 0) {
			unioneE = new String[totale];
			if (mandatoryWagE != null) {
				for (int i = 0; i < mandatoryWagE.length; i++) {
					unioneE[i] = mandatoryWagE[i];
				}
			}
			if (optionalWagE != null) {
				for (int i = 0; i < optionalWagE.length; i++) {
					unioneE[i + totale
							- optionalWagE.length] = optionalWagE[i];
				}
			}
		}
		/* in position [7] we find the possible previous locomotives */
		s = atomi[k][7].trim();
		if (s.isEmpty()) {
			precedingE = null;
		} else {
			// x2c is the comma
			precedingE = s.split("\\x2c");
			for (int i = 0; i < precedingE.length; i++) {
				precedingE[i] = precedingE[i].trim().toLowerCase();
				if (!Pattern.matches("\\p{Alnum}+", precedingE[i])) {
					Mix.discloseError("In the definition of " + Mix
							.dress_A(stub) + " the preceding locomotor "
							+ Mix.dress_A(precedingE[i])
							+ " is incorrect.", "Alarm", logger);
				}
			}
		}
		/* position 6 is dominion, a list of strings delimited by
		 * commas */
		String w = atomi[k][6].trim();
		String tipo = atomi[k][1].trim();
		if (tipo.equals("23")) {
			expectedValueE = new String[Universal.languageE.length];
			for (int i = 0; i < Universal.languageE.length; i++) {
				expectedValueE[i] = Universal.languageE[i][1];
			}
		} else if (w.isEmpty()) {
			expectedValueE = null;
		} else {
			expectedValueE = w.split("\\x2c"); // x2c is comma
			for (int i = 0; i < expectedValueE.length; i++) {
				expectedValueE[i] = expectedValueE[i].trim();
			}
		}
	}
}
