 /*
  * (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.
  */
/**
 *
 * ShortcutReceiver
 *
 * receives UDP packets containing requests and processes them as necessary
 *
 * @author   Bill Morein
 * @version  3/9/03
 *
 */

import java.io.*;
import java.net.*;
import java.util.*;

public class ShortcutReceiver extends Thread {

    protected DatagramSocket socket = null;
    protected BufferedReader in = null;
    protected boolean more = true;
    protected SynchHashMap beaconCache;       // targetIP, ShortcutCacheEntry
    protected SynchHashMap localCache;
    protected ShortcutSender sender;
    protected ShortcutRouter parent;

    public ShortcutReceiver(int listenPort, SynchHashMap bc, SynchHashMap lc,
                            ShortcutSender ss, ShortcutRouter sr) {
        beaconCache = bc;
        localCache = lc;
        sender = ss;
        parent = sr;

        try {
            socket = new DatagramSocket(listenPort);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() {

        while (more) {
            try {
                byte[] buf = new byte[512];

                // receive request
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                socket.receive(packet);
                int length = packet.getLength();
                String received = (new String(packet.getData(), 0, length));
                char code = received.charAt(0);

                /* these should probably spawn or reactivate processes to
                 * ensure that we do not miss any UDP packets, but to my
                 * understanding there is some buffering in the DatagramSocket
                 * class so this should be good enough for now */
                if ('0' == code) {
                    processRequest(received);
                }
                else if ('1' == code) {
                    processResponse(received);
                }
                else if ('2' == code) {
                    processServletAnnounce(received);
                }
                else {
                    System.err.println("ShortcutReceiver: unknown code");
                }
            }

        catch (IOException e) {
                e.printStackTrace();
                more = false;
            }
        }

        socket.close();
    }

    protected void processRequest(String received) {
        try {
            StringTokenizer st = new StringTokenizer(received, ",:");
            String requestCode = st.nextToken();
            String targetIP = st.nextToken();
            String requesterIP = st.nextToken();
            int requesterPort = Integer.parseInt(st.nextToken());

            /* am I the beacon? */
            ShortcutCacheEntry cacheEntry = (ShortcutCacheEntry)
                                                     beaconCache.get(targetIP);
            if (cacheEntry != null) {
                // it is in there, so reply directly to requester
                String servletIP = cacheEntry.servletIP;
                int servletPort = cacheEntry.servletPort;
                sender.sendResponse(servletIP, servletPort, targetIP,
                                    requesterIP, requesterPort);
            }

            /* not the beacon, so send the request onwards through chord */
            else {

                // get address from the chord module
                String nextHopIP = parent.queryRoutingAlg(targetIP);
                int nextHopPort = parent.UDP_PORT;

                // need to make sure that I am not supposed to be the beacon
                if (nextHopIP.equals(parent.localIP)) {
                    System.err.println("Shortcut Receiver: am supposed to be "+
                                       "the beacon, but I have no information"+
                                       " on the request");
                }
                else {
                    sender.sendRequest(targetIP, nextHopIP, nextHopPort,
                                       requesterIP, requesterPort);
                }
            }
        }
        catch (Exception e) {
            System.err.println("ShortcutReceiver: invalid request message: " +
                               received);
        }
    }

    protected void processResponse(String received) {
        try {
            // parse the response code
            StringTokenizer st = new StringTokenizer(received, ",:");
            String requestCode = st.nextToken();
            String servletIP = st.nextToken();
            int servletPort = Integer.parseInt(st.nextToken());
            String targetIP = st.nextToken();

            // put it in our local cache
            localCache.put(targetIP, new ShortcutCacheEntry (servletIP,
                                                             servletPort));
            System.out.println(localCache);
        }
        catch (Exception e) {
            System.err.println("ShortcutReceiver: invalid response message: " +
                               received);
        }
    }

    /* untested as of yet */
    protected void processServletAnnounce(String received) {
        try {
            /* parse the response code */
            StringTokenizer st = new StringTokenizer(received, ",:");
            String requestCode = st.nextToken();
            String servletIP = st.nextToken();
            int servletPort = Integer.parseInt(st.nextToken());
            String targetIP = st.nextToken();

            /* get the next hop from Chord; if me then I am the beacon so
             * put it in my beacon cache */
            // get address from the chord module
            String nextHopIP = parent.queryRoutingAlg(targetIP);
            int nextHopPort = parent.UDP_PORT;

            /* this assumes only one Chord node per ip, which seems realistic*/
            if (nextHopIP.equals(parent.localIP)) {
                // I am the beacon, so put in my beacon cache
                System.out.println("received announcement for " + servletIP);
                beaconCache.put(targetIP, new ShortcutCacheEntry(servletIP,
                                                                 servletPort));
            }
            else {
                // not the beacon, so send on to the beacon through Chord
                sender.sendServletAnnounce(servletIP, servletPort, targetIP,
                                           nextHopIP, nextHopPort);
            }

        }
        catch (Exception e) {
            System.err.println("ShortcutReceiver: invalid servlet announce: " +
                               received);
        }
    }

    public static void main(String[] args) {
        SynchHashMap beaconCache = new SynchHashMap();
        SynchHashMap localCache = new SynchHashMap();

        ShortcutSender sender = new ShortcutSender();

        beaconCache.put("theTarget", new ShortcutCacheEntry("servlet", 10000));

        ShortcutReceiver r = new ShortcutReceiver(4445, beaconCache,
                                                  localCache, sender, null);
        r.start();
    }
}
