 /*
  * (C) 2003-2004 Columbia University Departments of Computer Science and
  * Electrical Engineering
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, is permitted provided the original authors give
  * written consent and the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above
  *    copyright notice, this list of conditions and the following
  *    disclaimer in the documentation and/or other materials
  *    provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  */
import java.util.*;
import java.io.*;
import java.net.*;

public class ShortcutRouter extends Thread {

    public static final int PROXY_PORT = 18080;
    public static final int UDP_PORT =   18081;      // used for queries

    public static final int MAX_RETRIES = 10;
    public static final int RETRY_INTERVAL_MILLIS = 250;

    public static String     localIP;
    public static String     localHostname;

    protected int        rAlgPort;

    protected SynchHashMap      beaconCache;             // both synchronized
    protected SynchHashMap      localCache;
    protected ArrayList         targets;

    protected ShortcutSender    sender;
    protected ShortcutReceiver  receiver;

    protected BufferedReader    br;               // for contact with routing
    protected BufferedWriter    bw;

    public ShortcutRouter(int rAlgPort) {

        /* open socket to Chord or similar routing algorithm */
        this.rAlgPort = rAlgPort;
        try {
        Socket rAlgSocket = new Socket(localHostname, rAlgPort);
        System.err.println("Connect to: "+localIP+":"+rAlgPort);
	InputStream is = rAlgSocket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        br = new BufferedReader(isr);

        OutputStream os = rAlgSocket.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(os);
        bw = new BufferedWriter(osw);
        }
        catch (Exception e) {
            System.err.println("ShortcutRouter: error setting up link to " +
                               "routing algorithm: " + e);
            System.exit(0);
        }

        /* set up data structures for servlet information */
        //beaconCache = Collections.synchronizedMap(new HashMap());
        //localCache = Collections.synchronizedMap(new HashMap());
        beaconCache = new SynchHashMap();
        localCache = new SynchHashMap();


        /* set up object used to send UDP data through the overlay */
        sender = new ShortcutSender();

        /* create thread to accept all UDP information */
        receiver = new ShortcutReceiver(UDP_PORT, beaconCache,
                                        localCache, sender, this);
        receiver.start();

        /* we are not the servlet for any targets (yet) */
        targets = null;
    }

    /* special constructor for cases where we have a list of targets for which
     * we are the servlet */
    public ShortcutRouter(int rAlgPort, String servletFile) {

        /* call constructor as normal */
        this(rAlgPort);

        /* get list of all the addresses for which we are the servlet */
        targets = new ArrayList();
        try {
            BufferedReader in =new BufferedReader(new FileReader(servletFile));
            String line;
            while((line = in.readLine()) != null) {
                System.out.println(line);
		targets.add(line);
            }
        }
        catch (Exception e) {

        }

        /* create thread to periodically send out notifications to the beacon*/
        ShortcutAnnounce sa = new ShortcutAnnounce(targets, UDP_PORT, this);
        sa.start();

    }

    public NextDestination getNextHop (String target) {

        /* am I the servlet? */
        if (targets != null) {
            for (int i=0; i<targets.size(); i++) {
                String curTarget = (String)targets.get(i);
                if (curTarget.equalsIgnoreCase(target)) {
                    System.out.println("I am the Servlet");
		    return new NextDestination(target, 0, true);
                }
            }
        }

        /* is the servlet already listed in the local cache */
        ShortcutCacheEntry ce = (ShortcutCacheEntry)localCache.get(target);
        if (ce != null) {
			System.err.println("I have the servlet in the SERVLET cache..."+ce);
            return new NextDestination(ce.servletIP, PROXY_PORT, false);
        }
        System.err.println(localCache);

        /* am I the beacon? */
        ce = (ShortcutCacheEntry)beaconCache.get(target);
        if (ce != null) {
            System.err.println("I have the servlet in the BEACON cache..."+ce);
			return new NextDestination(ce.servletIP, PROXY_PORT, false);
        }

        /* if none of the above, must query the beacon through Chord */
        // get next hop from chord
        String nextIP = queryRoutingAlg(target);
        System.err.println("Servlet not found in local caches searching chord...");

        // send request into chord
        sender.sendRequest(target, nextIP, UDP_PORT, localIP, UDP_PORT);

        // use the special method on the cache to test if we get a response
        System.err.println("using getWait");
        ce=(ShortcutCacheEntry)localCache.getWait(target);

        if (ce != null) {
            return new NextDestination(ce.servletIP,PROXY_PORT, false);
        }

        else return null;
    }

    public String queryRoutingAlg (String target) {

        String nextChordHop = "";

        // write request to Chord
        target = target + "\n";

        try {
            bw.write(target);
            bw.flush();

        // read response
            nextChordHop = br.readLine();
        }
        catch (Exception e) {
            e.printStackTrace();
        }


        return nextChordHop;
    }

    public static void main(String[] args) {

        /* start the main routing processes */
        ShortcutRouter sr = null;
        if (args.length == 3) {
            int rAlgPort = Integer.parseInt(args[0]);
	    localHostname = args[1];
	    localIP = args[2];
            sr = new ShortcutRouter(rAlgPort);
        }
        else if (args.length == 4) {
            int rAlgPort = Integer.parseInt(args[0]);
	    localHostname = args[1];
	    localIP = args[2];
            sr = new ShortcutRouter(rAlgPort, args[3]);
        }
        else {
            System.err.println("Usage: java ShortcutRouter <routeAlgPort> " +
                               "<Machine Hostname> <Machine IP> [targetList]");
            System.exit(0);
        }
        sr.start();

        /* start the proxy server with a link to the main routing process */
        SSLSocketServer sp = new SSLSocketServer(PROXY_PORT, sr);
        //sp.start();
    }
}
