package uk.ac.ebi.arrayexpress.utils.search;

import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmValue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import uk.ac.ebi.arrayexpress.app.Application;
import uk.ac.ebi.arrayexpress.components.SaxonEngine;
import uk.ac.ebi.arrayexpress.utils.RegExpHelper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Search experiments based on full-text information stored in ExperimentText class
 */
public class ExperimentSearch
{
    // logging machinery
    private final Log log = LogFactory.getLog(getClass());

    // array of ExperimentText objects in the same order they exist in the document
    private List<ExperimentText> expText = new ArrayList<ExperimentText>();

    // mapping of experiment accession numbers to experiment text indices
    private Map<String, Integer> accessionIdx = new HashMap<String, Integer>();

    public boolean isEmpty()
    {
        return expText.isEmpty();
    }

    public void buildText( XdmNode experiments )
    {
        if (null != experiments && null != experiments.getUnderlyingNode()) {
            try {
                expText.clear();
                accessionIdx.clear();

                XdmValue exps = ((SaxonEngine)Application.getAppComponent("SaxonEngine")).evaluateXPath(experiments, "/experiments/experiment");
                if (null != exps) {
                    for (XdmItem expObj : exps) {
                        ExperimentText text = new ExperimentText().populateFromExperiment((XdmNode)expObj);
                        expText.add(text);
                        accessionIdx.put(text.accession, expText.size() - 1);
                    }
                }
            } catch ( Throwable x ) {
                log.error("Caught an exception:", x);
            }
        }
    }

    public boolean matchText( String textIdx, String keywords, boolean wholeWords )
    {
        int idx = Integer.parseInt(textIdx);
        return matchString(expText.get(idx).text, keywords, wholeWords);
    }

    public boolean matchAccession( String textIdx, String accession )
    {
        int idx = Integer.parseInt(textIdx);
        return expText.get(idx).accessions.contains(" ".concat(accession).concat(" ").toLowerCase());
    }

    public boolean matchSpecies( String textIdx, String species )
    {
        int idx = Integer.parseInt(textIdx);
        return expText.get(idx).species.contains(species.trim().toLowerCase());
    }

    public boolean matchArray( String textIdx, String array )
    {
        int idx = Integer.parseInt(textIdx);
        return expText.get(idx).array.contains(array.trim().toLowerCase());
    }

    public boolean matchExperimentType( String textIdx, String experimentType )
    {
        int idx = Integer.parseInt(textIdx);
        return expText.get(idx).experimentType.contains(experimentType.trim().toLowerCase());
    }

    public boolean matchUser( String textIdx, String user )
    {
        int idx = Integer.parseInt(textIdx);
        return expText.get(idx).users.contains(" ".concat(user).concat(" "));
    }

    public boolean isAccessible( String accession, String user )
    {
        if (user.equals("0")) {
            return true;
        }

        Integer idx = accessionIdx.get(accession.toLowerCase());
        return (null != idx) && matchUser(String.valueOf(idx), user);
    }

    public boolean doesPresent( String accession )
    {
        return accessionIdx.containsKey(accession.toLowerCase());
    }

    private String keywordToPattern( String keyword, boolean wholeWord )
    {
        return (wholeWord ? "\\b\\Q" + keyword + "\\E\\b" : "\\Q" + keyword + "\\E");
    }

    private boolean matchString( String input, String keywords, boolean wholeWords )
    {
        // trim spaces on both sides
        keywords = keywords.trim();

        if (0 < keywords.length()) {
            // by default (i.e. no keywords) it'll always match

            if (2 < keywords.length() && '"' == keywords.charAt(0) && '"' == keywords.charAt(keywords.length() - 1)) {
                // if keywords are adorned with double-quotes we do phrase matching
                return new RegExpHelper(keywordToPattern(keywords.substring(1, keywords.length() - 1), true), "i").test(input);
            } else {
                // any keyword fails -> no match :)
                String[] kwdArray = keywords.split("\\s+");
                for ( String keyword : kwdArray ) {
                    if (!new RegExpHelper(keywordToPattern(keyword, wholeWords), "i").test(input))
                        return false;
                }
            }
        }
        return true;
    }
}

@@ -2020,34 +2020,105 @@
 %0A    %7B%0A        i
-nt
+f (null == textIdx) %7B%0A            return false;%0A        %7D%0A        Integer
  idx = Integer.p
@@ -2149,16 +2149,33 @@
   return
+ (null != idx) &&
  matchSt
@@ -2309,34 +2309,105 @@
 %0A    %7B%0A        i
-nt
+f (null == textIdx) %7B%0A            return false;%0A        %7D%0A        Integer
  idx = Integer.p
@@ -2430,32 +2430,49 @@
 ;%0A        return
+ (null != idx) &&
  expText.get(idx
@@ -2623,34 +2623,105 @@
 %0A    %7B%0A        i
-nt
+f (null == textIdx) %7B%0A            return false;%0A        %7D%0A        Integer
  idx = Integer.p
@@ -2744,32 +2744,49 @@
 ;%0A        return
+ (null != idx) &&
  expText.get(idx
@@ -2911,34 +2911,105 @@
 %0A    %7B%0A        i
-nt
+f (null == textIdx) %7B%0A            return false;%0A        %7D%0A        Integer
  idx = Integer.p
@@ -3032,32 +3032,49 @@
 ;%0A        return
+ (null != idx) &&
  expText.get(idx
@@ -3213,34 +3213,105 @@
 %0A    %7B%0A        i
-nt
+f (null == textIdx) %7B%0A            return false;%0A        %7D%0A        Integer
  idx = Integer.p
@@ -3334,32 +3334,49 @@
 ;%0A        return
+ (null != idx) &&
  expText.get(idx
@@ -3521,18 +3521,89 @@
        i
-nt
+f (null == textIdx) %7B%0A            return false;%0A        %7D%0A        Integer
  idx = I
@@ -3634,32 +3634,49 @@
 ;%0A        return
+ (null != idx) &&
  expText.get(idx

fix to prevent unnecessary exceptions