/*
 * todo: 
 * - results so abholen, dass gleich die einzenlen dateien verlinkt werden können und aufs handy gepusht werden
 * - in eine datei die aktuellen ergebnisse zwischenspeichern serilized, bei jedem start einlesen ( key=SESSIONID, VALUE=XMLRESULTSTRING )
 * - polling beim server einbauen
 * - im fehlerfall thread stoppen und neuen thread starten oder externe anwendung startet im fehlerfall neu
 * - lockfile support, run only one instance
 * http://download.oracle.com/javase/tutorial/essential/concurrency/simple.html
 */
package buzzerproxy;

import buzzerproxy.CommunicationProtocol;
import buzzerproxy.Crypto;
import buzzerproxy.Google;
import buzzerproxy.GoogleResultParser;
import buzzerproxy.Gzip;
import buzzerproxy.IO;
import buzzerproxy.Logging;
import buzzerproxy.XMLParser;
import buzzerproxy.bprot.ByteProtServer;
import buzzerproxy.handler.ExceptionHandler;
import buzzerproxy.handler.QuestionInterface;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 *
 * @author thecerial
 */
public class BuzzerProxy implements ExceptionHandler, QuestionInterface {

    private ServerSocket serverSocket = null;
    private Socket socket = null;
    private final String secredCryptoKey = "7953e8d5dc9fed4b6d37569410fe2f85";
    private int ServerPort = ###########;
    private int backlogQueue = 10;
    private int timeout = 7000;                                                 // 7 Sekunden darf jeder Request die Server Ressourcen verbraten
    private String BuzzerProxyVersion = "1.1.a - 12.8.2011";
    private String SECRET_AUTH_KEY = "2429545b007ffc86a98df6f3cab76fc4";
    private String Version = "www.buzzerstar.com" + " / BuzzerProxyJar Version -" + BuzzerProxyVersion
            + " running on localhost on Port " + ServerPort;
    private Socket client;
    private ServerSocket server;
    private String xmlContent = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>" + "<search>"
            + "<servicetyp>find</servicetyp>" + "<searchquery>Madonna</searchquery>"
            + "<filetyp>.mp3+.wav+.flac</filetyp>"
            + "<session>23232323232323232323232323</session>"
            + "<clientversion>1.0.1</clientversion>"
            + "<clientid>2429545b007ffc86a98df6f3cab76fc4</clientid>" + "</search>"
            + "</xml>";
    private XMLParser XMLParser = null;
    private Logging Logging = null;
    private Gzip Gzip = null;
    private Crypto Crypto = null;
    private Google Google = null;
    private GoogleResultParser GoogleResultParser = null;
    private CommunicationProtocol CommunicationProtocol = null;
    private IO IO = null;
    private HashMap<String, Serializable> resultsForClients = null;
    //Timer                                 timer                 = null;

    /**
     */
    private BuzzerProxy() {
        // Server mit Byte Protokol
        System.out.println(Version);
        new ByteProtServer(3382, this/*QuestionInterface*/, this/*ExceptionHandler*/).start();

        // new ServerThread().start();

    }

    final class ServerThread extends Thread {

        @Override
        public void run() {

            String decryptedGzipXMLContent = "";
            String xmlContent = "";
            String googleResultString = "";
            HashMap<String, String> results = null;
            String clientid = null;
            String clientversion = null;
            String searchservice = null;
            String searchquery = null;
            String page = null;
            String filetyp = null;
            String cryptedResults = null;

            BufferedReader inFromServer = null;
            DataOutputStream outToClient = null;

            try {
                server = new ServerSocket(ServerPort);

            } catch (IOException ex) {
                Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ex);
            }

            //System.out.println(this); 
            //this.clone();

            //System.exit(9);

            while (this.isAlive()) {

                try {

                    client = server.accept();
                    String clientIPAdress = client.getInetAddress().toString();
                    System.out.println("[" + clientIPAdress + "]" + " Client Connected");

//                    timer = new Timer( timeout, client );

                    try {

                        client.setKeepAlive(true);
                        // client.setSoTimeout(timeout);
                        client.setTrafficClass(0x04);
                        client.setTcpNoDelay(true);

                    } catch (Exception x) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, x);
                    } finally {
                    }

                    inFromServer = new BufferedReader(new InputStreamReader(client.getInputStream()));
                    outToClient = new DataOutputStream(client.getOutputStream());
                    // outToClient.writeUTF("DEBUG: Welcome to " + Version);

                    try {
                        String userInput = null;
                        //cryptedResults = inFromServer.readLine();
                        while ((userInput = inFromServer.readLine()) != null) {
                            cryptedResults += userInput;
                        }
                    } catch (Exception e) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, e);
                    }

//                    if ( timer.isTimeout == true ) {
//                        System.out.println( "Got Timeout" );
//                    }

                    try {
                    } catch (NullPointerException n) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, n);
                        client.close();
                    } catch (Exception n) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, n);
                        client.close();
                    } finally {
                        //                        System.out.println("Beende mich");
                        // System.exit(1);
                        //                        client.close();
                        //                        this.interrupted();
                    }

                    System.out.println("Got from Client: " + cryptedResults.toString());
                    if (cryptedResults.equalsIgnoreCase("exit")) {
                        outToClient.writeUTF("BuzzerProxy closing Client Socket Connection because of Termination Request");
                        client.close();
                    } //  if (cryptedResults.equalsIgnoreCase("exit")) {

                    //////                try { // Decrypt
                    //////                    decryptedGzipXMLContent = Crypto.performDecrypt(secredCryptoKey.getBytes(), cryptedResults.toString());
                    //////                } catch (Exception ex) {
                    //////                    System.out.println("Es gab probleme beim Decrypten der Nachricht.");
                    //////                    Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ex);
                    //////                }
                    //////
                    //////                try { // Gunzip
                    //////                  xmlContent = Gzip.gunzipString(decryptedGzipXMLContent);
                    //////                } catch (IOException ioex) {
                    //////                  Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ioex);
                    //////                }

                    try {
                        results = XMLParser.parseResults(xmlContent);
                        clientid = results.get(XMLParser.CLIENT_ID);
                        clientversion = results.get(XMLParser.CLIENT_VERSION);
                        searchservice = results.get(XMLParser.SEARCH_SERVICE);
                        searchquery = results.get(XMLParser.SEARCH_QUERY);
                        page = results.get(XMLParser.RESULT_PAGE);
                        filetyp = results.get(XMLParser.FILE_TYP);
                    } catch (NullPointerException n) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, n);
                        // client.close();
                    }

                    String output = "##############################################################\n" + "Client IP:          " + clientIPAdress
                            + "\n" + "Client ID:          " + clientid + "\n" + "Client Version:     " + clientversion + "\n"
                            + "Client Service:     " + searchservice + "\n" + "Client Query:       " + searchquery + "\n"
                            + "Client Page:        " + page + "\n" + "Client FileTyp:     " + filetyp + "\n";

                    /**
                    Nimm den Request vom Client entgegegn und sende ihm eine 32 Bittige Session ID            
                     */
                    try { // Send Google Request
                        if (IO.isSet(searchservice) && IO.isSet(clientid)) {
                            if (searchservice.equalsIgnoreCase("find") && clientid.equals(SECRET_AUTH_KEY)) {

                                long sessionID = System.currentTimeMillis();// Crypto.generateSessionID();
                                String sessionXML = CommunicationProtocol.sessionXML(sessionID);
                                outToClient.writeUTF(sessionXML);
                                client.close();

                            } // if ( searchservice.equalsIgnoreCase("find") && clientid.equals(SECRET_AUTH_KEY)) {   
                        } //  if ( IO.isSet(searchservice) && IO.isSet(clientid) {
                    } catch (Exception ex) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ex);
                        // client.close();
                    }

                    try {

                        if (IO.isSet(searchquery) && IO.isSet(filetyp)
                                && (searchservice.equalsIgnoreCase("results") && clientid.equals(SECRET_AUTH_KEY))) {

                            googleResultString = Google.getGoogleContent(searchquery, filetyp);
                            ArrayList<ResultContainer> ResultXMLString = GoogleResultParser.parseResults(googleResultString, filetyp);
                            outToClient.writeUTF("ResultXMLString");
                            client.close();

                            output += "Client Google JSON: " + googleResultString + "\n";
                            output += "Client Results:     " + ResultXMLString + "\n";
                            output += "##############################################################" + "\n";

                            System.out.println(output);
                            Logging.appendFile(output);

                        }// if (IO.isSet(searchquery) && IO.isSet(filetyp) && (searchservice.equalsIgnoreCase("results") && clientid.equals(SECRET_AUTH_KEY)) )

                    } catch (Exception ex) {
                        Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ex);
                        //   client.close();
                    }

                } catch (IOException ex) {
                    Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ex);
                }

                // Reset the timer
                //   timer.reset();

            } // while (true) {
        } // public void run() {
    } // final class ServerThread extends Thread {

    public void stop() {
        try {
            server.close();
            client.close();
        } catch (IOException ex) {
            Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Fange IOException ab");
        } catch (NullPointerException nex) {
            Logger.getLogger(BuzzerProxy.class.getName()).log(Level.SEVERE, null, nex);
            System.out.println("Fange Nullpointer ab");
        }
        System.out.println("Bin in der Stop Methode angekommen");
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        BuzzerProxy proxy = new BuzzerProxy();

    } // public static void main(String[] args) {

    @Override
    public void throwException(Throwable throwable) {
        throwable.printStackTrace();
    }

    @Override
    public List<ResultContainer> request(String xmlRequest) throws Exception {

        // TODO Google Abfrage oder Puffer
        List<ResultContainer> resultBox = null; // <<< Sebastian
        return resultBox;
    }
    //////    private void init() {
    //////
    //////        XMLParser XMLParser = new XMLParser();
    //////        Logging Logging = new Logging();
    //////        Gzip Gzip = new Gzip();
    //////        Crypto Crypto = new Crypto();
    //////        Google Google = new Google();
    //////        GoogleResultParser GoogleResultParser = new GoogleResultParser();
    //////        CommunicationProtocol CommunicationProtocol = new CommunicationProtocol();
    //////        IO IO = new IO();
    //////
    //////
    //////    } // public void init(){
}