package uk.ac.ebi.atlas.trader;

import uk.ac.ebi.atlas.model.experiment.ExperimentDesign;
import uk.ac.ebi.atlas.model.OntologyTerm;
import uk.ac.ebi.atlas.model.SampleCharacteristic;
import com.google.common.collect.ImmutableList;
import org.springframework.context.annotation.Scope;
import uk.ac.ebi.atlas.commons.readers.TsvReader;
import uk.ac.ebi.atlas.model.resource.AtlasResource;
import uk.ac.ebi.atlas.resource.DataFileHub;

import javax.inject.Inject;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Named
@Scope("prototype")
public class ExperimentDesignParser {

    private static final String ONTOLOGY_TERM_DELIMITER = " ";

    static final Pattern SAMPLE_COLUMN_HEADER_PATTERN = Pattern.compile("\\s*Sample Characteristic\\[(.*?)\\]\\s*");
    private static final Pattern SAMPLE_ONTOLOGY_TERM_COLUMN_HEADER_PATTERN = Pattern.compile("\\s*Sample Characteristic Ontology Term\\[(.*?)\\]\\s*");

    private static final Pattern FACTOR_COLUMN_HEADER_PATTERN = Pattern.compile("\\s*Factor Value\\[(.*?)\\]\\s*");
    private static final Pattern FACTOR_VALUE_ONTOLOGY_TERM_COLUMN_HEADER_PATTERN = Pattern.compile("\\s*Factor Value Ontology Term\\[(.*?)\\]\\s*");

    private final DataFileHub dataFileHub;

    @Inject
    ExperimentDesignParser(DataFileHub dataFileHub) {
        this.dataFileHub = dataFileHub;
    }


    public ExperimentDesign parse(String experimentAccession) {

        AtlasResource<TsvReader> r = dataFileHub.getExperimentFiles(experimentAccession).experimentDesign;

        if (!r.exists()) {
            throw new IllegalStateException(String.format("%s does not exist", r));
        }
        List<String[]> csvLines = new ArrayList<>(r.get().readAll());

        String[] headerLine = csvLines.remove(0);

        Map<String, Integer> sampleHeaderIndexes = extractHeaderIndexes(headerLine, SAMPLE_COLUMN_HEADER_PATTERN);
        Map<String, Integer> sampleValueOntologyTermHeaderIndexes = extractHeaderIndexes(headerLine, SAMPLE_ONTOLOGY_TERM_COLUMN_HEADER_PATTERN);

        Map<String, Integer> factorHeaderIndexes = extractHeaderIndexes(headerLine, FACTOR_COLUMN_HEADER_PATTERN);
        Map<String, Integer> factorValueOntologyTermHeaderIndexes = extractHeaderIndexes(headerLine, FACTOR_VALUE_ONTOLOGY_TERM_COLUMN_HEADER_PATTERN);

        int headersStartIndex = headerLine.length - (sampleHeaderIndexes.size() + sampleValueOntologyTermHeaderIndexes.size() + factorHeaderIndexes.size() + factorValueOntologyTermHeaderIndexes.size());

        ExperimentDesign experimentDesign = new ExperimentDesign();
        for (int i = 0; i < headersStartIndex; i++) {
            experimentDesign.addAssayHeader(headerLine[i]);
        }

        for (String[] line : csvLines) {
            String runOrAssay = line[0];
            if (headersStartIndex > 1) {
                experimentDesign.putArrayDesign(runOrAssay, line[1]);
            }

            for (String sampleHeader : sampleHeaderIndexes.keySet()) {
                String sampleValue = line[sampleHeaderIndexes.get(sampleHeader)];

                Integer sampleValueOntologyTermIndex = sampleValueOntologyTermHeaderIndexes.get(sampleHeader);
                OntologyTerm[] sampleValueOntologyTerms = createOntologyTerms(line, sampleValueOntologyTermIndex);
                SampleCharacteristic sampleCharacteristic = SampleCharacteristic.create(sampleHeader, sampleValue, sampleValueOntologyTerms);

                experimentDesign.putSampleCharacteristic(runOrAssay, sampleHeader, sampleCharacteristic);
            }

            for (String factorHeader : factorHeaderIndexes.keySet()) {
                String factorValue = line[factorHeaderIndexes.get(factorHeader)];

                Integer factorValueOntologyTermIndex = factorValueOntologyTermHeaderIndexes.get(factorHeader);
                OntologyTerm[] factorValueOntologyTerms = createOntologyTerms(line, factorValueOntologyTermIndex);

                experimentDesign.putFactor(runOrAssay, factorHeader, factorValue, factorValueOntologyTerms);
            }
        }

        return experimentDesign;
    }

    private OntologyTerm[] createOntologyTerms(String[] line, Integer ontologyTermIndex) {
        if (ontologyTermIndex == null || line[ontologyTermIndex].isEmpty()) {
            return new OntologyTerm[0];
        }

        ImmutableList.Builder<OntologyTerm> ontologyTermBuilder = new ImmutableList.Builder<>();
        String uriField = line[ontologyTermIndex];
        for (String uri : uriField.split(ONTOLOGY_TERM_DELIMITER)) {
            ontologyTermBuilder.add(OntologyTerm.createFromURI(uri));
        }
        List<OntologyTerm> ontologyTermList = ontologyTermBuilder.build();

        return ontologyTermList.toArray(new OntologyTerm[ontologyTermList.size()]);
    }

    protected Map<String, Integer> extractHeaderIndexes(String[] columnHeaders, Pattern columnHeaderPattern) {
        Map<String, Integer> map = new TreeMap<>();
        for (int i = 0; i < columnHeaders.length; i++) {
            String matchingHeaderContent = extractMatchingContent(columnHeaders[i], columnHeaderPattern);
            if (matchingHeaderContent != null) {
                map.put(matchingHeaderContent, i);
            }
        }
        return map;
    }

    static String extractMatchingContent(String string, Pattern pattern) {
        Matcher matcher = pattern.matcher(string);
        if (matcher.matches()) {
            return matcher.group(1);
        }
        return null;
    }

}

@@ -230,61 +230,8 @@
 st;%0A
-import org.springframework.context.annotation.Scope;%0A
 impo
@@ -272,24 +272,24 @@
 .TsvReader;%0A
+
 import uk.ac
@@ -430,16 +430,54 @@
 .Named;%0A
+import java.io.FileNotFoundException;%0A
 import j
@@ -643,28 +643,8 @@
 med%0A
-@Scope(%22prototype%22)%0A
 publ
@@ -677,17 +677,16 @@
 arser %7B%0A
-%0A
     priv
@@ -1436,17 +1436,16 @@
 %0A    %7D%0A%0A
-%0A
     publ
@@ -1497,16 +1497,45 @@
 cession)
+ throws FileNotFoundException
  %7B%0A%0A    
@@ -1691,20 +1691,20 @@
 new 
-IllegalState
+FileNotFound
 Exce

Throw FileNotFoundException if a resource file is missing instead of IllegalStateException