//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd 2013  (all rights reserved)
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  Provided no fee is charged (either by the distributer or user)
//  permission to use, copy, modify, and distribute this software and
//  its documentation for any purpose and without fee is hereby granted,
//  provided that the above copyright notice appear in all copies and
//  that both that copyright notice and this permission notice appear
//  in supporting documentation.
//
//  No waranty or suitablity for purpose is implied.
//
//
//  SQLmapi definitions and prototypes
//  NOTES: Implementated as either Windows service or console application 
//

#include "stdafx.h"
#include "P2Pwin32.h"
#include "SQLmapi.h"
#include "SQLmapiHub.h"
#include "P2Pmsg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
using namespace std;

///////////////////////////////////////////////////////////////////////
// AKAmapi SERVICE handlers
#define _TSQLmapi _T("P2Pmsgservice")
SQLmapi *g_pSQLmapi = 0;

VOID WINAPI
SQLmapiServiceStart ( DWORD argc, LPTSTR *argv )
{
    g_pSQLmapi -> Service ( argc, argv );
}

VOID WINAPI
SQLmapiServiceHandler ( DWORD dwControl )
{
    g_pSQLmapi -> ServiceCtrlHandler ( dwControl );
}

////////////////////////////////////////////////////////////////////////
//  Windows service and console application object
//
int
_tmain ( int argc, TCHAR *argv[], TCHAR *envp[] )
{
    // Locals and global dependancies
    SERVICE_TABLE_ENTRY  DispatchTable[]
    = {  { _TSQLmapi, SQLmapiServiceStart }
      ,  { NULL,           NULL } };
    StartupP2Pevent ( );
    
    g_pSQLmapi = new SQLmapi ( _TSQLmapi, SQLmapiServiceStart, SQLmapiServiceHandler );
    g_pSQLmapi -> PostP2PeerHub ( new SQLmapiHub(_T("SQLmapi")) );

    // Preprocess command arguments for both validation and synchronisation
    // NOTES: Normally services run as services.  But during development from
    //        from time to time the application will be run from Visual Studio
    //        or from a DOS window.
    //      : Hence the application will be run as a service whenever
    //        the -C[onsole] switch is not present.
    //      : Remember to include the -C[onsole] argument switch in AKAmapi
    //        Property Pages >> Configuration Properties >> Debugging >> Command Arguments
    for ( DWORD a = 1; a < (DWORD)argc; a++ )
    {
      if ( g_pSQLmapi->ServiceMainArgs(argv,a,false) )
        continue;
      if ( g_pSQLmapi->DefaultMainArgs(argv,a,false) )
        continue;
      if ( g_pSQLmapi->HelpMainArgs(argv,a,false) )
        continue;
     _tprintf ( _T("%s: [%s] Unknown command argument\n")
              , _TSQLmapi, argv[a] );
     _tprintf ( _T("%s: Application terminated\n")
              , _TSQLmapi );
      g_pSQLmapi -> SetExitMode ( 1 );
    }

    // Run as traditional service
    // NOTES: Prior error(s) or the -Exit command argument activate
    //        exit mode in which case control simply falls through
    if ( g_pSQLmapi->IsServiceMode() )
    {
      if ( !g_pSQLmapi->IsExitMode() )
        g_pSQLmapi -> StartDispatcher ( DispatchTable );
    }

    // Run as a console application
    else if ( g_pSQLmapi->IsConsoleMode() )
    {
      // Process execution arguments from console environment
      for ( DWORD a = 1; a < (DWORD)argc; a++ )
      {
        if ( g_pSQLmapi->ServiceMainArgs(argv,a,true) )
          continue;
        if ( g_pSQLmapi->DefaultMainArgs(argv,a,true) )
          continue;
        if ( g_pSQLmapi->HelpMainArgs(argv,a,true) )
          continue;
       _tprintf ( _T("%s: [%s] Unknown command argument\n")
                , _TSQLmapi, argv[a] );
       _tprintf ( _T("%s: Application terminated\n")
                , _TSQLmapi );
        g_pSQLmapi -> SetExitMode ( 1 );
      }

      if ( !g_pSQLmapi->IsExitMode() )
        g_pSQLmapi -> Run ( );    // Delegates to RunConsoleModeAttachment()
    }

    // Tidy up, and
    DWORD dwExitCode = g_pSQLmapi -> GetExitCode();
    delete g_pSQLmapi;
    return dwExitCode;
}

//
//  Performs AKAmapi processing functions
//

///////////////////////////////////////////////////////////////////////
//  Constructors and destructor

SQLmapi::SQLmapi ( LPCTSTR lpszServicename
                 , LPSERVICE_MAIN_FUNCTION pftnServiceMain
                 , LPHANDLER_FUNCTION pftnServiceControl ) 
            : P2PeerService ( lpszServicename, pftnServiceMain, pftnServiceControl )
{
    // Firstly
    m_nIPort = 80;
}

SQLmapi::~SQLmapi ( )
{
}

//
//  Checks for and processes the customised service arguments
//  NOTES: Handles the customised service argument set
//
//  Parameters: LPTSTR argv[]
//              Command list containing argument to be processed
//
//              DWORD& argn
//              Argument to be processed, incremented accordingly
//     
//              bool
//              Implementation flag
//                true... Implement decoded option
//                false.. Validation and synchronisation
//
//  Returns:    bool
//              Parsing summary
//                true... Processed known argument
//                false.. Unknown argument
bool
SQLmapi::ServiceMainArgs ( LPTSTR argv[], DWORD& argn, bool bImplement )
{
    // Locals
    bool bResult = false;
    int  a = argn, b = argn;
    size_t iSize = _tcslen ( argv[a] );

    // Problematic
    try
    {
      // Listening port
      if ( _tcsnicmp(argv[a],_T("-Mode"),max(3,iSize)) == 0 ||
           _tcsnicmp(argv[a],_T("/Mode"),max(3,iSize)) == 0    )
      {
        bResult = true;
        DWORD nPort = _ttol(argv[++b]);
        if ( nPort < 1          ||
             nPort > USHRT_MAX     )
        {
         _tprintf ( _T("Invalid operational mode [%s], expected range (SendCMD, SendSQL or RecvSQL)"), nPort, USHRT_MAX );
         _tprintf ( _T("  -IPo[rt] nnnnn") );
          SetExitMode ( 1 );
        }
        else if ( bImplement )
          m_nIPort = (short)nPort;
      }
    }

    // Exceptions
    catch ( ... )
    {
     _tprintf ( _T("Unknown exception: %s"), argv );
      SetExitMode ( 1 );
    }

    // Tidy up, and
    if ( bResult )
      argn = b;
    return bResult;
}

//
//  Checks for and processes -Help service arguments
//  NOTES: The contained argument set defines the default P2PeerService
//         -Help environment
//
//  Parameters: LPTSTR argv[]
//              Argument to be processed
//
//              DWORD& argn
//              Primary argument to be checked
//
//              bool bImplement
//                true... Implement decoded option
//                false.. Validation and object synchronisation
bool
SQLmapi::HelpMainArgs ( LPTSTR argv[], DWORD& argn, bool bImplement )
{
    // Locals
    bool bResult = false;
    int  a = argn, b = argn;
    int  iSize   = _tcslen ( argv[a] );

    // Because users are involved
    try
    {
      // Help
      // NOTES: Simple TTY expression of default set of execution arguments
      if ( _tcsnicmp ( argv[a], _T("-Help"), max(2,iSize) ) == 0 ||
           _tcsnicmp ( argv[a], _T("/Help"), max(2,iSize) ) == 0 ||
           _tcsnicmp ( argv[a], _T("-?"), 2 )               == 0 ||
           _tcsnicmp ( argv[a], _T("/?"), 2 )               == 0    )
      {
        if ( bImplement )
        {
          _tprintf ( _T("\n") );
          _tprintf ( _T("Ivyware (c) SQLmapi execution command arguments\n") );
          _tprintf ( _T("  -IPo[rt] nnnnn\n") );
          _tprintf ( _T("   Specifies the IP-Port on which SQLmapi listens for HTTP requests\n") );
        }
      __super::HelpMainArgs ( argv, argn, bImplement );
        bResult = true;
      }
    }

    // Exceptions
    catch ( ... )
    {
      EVERR->MODULE
           ->AFP(*argv)->AFP(argn)->AFP(bImplement)
           ->Message(_T("Unknown exception"))
           ->SetLast();
    }

    // Tidy up and
    if ( bResult )
      argn = b;                        // Consumed arguments
    return bResult;
}


int
SQLmapi::RunConsoleModeAttachment ( )
{
    // Locals
    int   nRetCode = 0;
    char  cOption  = 0;
    short  nIPort  =  17080;
    
    // Assume problematic environment
TOP:try
    {
      // Until terminated
      while ( cOption != _T('Q') &&
              cOption != _T('q')    )
      {
        UINT nHubID = GetP2PeerHub() -> GetHubID();
        UINT nPause = nHubID > 0 ? GetP2PmsgHubConCount ( nHubID ) : 0;
        cout << std::endl;
        cout << "(SQLmapi) Select option from:" << std::endl;
        if ( nHubID <= 0 )
          cout << " 1. Start Hub" << std::endl;
        if ( nHubID > 0 )
          cout << " 2. Stop Hub [HibID=" << nHubID << "]" << std::endl;
        if ( nPause )
          cout << " 3. Pause Hub" << std::endl;
        if ( nHubID > 0 && nPause == 0 )
          cout << " 4. Continue Hub" << std::endl;
        //cout << " 5. Spare5 [" << szSpare2 << "]" << std::endl;
        //cout << " 6. Spare6 [" << szSpare6 << "]" << std::endl;
        //cout << " 7. Spare7" << std::endl;
        cout << " 8. Toggle debug" << std::endl;
        cout << " R. Refresh" << std::endl;
        cout << " Q. Quit application" << std::endl;

        cout << "Option:";
        cin  >> cOption;
        cout << std::endl;
        nHubID = GetP2PeerHub() -> GetHubID();
        nPause = nHubID > 0 ? GetP2PmsgHubConCount ( nHubID ) : 0;

        // Start hub
        if ( cOption == _T('1') &&
             nHubID  <=  0         )
          GetP2PeerHub() -> SpawnHub ( );

        // Stop hub
        if ( cOption == _T('2') ||
             cOption == _T('Q') ||
             cOption == _T('q')    )
          GetP2PeerHub() -> CloseHub ( );

        // Pause hub by delegating to service equivalent
        if (  cOption == _T('3') &&
              nHubID  >      0   &&
              nPause                 )
        {
          nPause = 1;
          GetP2PeerHub() -> PauseHub ( );
        }

        // Continue hub by delegating to service equivalent
        if (  cOption == _T('4') &&
              nHubID  >      0   &&
             !nPause                 )
          GetP2PeerHub() -> WakeupHub ( );

        // Toggle debug mode
        // NOTES: Results are asynchronously displayed in the
        //        P2PeerSQLconsole hub.  Since the main() thread does
        //        not integrate seamlessly into a P2PeerHub virtual
        //        network
        if ( cOption == '8' )
        {
          P2Pevent::Configure ( P2Pevent::ADDMASK
                              , P2Pevotn_ERROR | P2Pevotn_DEBUG );
        }
      }
    }

    // Exceptions
    catch ( P2Pevent *pEVT )
    {
      pEVT->Cancel ( );
      goto TOP;
    }

    // Tidy up, and
	  return nRetCode;
}
