package de.unituebingen.ub.ubtools.solrmarcMixin;

import java.text.Collator;
import java.util.*;
import org.marc4j.marc.DataField;
import org.marc4j.marc.Record;
import org.marc4j.marc.Subfield;
import org.marc4j.marc.VariableField;
import org.solrmarc.index.SolrIndexerMixin;
import de.unituebingen.ub.ubtools.solrmarcMixin.*;

public class IxTheoKeywordChains extends SolrIndexerMixin {

    private final static String KEYWORD_DELIMITER = "/";
    private final static String SUBFIELD_CODES = "abctnpz";
    private final static TuelibMixin tuelibMixin = new TuelibMixin();

    public Set<String> getKeyWordChain(final Record record, final String fieldSpec, final String lang) {
        final List<VariableField> variableFields = record.getVariableFields(fieldSpec);
        final Map<Character, List<String>> keyWordChains = new HashMap<>();

        for (final VariableField variableField : variableFields) {
            final DataField dataField = (DataField) variableField;
            processField(dataField, keyWordChains, lang);
        }
        return concatenateKeyWordsToChains(keyWordChains);
    }

    /**
     * Create set version of the terms contained in the keyword chains
     */

    public Set<String> getKeyWordChainBag(final Record record, final String fieldSpec, final String lang) {
        final List<VariableField> variableFields = record.getVariableFields(fieldSpec);
        final Map<Character, List<String>> keyWordChains = new HashMap<>();
        final Set<String> keyWordChainBag = new HashSet<>();

        for (final VariableField variableField : variableFields) {
            final DataField dataField = (DataField) variableField;
            processField(dataField, keyWordChains, lang);
        }

        for (List<String> keyWordChain : keyWordChains.values()) {
            keyWordChainBag.addAll(keyWordChain);
        }

        return keyWordChainBag;
    }

    public Set<String> getKeyWordChainSorted(final Record record, final String fieldSpec, final String lang) {
        final List<VariableField> variableFields = record.getVariableFields(fieldSpec);
        final Map<Character, List<String>> keyWordChains = new HashMap<>();

        for (final VariableField variableField : variableFields) {
            final DataField dataField = (DataField) variableField;
            processField(dataField, keyWordChains, lang);

            // Sort keyword chain
            final char chainID = dataField.getIndicator1();
            final List<String> keyWordChain = getKeyWordChain(keyWordChains, chainID);
            Collator collator = Collator.getInstance(Locale.forLanguageTag(lang));
            Collections.sort(keyWordChain, collator);
        }
        return concatenateKeyWordsToChains(keyWordChains);
    }

    /**
     * Extracts the keyword from data field and inserts it into the right
     * keyword chain.
     */
    private void processField(final DataField dataField, final Map<Character, List<String>> keyWordChains, String lang) {
        final char chainID = dataField.getIndicator1();
        final List<String> keyWordChain = getKeyWordChain(keyWordChains, chainID);

        boolean gnd_seen = false;
        StringBuilder keyword = new StringBuilder();
        // Collect elements within one chain in case there is a translation for a whole string
        List<String> complexElements = new ArrayList<String>();
        for (final Subfield subfield : dataField.getSubfields()) {
            if (gnd_seen) {
                if (SUBFIELD_CODES.indexOf(subfield.getCode()) != -1) {
                    if (keyword.length() > 0) {
                        if (subfield.getCode() == 'z') {
                            keyword.append(" (" + tuelibMixin.translateTopic(subfield.getData(), lang) + ")");
                            continue;
                        }
                        else if (subfield.getCode() == 'n')
                            keyword.append(" ");
                        else if (subfield.getCode() == 'p')
                            keyword.append(". ");
                        else
                            keyword.append(", ");
                    }
                    final String term = subfield.getData().trim();
                    keyword.append(tuelibMixin.translateTopic(term, lang));
                    complexElements.add(term);
                } else if (subfield.getCode() == '9' && keyword.length() > 0 && subfield.getData().startsWith("g:")) {
                    // For Ixtheo-translations the specification in the g:-Subfield is appended in angle
                    // brackets, so this is a special case where we have to begin from scratch
                    final String specification = subfield.getData().substring(2);
                    final Subfield germanASubfield = dataField.getSubfield('a');
                    if (germanASubfield != null) {
                        final String translationCandidate = germanASubfield.getData() + " <" + specification + ">";
                        final String translation = tuelibMixin.translateTopic(translationCandidate, lang);
                        keyword.setLength(0);
                        keyword.append(translation.replaceAll("<", "(").replaceAll(">", ")"));
                    }
                    else {
                        keyword.append(" (");
                        keyword.append(tuelibMixin.translateTopic(specification, lang));
                        keyword.append(')');
                    }
                }

            } else if (subfield.getCode() == '2' && subfield.getData().equals("gnd"))
                gnd_seen = true;
        }

        if (keyword.length() > 0) {
            // Check whether there exists a translation for the whole chain
            final String complexTranslation = (complexElements.size() > 1) ?
                                              tuelibMixin.getTranslationOrNull(String.join(" / ", complexElements), lang) : null;
            String keywordString = (complexTranslation != null) ? complexTranslation : keyword.toString();
            keywordString = keywordString.replace("/", "\\/");
            keyWordChain.add(keywordString);
        }
    }

    /**
     * Finds the right keyword chain for a given chain id.
     *
     * @return A map containing the keywords of the chain (id -> keyword), or an
     *         empty map.
     */
    private List<String> getKeyWordChain(final Map<Character, List<String>> keyWordChains, final char chainID) {
        List<String> keyWordChain = keyWordChains.get(chainID);
        if (keyWordChain == null) {
            keyWordChain = new ArrayList<>();
            keyWordChains.put(chainID, keyWordChain);
        }

        return keyWordChain;
    }

    private Set<String> concatenateKeyWordsToChains(final Map<Character, List<String>> keyWordChains) {
        final List<Character> chainIDs = new ArrayList<>(keyWordChains.keySet());
        Collections.sort(chainIDs);

        final Set<String> chainSet = new LinkedHashSet<>();
        for (final Character chainID : chainIDs) {
            chainSet.add(keyChainToString(keyWordChains.get(chainID)));
        }
        return chainSet;
    }

    private String keyChainToString(final List<String> keyWordChain) {
        final StringBuilder buffer = new StringBuilder();
        for (final String keyWord : keyWordChain) {
            buffer.append(KEYWORD_DELIMITER);
            buffer.append(keyWord);
        }

        if (buffer.length() == 0) {
            return "";
        }
        // Discard leading keyword delimiter.
        return buffer.toString().substring(1);
    }
}

@@ -498,16 +498,17 @@
 %22abctnpz
+f
 %22;%0A    p
@@ -3825,32 +3825,235 @@
               %7D%0A
+                        else if (subfield.getCode() == 'f') %7B%0A                            keyword.append(%22 (%22 + subfield.getData() + %22)%22);%0A                            continue;%0A                        %7D%0A
                 

Include subfield f in KWC