/***********************************************************************
*
*       ELMER, A Computational Fluid Dynamics Program.
*
*       Copyright 1st April 1995 - , Center for Scientific Computing,
*                                    Finland.
*
*       All rights reserved. No part of this program may be used,
*       reproduced or transmitted in any form or by any means
*       without the written permission of CSC.
*
*                Address: Center for Scientific Computing
*                         Tietotie 6, P.O. BOX 405
*                         02101 Espoo, Finland
*                         Tel.     +358 0 457 2001
*                         Telefax: +358 0 457 2302
*                         EMail:   Jari.Jarvinen@csc.fi
************************************************************************/

/***********************************************************************
Program:    ELMER Front
Module:     ecif_process_WIN32.hpp
Language:   C++
Date:       20.01.99
Version:    1.00
Author(s):  Martti Verho
Revisions:

Abstract:   Implementation, Win32 specific

************************************************************************/


// Helper routines
// ===============


void display_system_msg(char* header)
{
  LPVOID lpMsgBuf;
  FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                 GetLastError(),
                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                 (LPTSTR) &lpMsgBuf,    0,    NULL );// Display the string.

  MessageBox( NULL, (const char*)lpMsgBuf, header, MB_OK|MB_ICONINFORMATION );
  // Free the buffer.
  LocalFree( lpMsgBuf );
}


void display_msg(char* header, char* msg)
{
  MessageBox( NULL, (const char*)msg, header, MB_OK|MB_ICONINFORMATION );
}


BOOL CtrlHandler(DWORD fdwCtrlType)
{
  switch (fdwCtrlType) {

  case CTRL_C_EVENT:
    Beep(1000, 500);
    display_system_msg("Ctrl-C: Terminating process!");
    return FALSE;

  case CTRL_BREAK_EVENT:
    Beep(1000, 1000);
    display_system_msg("Ctrl-Break: Terminating process!");
    return FALSE;

  case CTRL_CLOSE_EVENT:
    Beep(1000, 2000);
    display_system_msg("Close: Terminating process!");
    return FALSE;

  case CTRL_LOGOFF_EVENT:
  case CTRL_SHUTDOWN_EVENT:
  default:
    return FALSE;
  }
}



// Console settings, system specific
// Called in main.cpp
//
void initConsole()
{
  BOOL rc = TRUE;

  //rc = SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);

  if (rc == FALSE)
    display_system_msg("SetConsoleCtrlHandler");
}


// =====================
// Process class methods
// =====================


bool
Process::exists()
{
  DWORD exit_code;

  BOOL rc =  GetExitCodeProcess(processHandle, &exit_code);

  if ( !rc ) {
    display_system_msg("Checking process existence (GetExitCodeProcess)");
    return false;
  }

  if ( exit_code != STILL_ACTIVE ) {
    return false;

  } else {
    return true;
  }
}


bool
Process::resume()
{
  BOOL rc;

  rc = ResumeThread(threadHandle);

  if (rc == 0xFFFFFFFF) {
    display_system_msg("Resume thread");
    return false;
   }

  return true;
}


Hfile
Process::setLogfile()
{
  if (logfileName == NULL ||
      logfileName[0] == '\0'
     )
    return 0;

  // Create file (for "redirection")
  // ===============================
  // Create inheritable file object handle
  SECURITY_ATTRIBUTES saAttr;
  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  saAttr.bInheritHandle = TRUE;
  saAttr.lpSecurityDescriptor = NULL;

  HANDLE outfile = CreateFile( logfileName,
                               GENERIC_READ | GENERIC_WRITE,
                               FILE_SHARE_READ | FILE_SHARE_WRITE,
                               &saAttr,
                               CREATE_ALWAYS,
                               FILE_ATTRIBUTE_NORMAL,
                               //FILE_FLAG_NO_BUFFERING,
                               //FILE_FLAG_WRITE_THROUGH,
                               NULL);

  if (outfile == INVALID_HANDLE_VALUE) {
    display_system_msg("Creating process logfile");
    return 0;
  }

  return outfile;
}


// ======================
// File based redirection
// ======================
#if 1

bool
Process::start()
{
  BOOL rc;

  bool success_flag = false;

  DWORD winmode; // Window mode for Startupinfo
  DWORD stdmode; // Stdoutput flag use mode

  HANDLE hStdoutRd, hChildStdoutWr;

  hChildStdoutWr = setLogfile();

  // No output,
  if ( !showConsole && hChildStdoutWr == 0) {
    winmode = SW_HIDE;
    stdmode = 0;

  // Output to console
  // Console will be created for the parent, so we can get
  // a handle fore it!
  } else if (showConsole) {
    winmode = SW_SHOW;
    stdmode = 0;

  // Output to file
  // Console will be created for the child
  // and then redirected to a file!
  } else {
    winmode = SW_HIDE;
    stdmode = STARTF_USESTDHANDLES;
  }

  outputHandle = hChildStdoutWr;

  // Set process parameters for the child process
  // ====================================
  STARTUPINFO si;

  GetStartupInfo(&si);
  si.lpTitle = NULL;
  si.dwFlags = STARTF_USESHOWWINDOW | stdmode;
  si.hStdOutput = hChildStdoutWr;
  si.hStdError = hChildStdoutWr;
  si.wShowWindow = winmode;

  DWORD pclass;

  switch (priority) {

  case ECIF_LOW_PRIORITY:
    pclass = IDLE_PRIORITY_CLASS;
    break;

  case ECIF_LOWER_THAN_NORMAL_PRIORITY:
    pclass = IDLE_PRIORITY_CLASS;
    //pclass = BELOW_NORMAL_PRIORITY_CLASS;  // NOTE: This is W2000 class

  case ECIF_NORMAL_PRIORITY:
    pclass = NORMAL_PRIORITY_CLASS;
    break;

  case ECIF_HIGHER_THAN_NORMAL_PRIORITY:
    pclass = NORMAL_PRIORITY_CLASS;
    //pclass = ABOVE_NORMAL_PRIORITY_CLASS;  // NOTE: This is W2000 class
    break;

  case ECIF_HIGH_PRIORITY:
    pclass = HIGH_PRIORITY_CLASS;
    break;

  default:
    pclass = NORMAL_PRIORITY_CLASS;
  }

  PROCESS_INFORMATION pi;
  DWORD flags = CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP | pclass;

  // Create process
  // ==============
  strstream strm;
  strm << command << " " << arguments << ends;

  // NOTE: When using this calling format, PATH is used to find
  // the executable, needed for F90 compiler call!
  rc = CreateProcess( NULL, strm.str(),
                      NULL, NULL,
                      TRUE,       // inherit handles
                      flags,      // creation flags
                      NULL, NULL, // use callers environment and cur-dir
                      &si, &pi);

  if (rc == FALSE) {
    strstream strm;

    if ( name != NULL ) {
      strm << "Starting process: " << command << " " << name << ends;
    } else {
      strm << "Starting process: " << command  << ends;
    }

    display_system_msg(strm.str());
    return false;

  } else {
    processId = pi.dwProcessId;
    processHandle = pi.hProcess;

    threadHandle = pi.hThread;
    threadId = pi.dwThreadId;

    success_flag = true;

  } // if process started

  return success_flag;
}
#endif



// ======================
// Pipe based redirection
// ======================
#if 0

bool
Process::start()
{
  char* consoleTitle = "MyTestConsole";

  BOOL rc;
  HANDLE hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup, hSaveStdout;

  bool success_flag = false;

  DWORD winmode; // Window mode for Startupinfo
  DWORD stdmode; // Stdoutput flag use mode

  SECURITY_ATTRIBUTES saAttr;
  // Set the bInheritHandle flag so pipe handles are inherited.
  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  saAttr.bInheritHandle = TRUE;
  saAttr.lpSecurityDescriptor = NULL;

  // The steps for redirecting child process's STDOUT:
  //  1. Save current STDOUT, to be restored later.
  //  2. Create anonymous pipe to be STDOUT for child process.
  //  3. Set STDOUT of the parent process to be write handle to
  //     the pipe, so it is inherited by the child process.
  //  4. Create a noninheritable duplicate of the read handle and
  //     close the inheritable read handle.

  // Save the handle to the current STDOUT.
  hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

  // Create a pipe for the child process's STDOUT.
  if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
    display_system_msg("Stdout pipe creation");

  // Set a write handle to the pipe to be STDOUT.

  if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
    display_system_msg("Redirecting STDOUT");

  // Create noninheritable read handle and close the inheritable read
  // handle.
  rc = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
                       GetCurrentProcess(), &hChildStdoutRdDup , 0,
                       FALSE,
                       DUPLICATE_SAME_ACCESS);

  if( !rc )
    display_system_msg("DuplicateHandle");

  CloseHandle(hChildStdoutRd);

  bool silent = false;

  // No output,
  if ( !showConsole && silent) {
    winmode = SW_HIDE;
    stdmode = 0;

  // Output to console
  // Console will be created for the parent, so we can get
  // a handle fore it!
  } else if (showConsole) {
    winmode = SW_SHOW;
    stdmode = 0;

  // Output to file
  // Console will be created for the child
  // and then redirected to a file!
  } else {
    winmode = SW_HIDE;
    stdmode = STARTF_USESTDHANDLES;
  }

  // Set process parameters for the child process
  // ====================================
  STARTUPINFO si;
  //si.cb = sizeof(si);
  GetStartupInfo(&si);
  si.lpTitle = NULL;
  //si.lpTitle = consoleTitle;
  si.dwFlags = STARTF_USESHOWWINDOW | stdmode;
  si.hStdOutput = hChildStdoutWr;
  si.hStdError = hChildStdoutWr;
  si.wShowWindow = winmode;

  outputHandle = hChildStdoutRdDup;

  PROCESS_INFORMATION pi;
  DWORD flags = CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP | IDLE_PRIORITY_CLASS;
  //DWORD flags = CREATE_NEW_CONSOLE | IDLE_PRIORITY_CLASS;
  //DWORD flags = DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | IDLE_PRIORITY_CLASS;

  // Create process
  // ==============

  rc = CreateProcess( command, arguments,
                      NULL, NULL,
                      TRUE,   // inherit handles
                      flags,  // creation flags
                      NULL, NULL,
                      &si, &pi);
  if (rc == FALSE) {
    display_system_msg("Create process");
  }
  else {
    processId = pi.dwProcessId;
    processHandle = pi.hProcess;

    threadHandle = pi.hThread;
    threadId = pi.dwThreadId;

#if 0
    HWND wHandle = FindWindowEx( NULL, NULL, "ConsoleWindowClass",  consoleTitle);
    if (wHandle == FALSE) {
      display_system_msg("Find Window");
    }

    rc = PostMessage( wHandle, WM_CLOSE, 0 ,0 );
    if (rc == FALSE) {
      display_system_msg("Post Msg");
    }

#endif

    //rc = SuspendThread(threadHandle);
    //rc = TerminateThread(threadHandle, 0);

    //if (rc == 0) {
    //  display_system_msg("Terminate thread");
    //}

    //Sleep(500);

    success_flag = true;

  } // if process started


  return success_flag;
}
#endif


bool
Process::stop()
{
  int rc;
  rc = TerminateProcess(processHandle, 0);

  if (rc == 0) {
    return false;
   }

  CloseHandle(threadHandle);
  CloseHandle(processHandle);


  CloseHandle(outputHandle);

  return true;
}


bool
Process::suspend()
{
  BOOL rc;

  rc = SuspendThread(threadHandle);

  if (rc == 0xFFFFFFFF) {
    display_system_msg("Suspend thread");
    return false;
   }

  return true;
}


void
Process::setPriorityLevel(priorityLevel level)
{
  priority = level;

  switch (level) {
  case ECIF_LOW_PRIORITY:
    SetPriorityClass(processHandle, IDLE_PRIORITY_CLASS);
    break;
  case ECIF_NORMAL_PRIORITY:
    SetPriorityClass(processHandle, NORMAL_PRIORITY_CLASS);
    break;
  case ECIF_HIGH_PRIORITY:
    SetPriorityClass(processHandle, HIGH_PRIORITY_CLASS);
    break;
  }
}


