package uk.ac.ebi.quickgo.annotation.model;

import uk.ac.ebi.quickgo.annotation.common.document.AnnotationFields;
import uk.ac.ebi.quickgo.rest.search.request.FilterRequest;

import java.util.*;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;

import static uk.ac.ebi.quickgo.annotation.common.document.AnnotationFields.*;

/**
 * A data structure for the annotation filtering parameters passed in from the client.
 *
 * Once the comma separated values have been set, then turn then into an object (SimpleFilter) that
 * encapsulates the list and solr field name to use for that argument.
 *
 * @author Tony Wardell
 * Date: 25/04/2016
 * Time: 11:23
 * Created with IntelliJ IDEA.
 */
public class AnnotationRequest {
    public static final int DEFAULT_ENTRIES_PER_PAGE = 25;
    public static final int MAX_ENTRIES_PER_PAGE = 100;

    private static final int DEFAULT_PAGE_NUMBER = 1;
    private static final String COMMA = ",";

    @Min(0) @Max(MAX_ENTRIES_PER_PAGE)
    private int limit = DEFAULT_ENTRIES_PER_PAGE;

    @Min(1)
    private int page = DEFAULT_PAGE_NUMBER;

    private final Map<String, String> filterMap = new HashMap<>();

    /**
     *  E.g. ASPGD,Agbase,..
     *  In the format assignedBy=ASPGD,Agbase
     */
    public void setAssignedBy(String assignedBy) {
        if (assignedBy != null) {
            filterMap.put(ASSIGNED_BY, assignedBy);
        }
    }

    @Pattern(regexp = "^[A-Za-z][A-Za-z\\-_]+(,[A-Za-z][A-Za-z\\-_]+)*",
            message = "At least one 'Assigned By' value is invalid: ${validatedValue}")
    public String getAssignedBy() {
        return filterMap.get(ASSIGNED_BY);
    }

    /**
     * E.g. DOI, DOI:10.1002/adsc.201200590, GO_REF, PMID, PMID:12882977, Reactome, Reactome:R-RNO-912619,
     * GO_REF:0000037 etc
     * @param reference
     * @return
     */
    public void setReference(String reference){
        filterMap.put(AnnotationFields.REFERENCE_SEARCH, reference);
    }

    //todo create validation pattern @Pattern(regexp = "")
    public String getReference(){
        return filterMap.get(AnnotationFields.REFERENCE_SEARCH);
    }

    //TODO:change the way the field is referenced
    private static final String ASPECT_FIELD = "aspect";

    public void setAspect(String aspect) {
        if (aspect != null) {
            filterMap.put(ASPECT_FIELD, aspect.toLowerCase());
        }
    }

    @Pattern(regexp = "biological_process|molecular_function|cellular_component", flags = Pattern.Flag.CASE_INSENSITIVE,
            message = "At least one 'Aspect' value is invalid: ${validatedValue}")
    public String getAspect() {
        return filterMap.get(ASPECT_FIELD);
    }

    /**
     * The older evidence codes
     * E.g. IEA, IBA, IBD etc. See <a href="http://geneontology.org/page/guide-go-evidence-codes">Guide QuickGO
     * evidence codes</a>
     * @param evidence the evidence code
     */
    public void setGoEvidence(String evidence) {
        filterMap.put(GO_EVIDENCE, evidence);
    }

    /**
     * NOT, enables etc
     * @param qualifier
     */
    public void setQualifier(String qualifier){
        filterMap.put(QUALIFIER, qualifier);
    }

    public String getQualifter(){
        return filterMap.get(QUALIFIER);
    }

    /**
     * A list of with/from values, separated by commas
     * In the format withFrom=PomBase:SPBP23A10.14c,RGD:621207 etc
     * Users can supply just the id (e.g. PomBase) or id SPBP23A10.14c
     * @param withFrom comma separated with/from values
     */
    public void setWithFrom(String withFrom){
        filterMap.put(WITH_FROM_SEARCH, withFrom);
    }

    /**
     * Return a list of with/from values, separated by commas
     * @return String containing comma separated list of with/From values.
     */
    public  String getWithFrom(){
        return filterMap.get(WITH_FROM_SEARCH);
    }

    @Pattern(regexp = "^[A-Za-z]{2,3}(,[A-Za-z]{2,3})*",
            message = "At least one 'GO Evidence' value is invalid: ${validatedValue}")
    public String getGoEvidence() {
        return filterMap.get(GO_EVIDENCE);
    }

    public void setTaxon(String taxId) {
        filterMap.put(TAXON_ID, taxId);
    }

    @Pattern(regexp = "[0-9]+(,[0-9]+)*",
            message = "At least one 'Taxonomic identifier' value is invalid: ${validatedValue}")
    public String getTaxon() {
        return filterMap.get(AnnotationFields.TAXON_ID);
    }

    void setGeneProductType(String geneProductType){
        filterMap.put(DB_OBJECT_TYPE, geneProductType);
    }

    @Pattern(regexp = "((?i)(complex|rna|protein),?){1,3}",
            message = "At least one 'Gene Product Type' value is invalid: ${validatedValue}")
    String getGeneProductType(){
        return filterMap.get(DB_OBJECT_TYPE);
    }


    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }


    public List<FilterRequest> createRequestFilters() {
        List<FilterRequest> filterRequests = new ArrayList<>();

        createSimpleFilter(ASPECT_FIELD).ifPresent(filterRequests::add);
        createSimpleFilter(ASSIGNED_BY).ifPresent(filterRequests::add);
        createSimpleFilter(TAXON_ID).ifPresent(filterRequests::add);
        createSimpleFilter(GO_EVIDENCE).ifPresent(filterRequests::add);
        createSimpleFilter(REFERENCE_SEARCH).ifPresent(filterRequests::add);
        createSimpleFilter(QUALIFIER).ifPresent(filterRequests::add);
        createSimpleFilter(WITH_FROM_SEARCH).ifPresent(filterRequests::add);


        return filterRequests;
    }

    private Optional<FilterRequest> createSimpleFilter(String key) {
        Optional<FilterRequest> request;
        if (filterMap.containsKey(key)) {
            FilterRequest.Builder requestBuilder = FilterRequest.newBuilder();
            requestBuilder.addProperty(key, filterMap.get(key).split(COMMA));
            request = Optional.of(requestBuilder.build());
        } else {
            request = Optional.empty();
        }

        return request;
    }
}

@@ -4483,16 +4483,23 @@
  %7D%0A%0A    
+public 
 void set
@@ -4499,26 +4499,17 @@
 oid setG
-eneProduct
+p
 Type(Str
@@ -4616,17 +4616,17 @@
 gexp = %22
-(
+%5E
 (?i)(com
@@ -4646,15 +4646,40 @@
 ein)
-,?)%7B1,3
+(,(?i)(complex%7Crna%7Cprotein))%7B0,2
 %7D%22,%0A
@@ -4776,16 +4776,23 @@
 %7D%22)%0A    
+public 
 String g
@@ -4794,26 +4794,17 @@
 ing getG
-eneProduct
+p
 Type()%7B%0A
@@ -5743,17 +5743,90 @@
 ::add);%0A
-%0A
+        createSimpleFilter(DB_OBJECT_TYPE).ifPresent(filterRequests::add);
 %0A       

Change name of Gene Product Type setter and getter. And add the gene product type to the filter creation.