import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

public class TftpMain {
    private static class ClientStarter implements Runnable {
        private DatagramPacket requestPacket;
        
        ClientStarter(DatagramPacket requestPacket) {
            this.requestPacket = requestPacket;
        }
        
        public void run() {
            RandomAccessFile openedFile = null;
            DatagramSocket clientSocket = null;
            try {
                try {
                    clientSocket = new DatagramSocket();
                } catch (SocketException e) {
                    System.out.printf("????: could not create socket to client\n");
                } catch (SecurityException e) {
                    System.out.printf("????: no permission to create socket to client\n");
                }

                String filename;
                try {
                    filename = TftpPacket.unpackReadRequest(requestPacket);
                } catch (TftpException e) {
                    System.out.printf("server: illegal read-request packet received\n");
                    respondError(clientSocket, e.getErrorCode(), e.getMessage());
                    return;
                }
                
                // open up file
                ByteBuffer fileMap = null;
                try {
                    openedFile = new RandomAccessFile(filename, "r");
                } catch (FileNotFoundException e) {
                    System.out.printf("server: file '%s' does not exist\n", filename);
                    respondError(clientSocket, 1, "cannot open file");
                    return;
                }
                try {
                    FileChannel channel = openedFile.getChannel();
                    fileMap = channel.map(MapMode.READ_ONLY, 0, channel.size());
                } catch (IOException e) {
                    System.out.printf("server: error while mapping file: %s\n", e.toString());
                    respondError(clientSocket, 1, "cannot map file to memory");
                    return;
                }
                
                TftpServer server = new TftpServer(clientSocket, requestPacket.getAddress(),
                        requestPacket.getPort(), filename, fileMap);
                server.run();
            } finally {
                if (clientSocket != null) {
                    clientSocket.close();
                }
                if (openedFile != null) {
                    try {
                        openedFile.close();
                    } catch (IOException e) { }
                }
            }
        }
        
        private void respondError(DatagramSocket socket, int errCode, String errMsg) {
            DatagramPacket packet = new DatagramPacket(new byte[600], 600, requestPacket.getAddress(), requestPacket.getPort());
            try {
                socket.send(packet);
            } catch (IOException e) { }
        }
    }
    
    public static void main(String[] args) {
        DatagramSocket server = null;
        try {
            server = new DatagramSocket(TftpClient.HOST_PORT);
        } catch (SocketException e) {
            System.out.println("could not create server socket - maybe another server is still running?");
            return;
        } catch (SecurityException e) {
            System.out.println("no permission to create client socket");
            return;
        }
        System.out.printf("server: listening on port %d\n",
            TftpClient.HOST_PORT);
        
        // start client
        TftpClient.runBackground();
        
        while (true) {
            DatagramPacket packet = new DatagramPacket(new byte[600], 600);
            try {
                server.receive(packet);
            } catch (IOException e) {
                System.out.printf("server: error while awaiting client connection\n");
                break;
            }
            ClientStarter starter = new ClientStarter(packet);
            new Thread(starter).start();
        }
    }
}
