package query_processing.student;

import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import uk.ac.bbk.dcs.automed.qproc.GQPFormatter;
import uk.ac.bbk.dcs.automed.qproc.QProcException;
import uk.ac.bbk.dcs.automed.qproc.QProcLogger;
import uk.ac.bbk.dcs.automed.qproc.OldQueryProcessor;
import uk.ac.bbk.dcs.automed.qproc.QueryProcessorConfiguration;
import uk.ac.bbk.dcs.automed.qproc.QueryProcessor;
import uk.ac.bbk.dcs.automed.qproc.iql.ASG;
import uk.ac.bbk.dcs.automed.qproc.iql.ParseException;
import uk.ac.bbk.dcs.automed.qproc.reformulate.LAVQueryReformulationException;
import uk.ac.ic.doc.automed.STR;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.wrappers.AutoMedWrapper;

/*
 * Problems
 * Query:
 *  - (s1sp-s1, BAV_Both, LAV_Upper, LAV_Both evaluation)
 *    "[{s}|{s,d}<-<<student,dcode>>;{d,dn}<-<<degree,dname>>;dn='BSc Mathematics']" // Sec3.3
 *    BAV_Both fails due to an unimplemented exception in class Union - FIXED
 *  - (sisp-s1, GAV_Both, Result is not a RANGE !!!) 
      "[{s,c,t}|{s,c,t}<-<<sit>>]"
 *
 * 
 * LAV reformulation problem - check - it may be correct.
 *  (s1sp-s1, LAV ALL) 
 *    "[{s,cw}|{{s,c,t},cw}<-<<sit,cwmark>>]"
    (s1sp-s1, LAV Lower) 
      "[{c,ti}|{c,ti}<-<<course,title>>]"
 *  (s1sp-s1, LAV LOWER)
 *    "[{s}|{s,d}<-<<student,dcode>>;{d,dn}<-<<degree,dname>>;dn='BSc Mathematics']" // Sec3.3
 */
/**
 * This application provides an example for the LAV query processing using
 * inverse rules. This particular class wraps three Student databases which are
 * integrated using a public schema. The databases are described in the paper
 * 
 * <pre>
 *  P.J. McBrien and A. Poulovassilis. P2P query reformulation over Both-as-View
 *  data transformation rules. In Proc of DBISP2P 2006.
 * </pre>
 * 
 * <p>
 * Running Instructions:
 * <p>
 * Following files should be run in the given order:
 * <ol>
 * <li>StudentDatabaseWrapping.java
 * <li>StudentDatabaseIntegration.java
 * <li>StudentDatabaseQuerying.java
 * </ol>
 * 
 * @author <a href="mailto:sandeep@dcs.bbk.ac.uk">Sandeep Mittal</a>
 */

public class StudentDatabaseQuerying {
    private static Logger _logger = QProcLogger.getDebugLogger();
    private static QueryProcessorConfiguration qpc = new QueryProcessorConfiguration();
    private static String[] queries = new String[] 
                                                 { 
        "[{c}|{c}<-<<course>>]"
//        "[{ti}|{c,ti}<-<<course,title>>;c>'500']"  // Sec3.1
//        "[{s,c,t}|{s,c,t}<-<<sit>>]" 
//        "[{s,c,cw}|{{s,c,t},cw}<-<<sit,cwmark>>;t>='1992-1']"  // Sec3.2
//        "[{s}|{s,d}<-<<student,dcode>>;{d,dn}<-<<degree,dname>>;dn='BSc Mathematics']" // Sec3.3
//        "[{s,cw}|{{s,c,t},cw}<-<<sit,cwmark>>]"
//        "[{c,ti}|{c,ti}<-<<course,title>>]"
//        "[{s,c,t}|{s,c,t}<-<<sit>>]"        
                                                 };

    public static void main(String[] args) throws Exception {
        
        System.out.println("Query Processor example for BAV/LAV/GAV: "
                + "Querying of Student Databases");
        //AutoMedWrapper.setDebugLevel(0);
        readArgs(args);
        Schema source = Schema.getSchema("s1sp");
        Schema[] targets = new Schema[] {Schema.getSchema("s1"),
                Schema.getSchema("s2"), Schema.getSchema("s3")
                };
        String idSemantics = "++";
        runGAV(source, targets, idSemantics);
        runBAV(source, targets, idSemantics);
        runLAV(source, targets, idSemantics);
        o("--------------");
        o("End of Program");
        o("--------------");
    }
     
    private static void runGAV(Schema source, Schema[] targets, String idSemantics) throws Exception {
        // GAV-RANGE_LOWER
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_GAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_LOWER);
        runQueries(queries, source, targets, idSemantics, qpc);
        //oldGAV(queries, source, getDataSourceTargets(targets), qpc);

        // GAV-RANGE_UPPER
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_GAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_UPPER);
        runQueries(queries, source, targets, idSemantics, qpc);
        //oldGAV(queries, source, getDataSourceTargets(targets), qpc);

        // GAV-RANGE_BOTH
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_GAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_BOTH);
        runQueries(queries, source, targets, idSemantics, qpc);
        //oldGAV(queries, source, getDataSourceTargets(targets), qpc);
    }

    private static void runBAV(Schema source, Schema[] targets, String idSemantics) throws Exception {

        // BAV-RANGE_LOWER
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_BAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_LOWER);
        runQueries(queries, source, targets, idSemantics, qpc);

        // BAV-RANGE_UPPER
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_BAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_UPPER);
        runQueries(queries, source, targets, idSemantics, qpc);
        
        // BAV-RANGE_BOTH
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_BAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_BOTH);
        runQueries(queries, source, targets, idSemantics, qpc);
        
    }
        
    private static void runLAV(Schema source, Schema[] targets, String idSemantics) throws Exception {
        // LAV-RANGE_LOWER
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_LAV_INV_RULES);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_LOWER);
        runQueries(queries, source, targets, idSemantics, qpc);

        // LAV-RANGE_UPPER
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_LAV_INV_RULES);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_UPPER);
        runQueries(queries, source, targets, idSemantics, qpc);
        
        // LAV-RANGE_BOTH
        qpc.setOptimisationState(true);
        qpc.setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_LAV_INV_RULES);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_BOTH);
        runQueries(queries, source, targets, idSemantics, qpc);
    }

    private static void runQueries(String[] queries, Schema source, Schema[] targets, String idSemantics, QueryProcessorConfiguration qpc) throws Exception {
        QueryProcessor qp = new QueryProcessor();
        for (int i=0; i<queries.length; i++) {
            o("***New Query: " + queries[i]);
            o(qpc.getReformulationSemanticsString() + " " + qpc.getRangeSemanticsString());
            try {
            ASG query = new ASG(queries[i]);
            qp.process(query, source, targets, idSemantics, qpc);
            o("Result: " + query);
            } catch (QProcException e) {
                if ((e.getCause() instanceof LAVQueryReformulationException) &&
                        (qpc.getReformulationSemantics() == QueryProcessorConfiguration.REFORMULATION_LAV_INV_RULES)){
                    o("###LAV Issue:");
                } else {
                    o("###Some Other Issue:"); 
                }
                qpc.printConfiguration();
                e.printStackTrace();
            }
        }
    }

    private static void oldGAV (String[] queries, Schema source, Schema[] targets, QueryProcessorConfiguration qpc) throws Exception{
        o("**********************************************");
        o("Old GAV processing : " + qpc.getRangeSemanticsString());
        OldQueryProcessor oqp = new OldQueryProcessor(source, targets, qpc);
        for (int i=0; i<queries.length; i++) {
            o("Query: " + queries[i]);
            ASG query = new ASG(queries[i]);
            oqp.process(query, qpc);
            o("Result: " + query);
        }
    }

    static void printHelp() {
        System.out.println("Usage: UniversityDatabaseQuerying [ options ]\n"
                + "  -debug <0-10>         set debug level\n");
    }

    static void o(Object s) {
        //System.out.println("***StudentDatabaseQuerying: " + s.toString());
        _logger.finest("***StudentDatabaseQuerying: " + s.toString());
    }
    
    public static void readArgs(String[] args) {
        int i = 0;
        try {
            while (i < args.length) {
                if (args[i].charAt(0) != '-') {
                    System.out.println("Error at " + args[i] + "\n");
                    printHelp();
                    System.exit(10);
                } else if (args[i].startsWith("-debug"))
                    STR.setDebugLevel(Integer.parseInt(args[++i]));
                else if (args[i].startsWith("-help"))
                    printHelp();

                ++i;
            }
            if (STR.atDebugLevel(5))
                System.out.println("Finished reading arguments, "
                        + "about to read database");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("You didn't supply an argument to the "
                    + args[i - 1] + " option!");
            printHelp();
            System.exit(10);
        }
    }
    private static Schema[] getDataSourceTargets(Schema[] automedTargets) {
        Schema[] datasourceTargets = new Schema[automedTargets.length];
        for (int i=0; i<automedTargets.length; i++) {
            datasourceTargets[i] = automedTargets[i].getSubnetSchema();
        }
        return datasourceTargets;
    }
    
}