/**
 * This file is part of the kpimutils library.
 *
 * Copyright (C) 2008 Jarosław Staniek <staniek@kde.org>
 * Copyright (C) 2012 Andre Heinecke <aheinecke@intevation.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */
/**
  @file
  This file is part of the KDEPIM Utilities library and provides
  static methods for process handling.

  @author Jarosław Staniek \<staniek@kde.org\>
*/

#include "processes.h"
using namespace KPIMUtils;

#ifdef Q_WS_WIN

#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <signal.h>
#include <unistd.h>

#ifdef Q_OS_WINCE
#include <Tlhelp32.h>
#endif

#include <QtCore/QList>
#include <QtCore/QtDebug>

#include <KDebug>

// Copy from kdelibs/kinit/kinit_win.cpp
PSID copySid(PSID from)
{
    if (!from)
        return 0;
    int sidLength = GetLengthSid(from);
    PSID to = (PSID) malloc(sidLength);
    CopySid(sidLength, to, from);
    return to;
}

// Copy from kdelibs/kinit/kinit_win.cpp
static PSID getProcessOwner(HANDLE hProcess)
{
#ifndef _WIN32_WCE
    HANDLE hToken = NULL;
    PSID sid;

    OpenProcessToken(hProcess, TOKEN_READ, &hToken);
    if(hToken)
    {
        DWORD size;
        PTOKEN_USER userStruct;

        // check how much space is needed
        GetTokenInformation(hToken, TokenUser, NULL, 0, &size);
        if( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
        {
            userStruct = reinterpret_cast<PTOKEN_USER>( new BYTE[size] );
            GetTokenInformation(hToken, TokenUser, userStruct, size, &size);

            sid = copySid(userStruct->User.Sid);
            CloseHandle(hToken);
            delete [] userStruct;
            return sid;
        }
    }
#endif
    return 0;
}

// Copy from kdelibs/kinit/kinit_win.cpp
static HANDLE getProcessHandle(int processID)
{
    return OpenProcess( SYNCHRONIZE|PROCESS_QUERY_INFORMATION |
                        PROCESS_VM_READ | PROCESS_TERMINATE,
                        false, processID );
}

void KPIMUtils::getProcessesIdForName( const QString &processName, QList<int> &pids )
{
  HANDLE h;
  PROCESSENTRY32 pe32;

  h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
  if ( h == INVALID_HANDLE_VALUE ) {
    return;
  }

  pe32.dwSize = sizeof(PROCESSENTRY32); // Necessary according to MSDN
  if ( !Process32First( h, &pe32 ) ) {
    return;
  }

  pids.clear();

  do {
    if ( QString::fromWCharArray( pe32.szExeFile ) == processName ) {
      PSID user_sid = getProcessOwner( GetCurrentProcess() );
      if ( user_sid ) {
        // Also check that we are the Owner of that process
        HANDLE hProcess = getProcessHandle( pe32.th32ProcessID );
        if (!hProcess) {
          continue;
        }

        PSID sid = getProcessOwner( hProcess );
        PSID userSid = getProcessOwner( GetCurrentProcess() );
        if (!sid || userSid && !EqualSid( userSid, sid )) {
          free ( sid );
          continue;
        }
      }
      pids.append( (int)pe32.th32ProcessID );
      kDebug() << "found PID: " << (int)pe32.th32ProcessID;
    }
  } while( Process32Next( h, &pe32 ) );
#ifndef _WIN32_WCE
    CloseHandle(h);
#else
    CloseToolhelp32Snapshot(h);
#endif
}

bool KPIMUtils::otherProcessesExist( const QString &processName )
{
  QList<int> pids;
  getProcessesIdForName( processName, pids );
  int myPid = getpid();
  foreach ( int pid, pids ) {
    if ( myPid != pid ) {
//      kDebug() << "Process ID is " << pid;
      return true;
    }
  }
  return false;
}

bool KPIMUtils::killProcesses( const QString &processName )
{
  QList<int> pids;
  getProcessesIdForName( processName, pids );
  if ( pids.empty() ) {
    return true;
  }

  qWarning() << "Killing process \"" << processName << " (pid=" << pids[0] << ")..";
  int overallResult = 0;
  foreach ( int pid, pids ) {
    int result;
#ifndef _WIN32_WCE
    result = kill( pid, SIGTERM );
    if ( result == 0 ) {
      continue;
    }
#endif
    result = kill( pid, SIGKILL );
    if ( result != 0 ) {
      overallResult = result;
    }
  }
  return overallResult == 0;
}

struct EnumWindowsStruct
{
  EnumWindowsStruct() : windowId( 0 ) {}
  int pid;
  HWND windowId;
};

BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam )
{
  if ( GetWindowLong( hwnd, GWL_STYLE ) & WS_VISIBLE ) {

    DWORD pidwin;

    GetWindowThreadProcessId( hwnd, &pidwin );
    if ( pidwin == ( (EnumWindowsStruct *)lParam )->pid ) {
      ( (EnumWindowsStruct *)lParam )->windowId = hwnd;
      return FALSE; //krazy:exclude=captruefalse
    }
  }
  return TRUE; //krazy:exclude=captruefalse
}

void KPIMUtils::activateWindowForProcess( const QString &executableName )
{
  QList<int> pids;
  KPIMUtils::getProcessesIdForName( executableName, pids );
  int myPid = getpid();
  int foundPid = 0;
  foreach ( int pid, pids ) {
    if ( myPid != pid ) {
      kDebug() << "activateWindowForProcess(): PID to activate:" << pid;
      foundPid = pid;
      break;
    }
  }
  if ( foundPid == 0 ) {
    return;
  }
  EnumWindowsStruct winStruct;
  winStruct.pid = foundPid;
  EnumWindows( EnumWindowsProc, (LPARAM)&winStruct );
  if ( winStruct.windowId == 0 ) {
    return;
  }
  SetForegroundWindow( winStruct.windowId );
}

#endif // Q_WS_WIN
