/*
 * Filename: Prix.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
 *
 */

package edu.columbia.print.ninja.prix;

import javax.swing.*;
import org.jini.rio.entry.WARDescriptor;
import org.jini.rio.web.EntryPoint;
import org.jini.rio.web.Gateway;
import edu.columbia.print.ninja.core.authenticate.*;
import edu.columbia.print.ninja.core.service.*;
import java.util.*;
import java.io.*;
import java.rmi.RemoteException;
import java.net.InetAddress;
import edu.columbia.print.ninja.prix.display.*;
import javax.print.attribute.standard.PrinterName;
import edu.columbia.print.ninja.prix.requester.Requester;
import net.jini.core.lookup.*;
import net.jini.lookup.*;
import net.jini.lookup.entry.*;
import net.jini.core.entry.*;
import net.jini.discovery.*;
import net.jini.core.event.*;
import net.jini.lease.*;
import net.jini.core.lease.*;
import com.sun.rio.services.watchsmith.*;
import com.sun.rio.resources.watcher.*;
import java.rmi.server.*;
import java.util.prefs.*;
import net.jini.lookup.ui.MainUI;
import net.jini.lookup.entry.UIDescriptor;
import com.sun.rio.resources.entry.*;
import com.sun.rio.resources.ui.*;
import com.sun.rio.resources.client.ServiceWatcher_Stub;
import java.net.URL;
import com.sun.rio.resources.jsb.*;
import org.jini.rio.jsb.*;

public class Prix extends com.sun.rio.services.cybernode.PersistentCybernodeImpl implements Station, net.jini.lease.LeaseListener {
    
    Ninja ninja;
    RequestQueue proxy;
    Class[] required = null;
    LeaseRenewalManager renewMgr;
    Gui gui;
    transient WatchsmithFinder watchFinder = null;
    transient ServiceBeanContextManager mgr = null;
    CounterWatch watch = null;
    long up = System.currentTimeMillis();
    
    public Prix(java.rmi.activation.ActivationID activationId, java.rmi.MarshalledObject data) throws Exception {
        super(activationId, data);
        if(getServiceID()!=null) activationId.activate(true);
    }
    
    public void initialize(org.jini.rio.jsb.ServiceBeanContext context, ServiceBeanAdapter.ExportDesc eDesc) throws Exception {
        super.initialize(context, eDesc);
//        if(eDesc!=null) System.out.println("port "+eDesc.getPort());
        setProperty("retainProperties", System.getProperty("prix.retainProperties"));
        JFrame frame = new JFrame();
        frame.setCursor(new java.awt.Cursor(java.awt.Cursor.CROSSHAIR_CURSOR));
        frame.setUndecorated(true);
        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        frame.setSize(screenSize);
        frame.setLocation(0,0);
        LoadPanel loadPanel = new LoadPanel();
        frame.getContentPane().add(loadPanel, java.awt.BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.show();
        loadPanel.progressBar.setValue(0);
        
        ninja = new Ninja();
        loadPanel.progressBar.setValue(10);
        
        debug(getServiceID());
        ninja.initialize(context);
        loadPanel.progressBar.setValue(20);
        
        //                ActivationDesc desc = new ActivationDesc(agi, "examples.activation.ActivatableImplementation",
        //                    location, data);
        //                proxy = (RequestQueue )Activatable.register();
        proxy = (RequestQueue )RemoteObject.toStub(ninja);
        loadPanel.progressBar.setValue(30);
        
        renewMgr = new LeaseRenewalManager();
        
        Preferences pref = Preferences.userNodeForPackage(getClass());
        net.jini.core.entry.Entry[] entries = null;
        loadPanel.progressBar.setValue(40);
        try {
            Gateway gateway = new Gateway();
            gateway.addEntryPoint(new EntryPoint("HTML", "selector.jsp", EntryPoint.JSP));
            String host = InetAddress.getLocalHost().getCanonicalHostName();
            WARDescriptor warConfig = new WARDescriptor(new URL(urlbase+"prix.war"), gateway, "prix");
            Address address = new Address();
            address.organization = getProperty("address.organization");
            //register myself with the lookup service
            entries = new net.jini.core.entry.Entry[] {
                address,
                new net.jini.lookup.entry.ServiceInfo("Print Station","Columbia",
                "Columbia","0.0","",""),
                new net.jini.lookup.entry.Name("Prix"),
                warConfig,
                UIDescriptorFactory.getUIDescriptor(
                MainUI.ROLE,
                new UIComponentFactory(
                new URL[] {new URL(urlbase+"prix-dl.jar")},
                Gui.class.getName()
                )
                )
            };
            applyAttributes(entries);
//            else serviceID = register(RemoteObject.toStub(this), entries);
        }
        catch(Exception e) {
            debug("creation "+e);
        }
        loadPanel.progressBar.setValue(50);
        
        java.util.Timer timer = new java.util.Timer();
        new Thread() {
            public void run() {
                for(;;) {
                    try {Thread.sleep(5*60*1000);}
                    catch(Exception e) {}
                    if(watchFinder!=null && watchFinder.isWatchAvailable()) {
                        String name = null;
                        try {name=java.net.InetAddress.getLocalHost().getHostName();}
                        catch(Exception e) {return;}
                        try {
                            com.sun.rio.resources.watcher.StopWatch tmp = watchFinder.getWatch(name+": "+"Uptime");
                            if(tmp==null) continue;
                            com.sun.rio.resources.watcher.WatchDataSource ds = tmp.getWatchDataSource();
                            watch = new CounterWatch(ds, name+": "+"Uptime");
                            if(watch!=null) watch.setCounter((System.currentTimeMillis()-up)/60000);
                        }
                        catch(Exception e) {}
                    }
                }
            }
        }.start();
        loadPanel.progressBar.setValue(60);
        
        loadPanel.progressBar.setValue(70);
        setAuthenticators();
        loadPanel.progressBar.setValue(80);
        ninja.registerServiceProvider(new edu.columbia.print.ninja.prix.service.NamePSLookup(new String[] {getProperty("outputPrinters")}));
        loadPanel.progressBar.setValue(90);
        
        gui = new Gui(
        new ServiceItem(getServiceID(), this, entries)
        );//, watchFinder);
        loadPanel.progressBar.setValue(100);
        
        frame.getContentPane().removeAll();
        frame.getContentPane().add(gui, java.awt.BorderLayout.CENTER);
//        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        gui.start();
        frame.validate();
        //		EventRegistration reg = ninja.addEventListener(null, new edu.columbia.print.ninja.core.event.PrintQueueEvent[0], (RemoteEventListener )gui, null, Lease.FOREVER);
        EventRegistration[] reg = gui.getEventRegistrations();
        for(int i=reg.length-1; i>=0; i--) {
            renewMgr.renewFor(reg[i].getLease(), Lease.FOREVER, Lease.ANY, this);
        }
        //		gui.setSequenceNumber(reg.getSequenceNumber());
        
        try {
            //get the remaining classes via properties files
            Requester rqr = new edu.columbia.print.ninja.prix.requester.SpoolIntercept(getProperty("spoolIntercept"));
            rqr.setInputDevice(getProperty("inputPrinter"));
            rqr.addPrintService(ninja);
            //this must be last
            StringTokenizer t = new StringTokenizer(getProperty("printService.attributes"));
            Vector req = new Vector();
            for(; t.hasMoreTokens();) req.add(Class.forName(t.nextToken()));
            java.io.File spool = new java.io.File(getProperty("spool.directory"));
            spool.mkdirs();
            rqr.produceRequest(
            spool,
            new javax.print.attribute.HashPrintRequestAttributeSet(),
            (Class[] )req.toArray(new Class[req.size()]),
            null
            );
            Thread.sleep(0L);
        }
        catch(Exception e) {
            System.out.println("prix.requester");
            e.printStackTrace();
        }
    }
    
    public void setAuthenticators() throws RemoteException,java.net.URISyntaxException,IOException {
        Authenticator[] auths = new Authenticator[8];
        for(int i = auths.length-1; i>=0; i--) {
            auths[i] =
                new edu.columbia.print.ninja.prix.authenticate.PcdAuth(
                    new java.net.URI(getProperty("pcd.uri")),
                    getProperty("pcd.uni.regexpLockout"),
                    Integer.parseInt(getProperty("pcd.password.maxLength")),
                    Integer.parseInt(getProperty("pcd.pageFactor")),
                    Integer.parseInt(getProperty("pcd.jobs.maxSheets"))
                );
        }
        ninja.setAuthenticators(auths);
    }

    public void destroy() {
        ninja.destroy();
        super.destroy();
    }
    
    public RequestQueue getQueue(boolean local) throws java.rmi.RemoteException {
        if(local) return (RequestQueue )ninja;
        return proxy;
    }
    
    public boolean isLocal(java.rmi.Remote remote) throws RemoteException {
        if(gui==null) return true;
        return (gui==remote);
    }
    
    static void debug(Object in) {com.millipad.util.Debug.print(Prix.class, in);}
    
    public void notify(net.jini.lease.LeaseRenewalEvent event) {
        System.out.println("can't renew lease "+event.getException()+" "+(event.getExpiration()-System.currentTimeMillis()));
        renewMgr.clear();
        try {
            long timeToReg = -System.currentTimeMillis();
            gui.registerListeners();
            timeToReg += System.currentTimeMillis();
            //took too long to register, something wrong
            if(timeToReg > 10*1000L) System.exit(-1);
            EventRegistration[] reg = gui.getEventRegistrations();
            for(int i=reg.length-1; i>=0; i--) {
                renewMgr.renewFor(reg[i].getLease(), Lease.FOREVER, Lease.ANY, this);
            }
            System.out.println("reregistered listeners");
        }
        catch(Exception e) {
            System.exit(-1);
        }
    }
    
    public WatchsmithFinder getWatchsmithFinder() throws java.rmi.RemoteException {
        return watchFinder;
    }
    
    public void setProperty(String prop, String value) throws RemoteException {
        Preferences pref = Preferences.userNodeForPackage(Prix.class);
        try {
            if("retainPreferences".equals(prop)) {
                if("true".equals(value)) pref.putBoolean(prop, true);
                else pref.clear();
                pref.flush();
                return;
            }
            if(!"true".equals(getProperty("retainPreferences"))) return;
            if("pcd.uri".equals(prop)) {
                pref.put(prop, value);
                pref.flush();
                setAuthenticators();
                return;
            }
            if("pcd.uni.regexpLockout".equals(prop)) {
                pref.put(prop, value);
                pref.flush();
                setAuthenticators();
                return;
            }
            if("pcd.password.maxLength".equals(prop)
            || "pcd.jobs.maxSheets".equals(prop)
            || "pcd.pageFactor".equals(prop)) {
                pref.putInt(prop, Integer.parseInt(value));
                pref.flush();
                setAuthenticators();
                return;
            }
            if("laf.refresh".equals(prop)) {
                pref.putLong(prop, Long.parseLong(value));
                pref.flush();
                gui.refreshLAF();
                return;
            }
            if("laf.main.page".equals(prop)
            || "laf.auth.page".equals(prop)
            || "laf.error.page".equals(prop)
            || "laf.status.page".equals(prop)
            || "laf.reboot.page".equals(prop)
            || "laf.base".equals(prop)) {
                pref.put(prop, value);
                pref.flush();
                gui.refreshLAF();
                return;
            }
            if("spool.directory".equals(prop)
            || "inputPrinter".equals(prop)
            || "outputPrinters".equals(prop)
            ) {
                pref.put(prop, value);
                pref.flush();
                return;
            }
            if("address.organization".equals(prop)
            ) {
                ninja.setProperty(prop, value);
                Address update = new Address();
                update.organization = value;
                joiner.getJoinManager().modifyAttributes(
                new Entry[] {new Address()},
                new Entry[] {update}
                );
                return;
            }
        }
        catch(Exception e) {
            throw new RemoteException("can't store request.failure.retries", e);
        }
    }
    
    public String[] getPropertyKeys() throws RemoteException {
        return new String[] {
            "retainPreferences",
            "pcd.uri","pcd.uni.regexpLockout","pcd.password.maxLength","pcd.pageFactor","pcd.jobs.maxSheets",
            "laf.refresh","laf.base","laf.main.page",
            "laf.auth.page","laf.error.page","laf.status.page","laf.reboot.page","spool.directory",
            "inputPrinter","outputPrinters","printService.attributes","address.organization"
        };
    }
    
    public String getProperty(String prop) throws RemoteException {
        Preferences pref = Preferences.userNodeForPackage(getClass());
        if("retainPreferences".equals(prop)) {
            return pref.getBoolean(prop, "true".equals(System.getProperty("prix."+prop)))+"";
        }
        if("pcd.uri".equals(prop)
        || "pcd.uni.regexpLockout".equals(prop)
        || "laf.base".equals(prop)
        || "laf.main.page".equals(prop)
        || "laf.auth.page".equals(prop)
        || "laf.error.page".equals(prop)
        || "laf.status.page".equals(prop)
        || "laf.reboot.page".equals(prop)
        || "spool.directory".equals(prop)
        || "inputPrinter".equals(prop)
        || "outputPrinters".equals(prop)
        || "spoolIntercept".equals(prop)
        || "printService.attributes".equals(prop)
        ) {
            return pref.get(prop, System.getProperty("prix."+prop));
        }
        if("pcd.password.maxLength".equals(prop)
        || "pcd.jobs.maxSheets".equals(prop)
        || "pcd.pageFactor".equals(prop)
        ) {
            return pref.getInt(prop, Integer.parseInt(System.getProperty("prix."+prop)))+"";
        }
        if("laf.refresh".equals(prop)
        ) {
            return pref.getLong(prop, Long.parseLong(System.getProperty("prix."+prop)))+"";
        }
        if("address.organization".equals(prop)
        ) {
            return ninja.getProperty(prop);
        }
        debug("uknown getProperty request: "+prop);
        return null;
    }
    
    public void restart() throws RemoteException {
    }
    
}
