#include <sys/types.h>
#include <sys/stat.h>
#include <ctime>
#include <cstring>

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <queue>
#include <cmath>
#include <exception>
#include <stdexcept>
#include "unistd.h"
#include "timing.hpp"
#include "threading.hpp"
#include "networking.hpp"
#include "SNFMulti.hpp"
#include "snf_xci.hpp"
#include "snf_sync.hpp"

#include "config.h"

// temporary - proving base64codec

#include "base64codec.hpp"

//#include "../nvwa-0.6/nvwa/debug_new.h"
using namespace std;                                                            // Introduce standard namespace.

const char* SERVER_VERSION_INFO = "SNF Server Version " PACKAGE_VERSION " Build: " __DATE__ " " __TIME__;

static const string XCIShutdownResponse =
  "<snf><xci><server><response message=\'shutdown in progress\' code=\'0\'/></server></xci></snf>\n";

class XCIShutdownWatcher : public snfXCIServerCommandHandler {                  // Shutdown watcher.
  public:
    XCIShutdownWatcher():TimeToStop(false){}                                    // Construct with shutdown flag false.
    bool TimeToStop;                                                            // Here is the flag.
    string processXCIRequest(snf_xci& X) {                                      // Here is how we process requests.
        if(0 == X.xci_server_command.find("shutdown")) {                        // If we find shutdown then
            TimeToStop = true;                                                  // set the shutdown flag
            return XCIShutdownResponse;                                         // and let them know we got it.
        }                                                                       // If we get some other request
        return XCIErrorResponse;                                                // return the error response.
    }
};

// Thread Status Analysis For Debugging.

void ThreadStatusToCout() {                                                     // Produce a thread status list.
    ThreadStatusReport R = Threads.StatusReport();                              // Get a report from Threads.
    cout << endl;                                                               // Break the line.
    for(
      ThreadStatusReport::iterator iR = R.begin();                              // Loop through the report.
      iR != R.end(); iR++
      ) {
        ThreadStatusRecord& S = (*iR);                                          // Take each status report and
        cout                                                                    // send it to cout on it's own line.
          << S.getName() << " (" << S.getPointer() << "), "
          << S.getType().Name << ", "
          << S.getState().Name << ", "
          << ((S.getRunning()) ? "Running, " : "Not Running, ")
          << ((S.getBad()) ? "Broken, " : "Ok, ")
          << S.getFault()
          << endl;
    }
    cout << endl;                                                               // Leave a blank line at the end.
}

// Here in the main thread is where we get executive tasks done.

int go(int argc, char* argv[]) { //// go() stands in for main(). main() catches any unhandled exceptions.

    // Check for debug mode.

    bool DebugMode = false;                                                     // This will be our debug mode.
    string argv0(argv[0]);                                                      // Capture how we were called.
    if(
      string::npos != argv0.find("Debug") ||                                    // If we find "Debug" or
      string::npos != argv0.find("debug")                                       // "debug" in our command path
      ) {                                                                       // then we are in DebugMode.
        DebugMode = true;                                                       // Set the flag and tell the
        cout << "Debug Mode" << endl;                                           // watchers.
    }

    // DebugMode = true;                                                           // Force it when needed.

    // Announce Version / Build Info.

	cout << SERVER_VERSION_INFO << endl;                                        // Shout out our version.
	cout << SNF_ENGINE_VERSION << endl;                                         // Shout out the engine version.

    // Sanity checks before we get going.

	if(2 != argc) {                                                             // Check the command line args.
	    cout << "Use: SNFServer <path-to-config-file>" << endl;                 // If wrong, say how we work.
	    return 0;
	}
	if(0 != access(argv[1], R_OK)) {                                            // Check the config file path.
	    cout << "Can't read " << argv[1] << endl;                               // If it's not accessible, punt.
	    return 0;
	}
    cout << "Launching with " << argv[1] << endl;                               // Tell them we're going.
    snf_RulebaseHandler MyRulebase;                                             // Create a rulebase manager.
    MyRulebase.PlatformVersion(SERVER_VERSION_INFO);                            // Set the Platform version string.
    XCIShutdownWatcher ShutdownWatcher;                                         // Make a server shutdown processor
    MyRulebase.XCIServerCommandHandler(ShutdownWatcher);                        // and register it with the engine.
    MyRulebase.open(argv[1], "", "");                                           // Open a configured rulebase.

    Sleeper WaitATic(1000);                                                     // Learn to wait a second.

    cout << "Running." << endl << endl;                                         // Tell them we're running.
    char Tic = '\\';                                                            // Tic/Toc indicator.
    while(false == ShutdownWatcher.TimeToStop) {                                // While running, update the screen.
        WaitATic();                                                             // One second between updates.

        // Animate the Tick/Toc Indicator

        switch(Tic) {
            case '\\': Tic = '|'; break;
            case '|': Tic = '/'; break;
            case '/': Tic = '-'; break;
            default: Tic = '\\'; break;
        }

        // Format and output the screen update. At the end post a \r so that
        // the line appears to update in place.

        cout
          << "M/min: " << setw(4) << (int) MyRulebase.MyLOGmgr.MessagesPerMinute() << " "
          << "SP: " << setw(6) << setprecision(2) << setiosflags(ios::fixed) <<
            ((0 < MyRulebase.MyLOGmgr.MessagesPerMinute()) ?
              (100 * MyRulebase.MyLOGmgr.SpamPerMinute() / MyRulebase.MyLOGmgr.MessagesPerMinute()) : 0.0) << "% "
          << "LR:" << setw(7) << MyRulebase.MyLOGmgr.LatestRuleID()
          << " ["
          << MyRulebase.MyXCImgr.pollClientCount() << "/"
          << MyRulebase.MyXCImgr.pollLoopCount() << " "
          << Tic << " " << (int) MyRulebase.MyXCImgr.TotalQueue() << "] "
          << "W:" << (int) MyRulebase.MyLOGmgr.WhitePerMinute() << " "
          << "C:" << (int) MyRulebase.MyLOGmgr.CautionPerMinute() << " "
          << "B:" << (int) MyRulebase.MyLOGmgr.BlackPerMinute() << " "
          << "T:" << (int) MyRulebase.MyLOGmgr.TruncatePerMinute() << " "
          << "S:" << (int) MyRulebase.MyLOGmgr.SamplePerMinute()
          << "     \r" << flush;

          if(DebugMode) ThreadStatusToCout();                                   // Debug? Show Thread Status Report.

    }
    cout << endl << endl << "Shutdown Received." << endl;

    // When this loop fails it is time to shut down.
    // All the rest happens via XCI now.

    cout << "Closing Rulebase Handler..." << endl;

    MyRulebase.close();

    // All done...

    cout << "Bye bye." << endl;

	return 0;
}

/*
class DebugExceptionHandler {                                                   // Hand wrapper for exception handler.
  public:
    DebugExceptionHandler() {
        LoadLibrary("exchndl.dll");
    }
};

static DebugExceptionHandler TheDebugExceptionHandler;                          // Global exception handler.

*/

int main(int argc, char* argv[]) {

    try {
        go(argc, argv);
    }
    catch(exception& e) {
        cout << "Unhandled Exception: " << e.what() << " Thrown!" << endl;
    }
    catch(...) {
        cout << "Unknown, Unhandled Exception Discovered!" << endl;
    }
    return 0;
}

