/* Copyright (c) 1997-2004
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program 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, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program 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.

   $Project: polymake $$Id: Launcher.java,v 1.2 2004/06/22 16:22:39 gawrilow Exp $
*/

package de.tuberlin.polymake.common;

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
import java.lang.reflect.*;

import jv.geom.PgPolygonSet;

import de.tuberlin.polymake.common.PolymakeJvxParser;
import de.tuberlin.polymake.common.SimpleGeometryParser;

/**
 * Launcher for an interactive springembedded graph viewer based on Javaview.
 * The calculations are made in a C++ client, which gets and returns the
 * data using sockets.
 * 
 * @author Thilo Schr&ouml;der
 */
public class Launcher{
    protected final static String USAGE = "usage: java de.tuberlin.polymake.common.Launcher ControlClass [-client_port <port>] ControlClass [-client_port <port>] ... -ps_port <port>";

    /** charset used for communication */
    public final static String CHARSET = "ISO-8859-1";
    
    protected Vector controlClasses;
    protected Vector clientPorts;
    
    /**
     * Creates a new Launcher and tries to establish a connection
     * to a c++ client via a socket. Also opens a pipe for communication
     * with Javaview frames.
     * 
     * @param portnumber		portnumber of socket to c++ client
     * @throws IOException	if an error occurs when opening the communication channels
     */
    public Launcher(Vector classes, Vector ports,int polymakeserverPort) 
	throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException,Exception {
	if(classes.size() != ports.size()) {
	    System.err.println("classes.size != ports.size");
	    System.exit(0);
	}

	controlClasses = classes;
	clientPorts = ports;
	
	String jvLicenceFile = System.getProperty("jv.licence");
	if(jvLicenceFile != null) {
	    jv.object.PsConfig.setCodeBase(jvLicenceFile);
	}

	// connect to existing socket on localhost
	InetSocketAddress psa = new InetSocketAddress("localhost",polymakeserverPort);
	SocketChannel polymakeserverChannel = SocketChannel.open();
	polymakeserverChannel.connect(psa);
	polymakeserverChannel.configureBlocking(false);
	BufferedReader psReader = new BufferedReader(Channels.newReader(polymakeserverChannel,CHARSET));

	SelectorThread selectorThread = new SelectorThread(polymakeserverChannel,CHARSET);

	// connect to existing socket on localhost
	Iterator ci = controlClasses.iterator();
	Iterator pi = clientPorts.iterator();
	ClassLoader classLoader = ClassLoader.getSystemClassLoader();

	Class embeddedGeometriesClass = classLoader.loadClass("de.tuberlin.polymake.common.EmbeddedGeometries");
	Class bufferedReaderClass = classLoader.loadClass("java.io.BufferedReader");
	Class pipeSinkChannelClass = classLoader.loadClass("java.nio.channels.Pipe$SinkChannel");
	SocketChannel clientChannel = null;
	StaticGeometryControl staticGeometryControl = null;
	while(ci.hasNext()) {
	    Class controlClass = (Class)ci.next();
	    int clientPort = ((Integer)pi.next()).intValue();
	    // System.out.println("class =" + controlClass.getName() + ", port =" + clientPort);
	    if(clientPort != -1) {
		Constructor constructor = controlClass.getConstructor(new Class[] {embeddedGeometriesClass,
										   bufferedReaderClass,
										   pipeSinkChannelClass});
		Pipe controlPipe = Pipe.open();
		controlPipe.source().configureBlocking(false);
		InetSocketAddress isa = new InetSocketAddress("localhost",clientPort);
		clientChannel = SocketChannel.open();
		clientChannel.connect(isa);
		clientChannel.configureBlocking(false);
		PolymakeControl pControl = (PolymakeControl)constructor.newInstance(new Object[] {PolymakeJvxParser.parse(psReader),
										 new BufferedReader(Channels.newReader(clientChannel,CHARSET)),
										 controlPipe.sink()});
		selectorThread.registerControl(pControl,clientChannel,controlPipe);
	    } else {
		if(staticGeometryControl == null) {
		    Pipe controlPipe = Pipe.open();
		    controlPipe.source().configureBlocking(false);
		    staticGeometryControl = new StaticGeometryControl(PolymakeJvxParser.parse(psReader),psReader,controlPipe.sink());
		    selectorThread.registerControl(staticGeometryControl,controlPipe);
		} else {
		    staticGeometryControl.update();
		}
	    }

	}
	selectorThread.start();
    }

    /**
     * main() method creating a new Launcher.
     */
    public static void main(String args[]) {
	try {
	    Vector classes = new Vector();
	    Vector ports = new Vector();
	    int ps_port = -1;
	    int i = 0;
	    boolean classFound = false;
	    while(i < args.length) {
		if(args[i].equals("-ps_port")) {
		    ps_port = Integer.parseInt(args[i+1]);
		    i += 2;
		    break;
		}
		String className = args[i];
		classes.add(Class.forName(className));
		if(i < args.length - 2 && args[i+1].equals("-client_port")) {
		    ports.add(new Integer(args[i+2]));
		    i += 3;
		} else {
		    ports.add(new Integer(-1));
		    i += 1;
		}
	    }
	    if(i != args.length || ps_port == -1) {
		System.err.println(USAGE);
		System.exit(0);
	    }
	    new Launcher(classes,ports,ps_port);
	} catch(ClassNotFoundException e) {
	    System.err.println(USAGE + "\n"+e.getMessage()+ " not found.");
	    System.exit(0);
	} catch(NumberFormatException e) {
	    System.err.println(USAGE);
	    System.exit(0);
	} catch (Exception e) {
	    System.err.println("Launcher: error initializing channels.");
	    e.printStackTrace(System.err);
	    System.exit(0);
	}
    }
}
