#include <windows.h>
#include <iostream>
#include <string>
#include "snfmultidll.h"
#include "../CodeDweller/timing.hpp"
#include "../CodeDweller/threading.hpp"


class Pounder : private Thread {                                                // A Pounder _REALLY_ scans its msg.
  private:
    string MessageToTest;                                                       // This is the message to test.
    bool TimeToStop;                                                            // True when time to stop.
    volatile int ScansAccomplished;                                             // Number completed so far.
    volatile int HighestHandleSeen;                                             // Highest handle so far.
    void myTask();                                                              // How it is done.

  public:
    Pounder(string Message);                                                    // Ctor takes the message to scan.
    ~Pounder();                                                                 // Dtor shuts things down.
    int NumberOfScans();                                                        // Returns number of scans.
    int HighestHandle();                                                        // Returns the highest handle.
    void stop();                                                                // Stops.
};

Pounder::Pounder(string Message) :                                              // Ctor for a pounder.
  MessageToTest(Message),                                                       // Capture the message to test
  TimeToStop(false),                                                            // and initialize the variables.
  ScansAccomplished(0),
  HighestHandleSeen(0) {
      run();                                                                    // When ready, run the task.
}

Pounder::~Pounder() {                                                           // When being destroyed stop
    stop();                                                                     // the thread if it's not stopped.
}

int Pounder::NumberOfScans() {                                                  // Return the number of scans.
    return ScansAccomplished;                                                   // Simple.
}

int Pounder::HighestHandle() {                                                  // Return the hightest handle.
    return HighestHandleSeen;                                                   // Simple.
}

void Pounder::stop() {                                                          // To stop the thread:
    if(!TimeToStop) {                                                           // Skip if already stopped.
        TimeToStop = true;                                                      // If not stopped, set the stop
        join();                                                                 // flag and wait for the end.
    }
}

void Pounder::myTask() {                                                        // This is how it is done.
    while(!TimeToStop) {                                                        // Until it is time to stop:

        int ScanHandle =                                                        // Perform a scan and get
          scanBuffer(                                                           // a handle to it's results.
            (unsigned char*)MessageToTest.c_str(),                              // Scan the message buffer,
            MessageToTest.length(),                                             // provide the buffer length,
            (char *) "nobigdeal",                                               // name the message,
            0                                                                   // and say no setup time.
        );

        if(1 > ScanHandle) {                                                    // If the scan failed, then
            TimeToStop = true;                                                  // it is time to stop.
            break;
        }

        if(ScanHandle > HighestHandleSeen) {                                    // Keep track of the highest
            HighestHandleSeen = ScanHandle;                                     // handle we've seen.
        }

        char* HeadersString = 0;                                                // Setp to get the results as
        int HeadersStringLength = 0;                                            // an xheader string.
        int ScanResult =
          getScanXHeaders(ScanHandle, &HeadersString, &HeadersStringLength);    // Get the results.

        string XHeaders = HeadersString;                                        // Form a string.
        closeScan(ScanHandle);                                                  // Got our data, close the scan.

        if(0 > ScanResult) { TimeToStop = true; break; }                        // If we got an error, stop.
        if(0 == HeadersStringLength) { TimeToStop = true; break; }              // If there is no length, stop.
        if(HeadersStringLength != XHeaders.length()) {                          // If the string seems corrupt
            TimeToStop = true; break;                                           // then stop.
        }

        ++ScansAccomplished;                                                    // All went well, count the scan.

    }
}

string BigGiantUglyMessage =
    "Received: from [213.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "Received: from [13.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "Received: from [23.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "Received: from [33.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "Received: from [43.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "Received: from [53.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "Received: from [63.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
    "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
    "From: \"somebody\" <somebody@someplace.com>\r\n"
    "Subject: Nothing to see here\r\n"
    "\r\n"
    "So, this is a message eh?\r\n"
    "Don't you have something special to say?\r\n"
    "I suppose you could say something else too.\r\n"
    "This is just more filler text. No. Really.\r\n"
    "Nothing to see here. Move along. Move along.\r\n"
    "If ever a whiz of a whiz there was the wizard of oz is one because,\r\n"
    "because, because, because, because, because..... \r\n"
    "Because of the wonderful things he does.\r\n"
    "\r\n"
    "I repeat myself when under stress. I repeat myself when under stress.\r\n"
    "I repeat myself when under stress. I repeat myself when under stress.\r\n"
    "I repeat myself when under stress. I repeat myself when under stress.\r\n"
    "I repeat myself when under stress. I repeat...\r\n"
    "\r\n"
    "I think that's enough craziness for now.\r\n"
    ".\r\n";

int main()
{
	std::cout << "Hello world!" << std::endl;

	int LastResult;

    std::cout << "Throttling not set" << endl;
    //setThrottle(3);

	std::cout << "startupSNF(): ";
	LastResult = startupSNF((char *) "prescale.xml");
	std::cout << LastResult << std::endl;

    std::cout << "Scanning ugly text: ";
    string UglyText1 =
      "Received: from [213.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
      "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
      "From: \"somebody\" <somebody@someplace.com>\r\n"
      "Subject: Nothing to see here\r\n"
      "\r\n"
      "So, this is a message eh?\r\n"
      ".\r\n";

    string UglyText2 =
      "Received: from [11.93.213.84] (HELO uw-4b58d8528225.arnhem.chello.nl)\r\n"
      "   by inbound.appriver.com (CommuniGate Pro SMTP 5.0.6)\r\n"
      "From: \"somebody\" <somebody@someplace.com>\r\n"
      "Subject: Nothing to see here\r\n"
      "\r\n"
      "So, this is a message eh?\r\n"
      ".\r\n";

    int HandleOne = scanBuffer((unsigned char*)UglyText1.c_str(), UglyText1.length(), (char *) "", 0);
    char* Result1; int Result1Length;
    LastResult = getScanXHeaders(HandleOne, &Result1, &Result1Length);
    std::cout << HandleOne << ", " << LastResult << endl;
    std::cout << Result1;
    LastResult = closeScan(HandleOne);
    std::cout << "Closed handle one: " << LastResult << endl;

    // DEBUG.
    //    cout << "scanFile('testFile', 33): " << scanFile("testFile", 33) << endl;
    //    exit(0);
    // END OF DEBUG.

    HandleOne = scanBuffer((unsigned char*)UglyText1.c_str(), UglyText1.length(), (char *) "", 0);
    LastResult = getScanXHeaders(HandleOne, &Result1, &Result1Length);
    std::cout << HandleOne << ", " << LastResult << endl;
    std::cout << Result1;

    int HandleTwo = scanBuffer((unsigned char*)UglyText1.c_str(), UglyText1.length(), (char *) "", 0);
    char* Result2; int Result2Length;
    LastResult = getScanXHeaders(HandleTwo, &Result2, &Result2Length);
    std::cout << HandleTwo << ", " << LastResult << endl;
    std::cout << Result2;

    std::cout << "Closing Scanners: ";
    LastResult = closeScan(HandleOne);
    std::cout << LastResult << ", ";
    LastResult = closeScan(HandleTwo);
    std::cout << LastResult << endl;

    std::cout << "Scanners Closed" << endl;

    //// Heavy multi-thread testing now...

    Sleeper WaitATic(1000);
    std::cout << "Starting Pounder 1" << endl;
    Pounder Pounder1(BigGiantUglyMessage);
    for(int a = 0; a < 10; a++) {
        WaitATic();
        std:cout
          << "Pounder 1: " << Pounder1.NumberOfScans()
          << ", "  << Pounder1.HighestHandle() << endl;
    }

    std::cout << "Starting Pounder 2" << endl;
    Pounder Pounder2(BigGiantUglyMessage);
    for(int a = 0; a < 10; a++) {
        WaitATic();
        std::cout << "Pounder 1: " << Pounder1.NumberOfScans()
                  << ", "  << Pounder1.HighestHandle() << endl;

        std::cout << "Pounder 2: " << Pounder2.NumberOfScans()
                  << ", "  << Pounder2.HighestHandle() << endl;

    }

    std::cout << "Starting Pounder 3" << endl;
    Pounder Pounder3(BigGiantUglyMessage);
    for(int a = 0; a < 10; a++) {
        WaitATic();
        std::cout << "Pounder 1: " << Pounder1.NumberOfScans()
                  << ", "  << Pounder1.HighestHandle() << endl;

        std::cout << "Pounder 2: " << Pounder2.NumberOfScans()
                  << ", "  << Pounder2.HighestHandle() << endl;

        std::cout << "Pounder 3: " << Pounder3.NumberOfScans()
                  << ", "  << Pounder3.HighestHandle() << endl;

    }

    std::cout << "Starting Pounder 4" << endl;
    Pounder Pounder4(BigGiantUglyMessage);
    for(int a = 0; a < 10; a++) {
        WaitATic();
        std::cout << "Pounder 1: " << Pounder1.NumberOfScans()
                  << ", "  << Pounder1.HighestHandle() << endl;

        std::cout << "Pounder 2: " << Pounder2.NumberOfScans()
                  << ", "  << Pounder2.HighestHandle() << endl;

        std::cout << "Pounder 3: " << Pounder3.NumberOfScans()
                  << ", "  << Pounder3.HighestHandle() << endl;

        std::cout << "Pounder 4: " << Pounder4.NumberOfScans()
                  << ", "  << Pounder4.HighestHandle() << endl;

    }

    std::cout << "Starting Pounder 5" << endl;
    Pounder Pounder5(BigGiantUglyMessage);
    for(int a = 0; a < 10; a++) {
        WaitATic();
        std::cout << "Pounder 1: " << Pounder1.NumberOfScans()
                  << ", "  << Pounder1.HighestHandle() << endl;

        std::cout << "Pounder 2: " << Pounder2.NumberOfScans()
                  << ", "  << Pounder2.HighestHandle() << endl;

        std::cout << "Pounder 3: " << Pounder3.NumberOfScans()
                  << ", "  << Pounder3.HighestHandle() << endl;

        std::cout << "Pounder 4: " << Pounder4.NumberOfScans()
                  << ", "  << Pounder4.HighestHandle() << endl;

        std::cout << "Pounder 5: " << Pounder5.NumberOfScans()
                  << ", "  << Pounder5.HighestHandle() << endl;

    }

    std::cout << "Starting Pounder 6" << endl;
    Pounder Pounder6(BigGiantUglyMessage);
    for(int a = 0; a < 30; a++) {
        WaitATic();
        std::cout << "Pounder 1: " << Pounder1.NumberOfScans()
                  << ", "  << Pounder1.HighestHandle() << endl;

        std::cout << "Pounder 2: " << Pounder2.NumberOfScans()
                  << ", "  << Pounder2.HighestHandle() << endl;

        std::cout << "Pounder 3: " << Pounder3.NumberOfScans()
                  << ", "  << Pounder3.HighestHandle() << endl;

        std::cout << "Pounder 4: " << Pounder4.NumberOfScans()
                  << ", "  << Pounder4.HighestHandle() << endl;

        std::cout << "Pounder 5: " << Pounder5.NumberOfScans()
                  << ", "  << Pounder5.HighestHandle() << endl;

        std::cout << "Pounder 6: " << Pounder6.NumberOfScans()
                  << ", "  << Pounder6.HighestHandle() << endl;

    }

    std::cout << "Stopping All Pounders" << endl;

    Pounder1.stop(); std::cout << "1 stopped." << endl;
    Pounder2.stop(); std::cout << "2 stopped." << endl;
    Pounder3.stop(); std::cout << "3 stopped." << endl;
    Pounder4.stop(); std::cout << "4 stopped." << endl;
    Pounder5.stop(); std::cout << "5 stopped." << endl;
    Pounder6.stop(); std::cout << "6 stopped." << endl;

	std::cout << "shutdownSNF(): ";
	LastResult = shutdownSNF();
	std::cout << LastResult << std::endl;

	std::cout << "That's All Folks";

	return 0;
}
