/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is Protege-2000.
 *
 * The Initial Developer of the Original Code is Stanford University. Portions
 * created by Stanford University are Copyright (C) 2007.  All Rights Reserved.
 *
 * Protege was developed by Stanford Medical Informatics
 * (http://www.smi.stanford.edu) at the Stanford University School of Medicine
 * with support from the National Library of Medicine, the National Science
 * Foundation, and the Defense Advanced Research Projects Agency.  Current
 * information about Protege can be obtained at http://protege.stanford.edu.
 *
 */

package edu.stanford.smi.protege.model.framestore;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Set;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.stanford.smi.protege.model.Frame;
import edu.stanford.smi.protege.model.query.Query;
import edu.stanford.smi.protege.model.query.QueryCallback;
import edu.stanford.smi.protege.model.query.QueryCallbackClone;
import edu.stanford.smi.protege.util.Log;

public class JournalingFrameStoreHandler extends AbstractFrameStoreInvocationHandler {
    public final static String journaller_name = "protege.journal";
    private Logger journaler;
    private Handler handler;
    private boolean recordQueries = false;

    public void start(URI journalURI) {
        try {
            stop();
            handler = new java.util.logging.FileHandler(new File(journalURI).getPath(), true);
            journaler = Logger.getLogger(journaller_name);
            journaler.setUseParentHandlers(false);
            journaler.addHandler(handler);
            journaler.setLevel(Level.ALL);
            handler.setFormatter(new JournalFormater());
            handler.setLevel(Level.ALL);
        } catch (IOException e) {
            Log.getLogger().throwing("JournalingFrameStoreHandler", "start", e);
        }
    }

    public void stop() {
        if (handler != null) {
            journaler.removeHandler(handler);
            handler.flush();
            handler.close();
            handler = null;
        }
        journaler = null;
    }

    public void handleClose() {
        stop();
    }

    public Object handleInvoke(Method method, Object[] args) {
        Object result = invoke(method, args);
        if (doRecord(method)) {
            record(method, args, result);
        }
        return result;
    }
    
    public void executeQuery(Query q, QueryCallback qc) {
      QueryCallback myCallback = qc;
      if (recordQueries) {
        myCallback = new QueryCallbackClone(qc) {
          public void provideQueryResults(Set<Frame> results) {
            journaler.info("Query Callback returning results");
            super.provideQueryResults(results);
          }
        };
      }
      getDelegate().executeQuery(q, myCallback);
      if (recordQueries) {
          journaler.info("Executing query " + q + " (callback may log entry before this log)");
      }
    }

    protected void record(Method m, Object[] args, Object result) {
        int length = (args == null) ? 2 : args.length + 2;
        Object[] params = new Object[length];
        params[0] = m;
        if (args != null) {
            System.arraycopy(args, 0, params, 1, args.length);
        }
        params[length - 1] = result;
        journaler.log(Level.INFO, "entry", params);
    }

    protected boolean doRecord(Method method) {
        return journaler != null && (recordQueries || isModification(method));
    }
}