/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.csu.sdmx.validation.model;

import cz.csu.sdmx.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 
 * @author themi
 */
public class SeriesSet {
    private String values;
    private LogicCondition condition;
    private Map<Param, Series> seriesMap;
    private List<Expression> expressions;
    
    public SeriesSet(String valuesString, LogicCondition condition){
        this.values = valuesString;
        this.condition = condition;
        seriesMap = new HashMap<>();
    }
    
    public boolean matches(String valuesString){
        return valuesString.equals(values);
    }
    
    public void addSeries(Series series){
        List<Param> params = condition.getParams();
        
        for(Param param : params){
            String attrVal = series.getAttrValue(param.getName());
            if(attrVal != null && attrVal.equals(param.getValue()))
                this.seriesMap.put(param, series);
        }
    }

    public ValidationError createNoMatchError(String filename, int conditionID) {
        List<String> lineNumbers = new ArrayList<>();
        for(Param key : seriesMap.keySet())
            lineNumbers.add(seriesMap.get(key).getLineNumber());
        
        ValidationError error = new ValidationError();
        error.setFilename(filename);
        error.setErrorCode("MissingSeries");
        error.setType(ValidationError.ERROR);
        error.setText("Did not found matching series for condition with id["+conditionID+"].");
        error.setLineNumber(Utils.join(lineNumbers, ","));
        
        return error;
    }

    public boolean foundMatches() {
        return condition.getParams().size() == seriesMap.size();
    }

    public List<ValidationError> parseExpressions() {
        List<ValidationError> validationErrors = new ArrayList<>();
        
        Map<Param, Map<String,Obs>> obsMapsList = new HashMap<>();
        //create map of params and observation imprints
        for(Map.Entry<Param, Series> entry : seriesMap.entrySet()){
            Series series = entry.getValue();
            List<Obs> obsList = series.getObsValues();
            Map<String,Obs> obsImprintMap = createFingerprintMap(obsList, condition.getObsValAttrName());
            obsMapsList.put(entry.getKey(), obsImprintMap);
        }
        
        expressions = new ArrayList<>();
        Set<String> obsImprints = obsMapsList.entrySet().iterator().next().getValue().keySet();
        //for all unique imprints
        for(String obsImprint : obsImprints){
            Expression expression = new Expression(condition);
            //find observation with the same imprint within each series
            for(Map.Entry<Param, Map<String,Obs>> e : obsMapsList.entrySet()){
                Obs obs = e.getValue().get(obsImprint);
                //if not found observation with such imprint, create error
                if(obs == null || e.getKey() == null){
                    ValidationError error = new ValidationError();
                    error.setFilename("data");
                    error.setErrorCode("MissingObs");
                    error.setType(ValidationError.ERROR);
                    error.setText("Did not found matching obs for condition with id["+condition.getId()+"].");
                    error.setLineNumber("Unknown");
                    
                    validationErrors.add(error);
                }else{
                    //if imprint found, add observation to expression
                    expression.addObs(e.getKey(), obs);
                }
            }
            //if found all needed observations, add to expression list
            if(expression.foundObsValues())
                expressions.add(expression);
        }
        
        //return found errors
        return validationErrors;
    }
    
    private Map<String, Obs> createFingerprintMap(List<Obs> obsList, String obsValAttrName){
        Map<String, Obs> map = new HashMap<>();
        //join all attributes and values of observation tag together 
        //except attribute containing observation value
        for(Obs obs : obsList){
            Map<String, String> attrs = obs.getAttrs();
            Set<String> keys = new HashSet<>();
            keys.addAll(attrs.keySet());
            keys.remove(obsValAttrName);
            StringBuilder builder = new StringBuilder();
            for(String key : keys){
                builder.append(key);
                builder.append(attrs.get(key));
            }
            map.put(builder.toString(), obs);
        }
      
        return map;
    }
    
    public List<Expression> getExpressions() {
        return expressions;
    }
    
    
}
