/*
 ** This file is part of Filius, a network construction and simulation software.
 ** 
 ** Originally created at the University of Siegen, Institute "Didactics of
 ** Informatics and E-Learning" by a students' project group:
 **     members (2006-2007): 
 **         André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding,
 **         Nadja Haßler, Ernst Johannes Klebert, Michell Weyer
 **     supervisors:
 **         Stefan Freischlad (maintainer until 2009), Peer Stechert
 ** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de>
 **         and Stefan Freischlad
 ** Filius 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) version 3.
 ** 
 ** Filius 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 Filius.  If not, see <http://www.gnu.org/licenses/>.
 */
package filius.software.rip;

import filius.hardware.NetzwerkInterface;
import filius.hardware.knoten.InternetKnoten;
import filius.software.clientserver.ServerMitarbeiter;
import filius.software.system.VermittlungsrechnerBetriebssystem;
import filius.software.transportschicht.Socket;
import filius.software.vermittlungsschicht.IP;

/**
 * 
 * @author pyropeter
 * @author stefanf
 * 
 */
public class RIPServerMitarbeiter extends ServerMitarbeiter {
    private RIPTable table;
    private VermittlungsrechnerBetriebssystem bs;
    private InternetKnoten knoten;

    public RIPServerMitarbeiter(RIPServer server, Socket socket) {
        super(server, socket);
        bs = (VermittlungsrechnerBetriebssystem) server.getSystemSoftware();
        knoten = (InternetKnoten) bs.getKnoten();
        table = bs.getRIPTable();
    }

    protected void verarbeiteNachricht(String nachricht) {
        RIPMessage msg;
        try {
            msg = new RIPMessage(nachricht);
        } catch (IllegalArgumentException e) {
            return;
        }

        // find the interface that (probably) received the message
        String nicIp = findeInterfaceIp(msg.ip);
        if (nicIp == null || nicIp.equals(msg.ip)) {
            return;
        }

        RIPRoute route;
        int hops;
        synchronized (table) {
            for (RIPMessageRoute entry : msg.routes) {
                if (entry.hops >= msg.infinity || entry.hops + 1 >= RIPTable.INFINITY) {
                    hops = RIPTable.INFINITY;
                } else {
                    hops = entry.hops + 1;
                }

                route = table.search(entry.ip, entry.mask);
                if (route != null) {
                    // route exists, just update
                    if (!route.getGateway().equals(msg.ip) && route.hops <= hops) {
                        continue;
                    }
                    if (route.hops > hops) {
                        // found a shorter route
                        route.setGateway(msg.ip);
                        route.hopPublicIp = msg.publicIp;
                        route.setInterfaceIpAddress(nicIp);
                    } else if (route.hops < hops) {
                        // the old route just got worse. this has to be
                        // flushed to other routers immediately
                        table.setNextBeacon(0);
                    }
                    route.hops = hops;
                    route.refresh(msg.timeout);
                } else {
                    // route is unknown, create it
                    if (hops < RIPTable.INFINITY) {
                        route = new RIPRoute(msg.timeout, entry.ip, entry.mask, msg.ip, msg.publicIp, nicIp, hops);
                        table.addRoute(route);
                        table.setNextBeacon(0);
                    }
                }
            }
            table.notifyAll();
        }
    }

    String findeInterfaceIp(String ipStr) {
        long ip = IP.inetAton(ipStr);

        for (NetzwerkInterface nic : knoten.getNetzwerkInterfaces()) {
            long netMask = IP.inetAton(nic.getSubnetzMaske());
            long netAddr = IP.inetAton(nic.getIp()) & netMask;

            if ((ip & netMask) == netAddr) {
                return nic.getIp();
            }
        }
        return null;
    }
}
