package query_processing.hospital;

import java.util.logging.Logger;

import uk.ac.bbk.dcs.automed.qproc.QProcException;
import uk.ac.bbk.dcs.automed.qproc.QProcLogger;
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.reformulate.LAVQueryReformulationException;
import uk.ac.ic.doc.automed.STR;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * Sandeep(Dec2006): I am using this example to research update functionality in
 * AutoMed. So it is likely to change frequently over next few months.
 * 
 * This application provides an example for the BAV query processing in AutoMed. 
 * This particular class wraps 2 hospital databases which are
 * integrated using a public schema. The databases are described in the paper
 * <pre>
 *      [MP03] Peter McBrien and Alexandra Poulovassilis. Defining peer­to­peer 
 *      data integration using both as view rules. In DBISP2P, pages 91--107, 2003. 
 * </pre>
 * 
 * The exact definition of the schemas of the three databases is given in the
 * file HospitalSchema.txt in SQL92 format.
 * <p>
 * Running Instructions:
 * <p>
 * Following files should be run in the given order:
 * <ol>
 * <li>HospitalDBWrapping.java
 * <li>HospitalDBIntegration.java
 * <li>HospitalDBQuerying.java
 * </ol>
 * 
 * @author <a href="mailto:sandeep@dcs.bbk.ac.uk">Sandeep Mittal</a>
 */

public class HospitalDBQuerying {
    private static Logger _logger = QProcLogger.getDebugLogger();

    private static QueryProcessorConfiguration qpc = new QueryProcessorConfiguration();

    // allPatients(ni, name, sex, gp) pk ni
    // allTreatments(ni, date, description) pk (ni, date)

    // ***
    // Query 1: Debug LAV inverse rules for for BAV query reformulation
    
    private static String[] queries = new String[] { 
//        "[{ni}|{ni}<-<<allPatients>>]"
        "[{ni}|{ni,ni}<-<<allPatients,ni>>]"
//        "[{ni,n}|{ni,n}<-<<allPatients,name>>]"
//        ,"[{ni,s}|{ni,s}<-<<allPatients,sex>>]"
//        ,"[{ni,g}|{ni,g}<-<<allPatients,gp>>]"
//        "[{ni,n,s,g}|{ni,n}<-<<allPatients,name>>;{ni,s}<-<<allPatients,sex>>;{ni,g}<-<<allPatients,gp>>]"
//        "[{ni,dt}|{ni,dt}<-<<allTreatments>>]"
//        "[{ni,dt}|{{ni,dt},ni}<-<<allTreatments,ni>>]"
//        "[{ni,dt}|{{ni,dt},dt}<-<<allTreatments,date>>]"
//        "[{ni,dt,ds}|{{ni,dt},ds}<-<<allTreatments,description>>]"
//        "[{ni,n,dt,ds}|{ni,n}<-<<allPatients,name>>;{{ni,dt},ds}<-<<allTreatments,description>>]"
    };

//    private static String[] queries = new String[] { 
//      "[{ni}|{ni}<-<<patient>>]"
////      "[{ni}|{ni,ni}<-<<allPatients,ni>>]"
////      "[{ni,n}|{ni,n}<-<<allPatients,name>>]"
////      ,"[{ni,s}|{ni,s}<-<<allPatients,sex>>]"
////      ,"[{ni,g}|{ni,g}<-<<allPatients,gp>>]"
////      "[{ni,n,s,g}|{ni,n}<-<<allPatients,name>>;{ni,s}<-<<allPatients,sex>>;{ni,g}<-<<allPatients,gp>>]"
////      "[{ni,dt}|{ni,dt}<-<<allTreatments>>]"
////      "[{ni,dt}|{{ni,dt},ni}<-<<allTreatments,ni>>]"
////      "[{ni,dt}|{{ni,dt},dt}<-<<allTreatments,date>>]"
////      "[{ni,dt,ds}|{{ni,dt},ds}<-<<allTreatments,description>>]"
////      "[{ni,n,dt,ds}|{ni,n}<-<<allPatients,name>>;{{ni,dt},ds}<-<<allTreatments,description>>]"
//  };

    public static void main(String[] args) throws Exception {

        System.out.println("Query Processor example for BAV/LAV/GAV: "
                + "Querying of Hospital Databases");
        // AutoMedWrapper.setDebugLevel(0);
        readArgs(args);
        Schema source = Schema.getSchema("pubh1");
        Schema[] targets = new Schema[] {
                Schema.getSchema("hs1")
                //Schema.getSchema("hs2")
                };
        String idSemantics = "++";
        runGAV(source, targets, idSemantics);
//        runLAV(source, targets, idSemantics);
//        runBAV(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);

        // GAV-RANGE_BOTH
        qpc.setOptimisationState(true);
        qpc
                .setReformulationSemantics(QueryProcessorConfiguration.REFORMULATION_GAV);
        qpc.setRangeSemantics(QueryProcessorConfiguration.RANGE_BOTH);
        runQueries(queries, source, targets, idSemantics, 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();
            }
        }
    }

    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("***HospitalDatabaseQuerying: " + 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);
        }
    }

}
