package net.i2p.router.admin;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Iterator;
import java.util.Set;

import net.i2p.data.Hash;
import net.i2p.data.DataHelper;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.web.StatsGenerator;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;

class AdminRunner implements Runnable {
    private Log _log;
    private RouterContext _context;
    private Socket _socket;
    private StatsGenerator _generator;
    
    public AdminRunner(RouterContext context, Socket socket) {
        _context = context;
        _log = context.logManager().getLog(AdminRunner.class);
        _socket = socket;
        _generator = new StatsGenerator(context);
    }
    
    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
            OutputStream out = _socket.getOutputStream();
            
            String command = in.readLine();
            runCommand(command, out);
        } catch (IOException ioe) {
            _log.error("Error running admin command", ioe);
        }
    }
    
    private void runCommand(String command, OutputStream out) throws IOException {
        _log.debug("Command [" + command + "]");
        if (command.indexOf("favicon") >= 0) {
            reply(out, "this is not a website");
        } else if ( (command.indexOf("routerStats.html") >= 0) || (command.indexOf("oldstats.jsp") >= 0) ) {
            try {
                out.write(DataHelper.getASCII("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n"));
                _generator.generateStatsPage(new OutputStreamWriter(out), true);
                out.close();
            } catch (IOException ioe) {
                if (_log.shouldLog(Log.WARN))
                    _log.warn("Error writing out the admin reply");
                throw ioe;
            }
        } else if (command.indexOf("/profile/") >= 0) {
            replyText(out, getProfile(command));
        } else if (command.indexOf("/shutdown") >= 0) {
            reply(out, shutdown(command));
        } else if (true || command.indexOf("routerConsole.html") > 0) {
            try {
                out.write(DataHelper.getASCII("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n"));
                // TODO Not technically the same as router().renderStatusHTML() was
                _context.routerAppManager().renderStatusHTML(new OutputStreamWriter(out));
                out.close();
            } catch (IOException ioe) {
                if (_log.shouldLog(Log.WARN))
                    _log.warn("Error writing out the admin reply");
                throw ioe;
            }
        }
    }
    
    private void reply(OutputStream out, String content) throws IOException {
        StringBuilder reply = new StringBuilder(10240);
        reply.append("HTTP/1.1 200 OK\n");
        reply.append("Connection: close\n");
        reply.append("Cache-control: no-cache\n");
        reply.append("Content-type: text/html\n\n");
        reply.append(content);
        try {
            out.write(DataHelper.getASCII(reply.toString()));
            out.close();
        } catch (IOException ioe) {
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error writing out the admin reply:\n" + content);
            throw ioe;
        }
    }
    
    private void replyText(OutputStream out, String content) throws IOException {
        StringBuilder reply = new StringBuilder(10240);
        reply.append("HTTP/1.1 200 OK\n");
        reply.append("Connection: close\n");
        reply.append("Cache-control: no-cache\n");
        reply.append("Content-type: text/plain\n\n");
        reply.append(content);
        try {
            out.write(DataHelper.getASCII(reply.toString()));
            out.close();
        } catch (IOException ioe) {
            if (_log.shouldLog(Log.WARN))
                _log.warn("Error writing out the admin reply:\n" + content);
            throw ioe;
        }
    }
    
    private String getProfile(String cmd) {
        Set peers = _context.profileOrganizer().selectAllPeers();
        for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
            Hash peer = (Hash)iter.next();
            if (cmd.indexOf(peer.toBase64().substring(0,10)) >= 0) {
                try {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream(64*1024);
                    _context.profileOrganizer().exportProfile(peer, baos);
                    return new String(baos.toByteArray());
                } catch (IOException ioe) {
                    _log.error("Error exporting the profile", ioe);
                    return "Error exporting the peer profile\n";
                }
            }
        }
        
        return "No such peer is being profiled\n";
    }
    
    private static final String SHUTDOWN_PASSWORD_PROP = "router.shutdownPassword";
    private String shutdown(String cmd) {
        String password = _context.router().getConfigSetting(SHUTDOWN_PASSWORD_PROP);
        if (password == null)
            password = _context.getProperty(SHUTDOWN_PASSWORD_PROP);
        if (password == null) 
            return "No shutdown password specified in the config or context - <b>REFUSING SHUTDOWN</b>." +
                   "<a href=\"/routerConsole.html\">back</a>";
        if (cmd.indexOf(password) > 0) {
            I2PThread t = new I2PThread(new Runnable() {
                public void run() { 
                    try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
                    _context.router().shutdown(Router.EXIT_HARD);
                }
            });
            t.start();
            return "Shutdown request accepted.  Killing the router in 30 seconds";
        } else {
            return "Incorrect shutdown password specified.  Please edit your router.config appropriately." +
                   "<a href=\"/routerConsole.html\">back</a>";
        }
    }
}
