/*
 * Filename: RemotePrintServiceAdapter.java
 *
 * Author: Joe Wang
 *
 * Copyright (c) 2003-2005 
 * The Trustees of Columbia University in the City of New York
 * Columbia University Information Technology
 *
 * This file is part of NINJa.
 *
 * NINJa is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * NINJa is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NINJa; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Created on May 14, 2002, 10:57 AM
 */

package edu.columbia.print.ninja.core.service;

import net.jini.print.data.Doc;
import net.jini.core.event.*;
import java.util.*;
import javax.print.DocPrintJob;
import edu.columbia.print.ninja.core.service.RemotePrintServiceAdapter.Job;
import net.jini.print.job.*;
import javax.print.DocFlavor;
import javax.print.attribute.*;
import java.rmi.*;
import java.io.Serializable;
import net.jini.print.service.*;
import javax.print.PrintService;


/**
 *
 * @author  <a href="mailto:jw168@columbia.edu">Joe Wang</a>
 */
public class RemotePrintServiceAdapter implements RemotePrintService, Serializable, javax.print.event.PrintServiceAttributeListener {
    
    PrintService service;
    Map listeners;
    long seqNo = 0L;
    transient RemoteNotifier notifier;
    
    /** Creates a new instance of RemotePrintService */
    public RemotePrintServiceAdapter(PrintService service) throws RemoteException {
        this.service=service;
        listeners = new HashMap();
        java.rmi.server.UnicastRemoteObject.exportObject((java.rmi.Remote )this);
        service.addPrintServiceAttributeListener(this);
        notifier = new RemoteNotifier(10);
    }
    
    public String toString() {return service.getName();}
    
    public EventRegistration addEventListener(Class[] theCategories, javax.print.attribute.PrintServiceAttribute[] theValues, RemoteEventListener theListener, MarshalledObject theHandbackObject, long theRequestedLeaseDuration) throws RemoteException {
        listeners.put(theListener, null);
        return null;
    }
    
    public DocPrintRequest createDocPrintRequest() throws PrintServiceException, RemoteException {
        return new Request(service.createPrintJob(), this);
    }
    
    public DocPrintRequest createDocPrintRequest(Doc theDoc, PrintRequestAttributeSet theAttributes) throws PrintServiceException, RemoteException {
        return null;
    }
    
    public javax.print.attribute.PrintServiceAttributeSet getAttributes() throws RemoteException {
        javax.print.attribute.PrintServiceAttributeSet set = service.getAttributes();
        Attribute[] attr = set.toArray();
        for(int i=attr.length-1; i>=0; i--) System.out.println("ps attr "+attr[i]);
        return service.getAttributes();
    }
    
    public Attribute getDefaultAttributeValue(Class theCategory, Settings theSettings) throws RemoteException {
        return (Attribute )service.getDefaultAttributeValue(theCategory);
    }
    
    public javax.print.attribute.AttributeSet getDefaultAttributeValues(Settings theSettings) throws RemoteException {
        return getAttributes();
    }
    
    public Class[] getSupportedAttributeCategories(Settings theSettings) throws RemoteException {
        return service.getSupportedAttributeCategories();
    }
    
    public Object getSupportedAttributeValues(Class theCategory, Settings theSettings) throws RemoteException {
        return service.getSupportedAttributeValues(theCategory, theSettings.flavor, theSettings.attributes);
    }
    
    public DocFlavor[] getSupportedDocFlavors(Settings theSettings) throws RemoteException {
        return service.getSupportedDocFlavors();
    }
    
    public Settings getUnsupportedSettings(Settings theSettings) throws RemoteException {
        return null;
    }
    
    public boolean isAttributeCategorySupported(Class theCategory, Settings theSettings) throws RemoteException {
        return service.isAttributeCategorySupported(theCategory);
    }
    
    public boolean isAttributeValueSupported(Attribute theValue, Settings theSettings) throws RemoteException {
        return service.isAttributeValueSupported(theValue, theSettings.flavor, theSettings.attributes);
    }
    
    public boolean isDocFlavorSupported(DocFlavor theFlavor, Settings theSettings) throws RemoteException {
        return service.isDocFlavorSupported(theFlavor);
    }
    
    public void attributeUpdate(javax.print.event.PrintServiceAttributeEvent psae) {
        Attribute[] attr = psae.getAttributes().toArray();
        for(int i=attr.length-1; i>=0; i--) System.out.println("psu attr "+attr[i]);
        try {
            final PrintServiceEvent event = new PrintServiceEvent(this, 0L, 0L, null, psae.getAttributes());
            for(Iterator i=listeners.keySet().iterator(); i.hasNext();) {
                RemoteEventListener listener = (RemoteEventListener )i.next();
                notifier.notify(listener, event);
            }
        }
        catch(java.io.IOException e) {
            e.printStackTrace();
            return;
        }
    }
    
    static class Request implements DocPrintRequest, Serializable {
        RemotePrintService service;
        DocPrintJob parent;
        Doc doc;
        PrintRequestAttributeSet attr;
        Map listeners;
        transient static Timer timer = new Timer(true);
        
        Request(DocPrintJob parent, RemotePrintService service) {
            this.parent = parent;
            this.service = service;
            listeners = new HashMap();
        }
        
        public net.jini.print.service.PrintService getPrintService() {
            return service;
        }
        
        public void print() throws PrintRequestException, RemoteException {
            try {parent.print(doc, attr);}
            catch(javax.print.PrintException e) {throw new RemoteException("can't print", e);}
        }
        
        //async call
        public PrintJobAndLease print(long theRequestedLeaseDuration) throws PrintRequestException, RemoteException {
            Job job = new Job(parent, service, doc, attr);
            timer.schedule(job, 0L);
            return new PrintJobAndLease(job, null);
        }
        
        public void setAttributes(PrintRequestAttributeSet theAttributes) {
            this.attr = theAttributes;
        }
        
        public PrintRequestAttributeSet getAttributes() {
            return attr;
        }
        
        public void setDoc(Doc theDoc) {
            this.doc = theDoc;
        }
        
        public EventRegistration addEventListener(boolean reportPrintDataSuccess, boolean reportPrintDataFailure, Class[] theCategories, PrintJobAttribute[] theValues, boolean reportPrintJobFinished, RemoteEventListener theListener, MarshalledObject theHandbackObject, long theRequestedLeaseDuration) throws RemoteException {
            listeners.put(theListener, null);
            return null;
        }
        
    }
    
    static class Job extends TimerTask implements net.jini.print.job.PrintJob, Serializable, javax.print.event.PrintJobAttributeListener {
        transient DocPrintJob parent;
        transient Map listeners;
        RemotePrintService service;
        Doc doc;
        PrintRequestAttributeSet attr;
        boolean complete;
        transient RemoteNotifier notifier;
        Job(DocPrintJob parent, RemotePrintService service, Doc doc, PrintRequestAttributeSet attr) {
            this.parent = parent;
            this.service = service;
            this.doc = doc;
            this.attr = attr;
            listeners = new HashMap();
            parent.addPrintJobAttributeListener(this, null);
            notifier = new RemoteNotifier(2);
        }
        
        public EventRegistration addEventListener(boolean reportPrintDataSuccess, boolean reportPrintDataFailure, Class[] theCategories, PrintJobAttribute[] theValues, boolean reportPrintJobFinished, RemoteEventListener theListener, MarshalledObject theHandbackObject, long theRequestedLeaseDuration) throws RemoteException {
            listeners.put(theListener, null);
            return null;
        }
        
        public javax.print.attribute.PrintJobAttributeSet getAttributes() throws RemoteException {
            javax.print.attribute.PrintJobAttributeSet set = parent.getAttributes();
//            Attribute[] attr = set.toArray();
//            for(int i=attr.length-1; i>=0; i--) System.out.println("pj attr "+attr[i]);
            return set;
        }
        
        public net.jini.print.service.PrintService getPrintService() throws RemoteException {
            return service;
        }
        
        private static final void debug(Object i) {com.millipad.util.Debug.print(RemotePrintServiceAdapter.class, i);}
        
        public void attributeUpdate(javax.print.event.PrintJobAttributeEvent pjae) {
            try {
                final PrintJobEvent event=new PrintJobEvent(service, 0L, 0L, null, this, pjae.getAttributes());
                for(Iterator i=listeners.keySet().iterator(); i.hasNext();) {
                    RemoteEventListener l = (RemoteEventListener )i.next();
                    notifier.notify(l, event);
                }
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
        
        public void run() {
            //System.out.println("print");
            try {parent.print(doc, attr);}
            catch(Exception e) {e.printStackTrace();}
            System.out.println("rpsa printing done");
            complete = true;
            synchronized(this) {notifyAll();}
        }
        
    }
}
