package it.unibz.inf.vig_mappings_analyzer.core;

import it.unibz.inf.ontop.model.Function;
import it.unibz.inf.ontop.model.OBDAMappingAxiom;
import it.unibz.inf.ontop.model.OBDAModel;
import it.unibz.inf.ontop.model.OBDASQLQuery;
import it.unibz.inf.ontop.model.Term;
import it.unibz.inf.ontop.model.Variable;
import it.unibz.inf.ontop.parser.SQLQueryDeepParser;
import it.unibz.inf.ontop.sql.DBMetadata;
import it.unibz.inf.ontop.sql.QuotedID;
import it.unibz.inf.ontop.sql.RelationID;
import it.unibz.inf.ontop.sql.api.ParsedSQLQuery;

/*
 * #%L
 * tupleSchemasExtractor
 * %%
 * Copyright (C) 2014 Free University of Bozen-Bolzano
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import it.unibz.inf.vig_mappings_analyzer.datatypes.Argument;
import it.unibz.inf.vig_mappings_analyzer.datatypes.Field;
import it.unibz.inf.vig_mappings_analyzer.datatypes.FunctionTemplate;
import it.unibz.inf.vig_mappings_analyzer.obda.OBDAModelFactory;
import net.sf.jsqlparser.expression.Expression;
import java.io.BufferedReader;
import java.io.FileReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

public class JoinableColumnsFinder extends OntopConnection {

    private JoinableColumnsFinder( OBDAModel model, DBMetadata meta) {
	super(model, meta);
    }

    public static JoinableColumnsFinder makeInstance( OBDAModel model, DBMetadata meta ){
	return new JoinableColumnsFinder(model, meta);
    }

    public List<FunctionTemplate> findFunctionTemplates() throws Exception {

	List<FunctionTemplate> result = new ArrayList<FunctionTemplate>();

	// Y shall do something similar for each dataproperty, and object property
	for( URI uri : obdaModel.getMappings().keySet() ){
	    logger.info(uri);
	    for( OBDAMappingAxiom a : obdaModel.getMappings(uri) ){

		List<Function> targetQuery = a.getTargetQuery();

		for( int i = 0; i < targetQuery.size(); ++i ){

		    Function atom = targetQuery.get(i); 
		    OBDASQLQuery sourceQuery = (OBDASQLQuery) a.getSourceQuery();

		    // Construct the SQL query tree from the source query
		    String viewString = sourceQuery.toString();
		    logger.info(viewString);
		    ParsedSQLQuery queryParsed = SQLQueryDeepParser.parse(this.meta, viewString);
		    if( queryParsed.getTables().size() != 1 ){ /*++skipped;*/ continue; } // Skip joins for the moment

		    //					++unskipped;

		    // alias -> originalName
		    Map<RelationID, RelationID> tableMap = queryParsed.getTables();
		    RelationID table = new ArrayList<>(tableMap.values()).get(0);
		    String tableName = table.getTableName();

		    // Get the terms in the atom, and build the related FunctionTemplate classes
		    for( Term t : atom.getTerms() ){
			
			if( t instanceof Function ){
			    Function f = (Function) t;
			    FunctionTemplate fT = new FunctionTemplate(f);
			    boolean old = false;
			    for( FunctionTemplate fTOld : result ){
				if( fTOld.getTemplateString().equals(fT.getTemplateString()) ){
				    fT = fTOld;
				    old = true;
				    break;
				}
			    }

			    if( fT.isURI() ){ // Consider only Joins over URIs
				if( !t.toString().contains("{") ){
				    continue;
				}
				Map<QuotedID, Expression> aliases = queryParsed.getAliasMap();	

				// Retrieve list of variables
				List<Variable> varlist = new ArrayList<Variable>();

				for( Term term : f.getTerms() ){
				    if( term instanceof Variable ){
					Variable v = (Variable)term;
					varlist.add(v);
				    }
				}

				for( int cnt = 0; cnt < varlist.size(); ++cnt ){

				    Variable v = varlist.get(cnt);
				    Argument arg = null;
				    if( !old ){
					arg = new Argument();
				    }
				    else{
					arg = fT.getArgumentOfIndex(cnt);
				    }
				    String colName = v.toString();
				    if( aliases.containsValue(v.toString()) ){
					for( QuotedID originalName : aliases.keySet() ){
					    if( aliases.get(originalName).toString().equals(colName) ){
						colName = originalName.toString();
						break;
					    }
					}
				    }
				    Field field = new Field(tableName, colName);
				    arg.addFillingField(field);

				    if( !old ){ fT.addArgument(arg); }	
				}
				if( !old ){ result.add(fT); }
			    }
			}
		    }
		} // END--FOR		
	    }
	}	
	return result;
    }

    public static class TestJoinableColumnsFinder{

	private static final String OUTFILE="src/main/resources/JoinableColumnFinderOutTest.txt";

	@Test
	public void testNPD(){
	    try{
		OBDAModel model = OBDAModelFactory.getSingletonOBDAModel("src/main/resources/npd-v2-ql_a.obda");
		DBMetadata parser = OBDAModelFactory.makeDBMetadata(model);

		JoinableColumnsFinder a = new JoinableColumnsFinder(model, parser);
		List<FunctionTemplate> fTemplates = a.findFunctionTemplates();

		// Remove templates picking from a single place
		List<FunctionTemplate> output = new ArrayList<FunctionTemplate>();

		for( FunctionTemplate t : fTemplates ){
		    if( !t.toString().contains("{") )
			System.err.println("DEBUG!!");
		    if( t.getArity() > 0 ){
			output.add(t);
		    }
		}
		StringBuilder testString = new StringBuilder();
		try(BufferedReader in = new BufferedReader(
			new FileReader(OUTFILE))){
		    String s;
		    while ((s = in.readLine()) != null){
			testString.append(s);
			testString.append("\n");
		    }
		}
		org.junit.Assert.assertEquals(testString.toString(), output.toString()+"\n");
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
    };

    public static void main(String[] args){

	try {
	    OBDAModel model = OBDAModelFactory.getSingletonOBDAModel("src/main/resources/npd-v2-ql_a.obda");
	    DBMetadata meta = OBDAModelFactory.makeDBMetadata(model);

	    JoinableColumnsFinder a = new JoinableColumnsFinder(model, meta);
	    List<FunctionTemplate> fTemplates = a.findFunctionTemplates();

	    // Remove templates picking from a single place
	    List<FunctionTemplate> output = new ArrayList<FunctionTemplate>();

	    for( FunctionTemplate t : fTemplates ){
		if( t.getArity() > 0 ){
		    output.add(t);
		}
	    }

	    System.out.println(output);

	    //			Map<String, List<FunctionTemplate>> mPredStringToFunctTemplates = new HashMap<>();
	    //			
	    //			for( FunctionTemplate t : fTemplates ){
	    //				if( mPredStringToFunctTemplates.containsKey(t.getTemplateString()) ){
	    //					mPredStringToFunctTemplates.get(t.getTemplateString()).add(t);
	    //				}
	    //				else{
	    //					mPredStringToFunctTemplates.put(t.getTemplateString(), new LinkedList<FunctionTemplate>());
	    //					mPredStringToFunctTemplates.get(t.getTemplateString()).add(t);
	    //				}
	    //			}
	    //			// Remove templates picking from a single place
	    //			Map<String, List<FunctionTemplate>> mPredStringToMultipleFunctTemplates = new HashMap<>();
	    //			
	    //			for( String key : mPredStringToFunctTemplates.keySet() ){
	    //				if( mPredStringToFunctTemplates.get(key).size() > 1 ){
	    //					mPredStringToMultipleFunctTemplates.put(key, mPredStringToFunctTemplates.get(key));
	    //				}
	    //			}
	    //			System.out.println(mPredStringToMultipleFunctTemplates);
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }	
};

class PredStringToFunctTemplMap{
    String predicateString;
    Set<FunctionTemplate> functionTemplates;


}







