/*
 * Copyright (c) 2003-2007 Rony Shapiro <ronys@users.sourceforge.net>.
 * All rights reserved. Use of the code is allowed under the
 * Artistic License 2.0 terms, as specified in the LICENSE file
 * distributed with this code, or available from
 * http://www.opensource.org/licenses/artistic-license-2.0.php
 */
#include "keysend.h"

#include  <Winable.h>

CKeySend::CKeySend(void) : m_delay(10)
{
	// get the locale of the current thread.
	// we are assuming that all window and threading in the 
	// current users desktop have the same locale.
	m_hlocale = GetKeyboardLayout(0);
}

CKeySend::~CKeySend(void)
{
}


void CKeySend::SendString(const CMyString &data)
{
  const int N = data.GetLength();

  for(int n=0;n<N;n++){
    SendChar(data[n]);
  }
}

void CKeySend::SendChar(TCHAR c)
{
  /*
   * "UNICODE" version works for WinXP on non-English keyboards, e.g., German
   * Non-Unicode version appears to work OK on all keyboard for Win98.
   * Since non-Unicode is currently released for Win98 support, this is Good
   * Enough.
   */
#ifdef UNICODE
  UINT status;
  INPUT input[1];
  input[0].type = INPUT_KEYBOARD;
  switch (c) {
  case TCHAR('\t'):
  case TCHAR('\r'):
    input[0].ki.wVk = c == TCHAR('\t') ? VK_TAB : VK_RETURN;
    input[0].ki.wScan = 0;
    input[0].ki.dwFlags = 0;
    break;
  default:
    input[0].ki.wVk = 0;
    input[0].ki.wScan = c;
    input[0].ki.dwFlags = KEYEVENTF_UNICODE;
    break;
  }  
  status = ::SendInput(1, input, sizeof(INPUT));
  if (status != 1)
    TRACE(_T("CKeySend::SendChar: SendInput failed\n"));
#else
  BOOL shiftDown=false; //assume shift key is up at start.
  BOOL ctrlDown=false;
  BOOL altDown=false;
  SHORT keyScanCode=VkKeyScanEx(c, m_hlocale );
  // high order byte of keyscancode indicates if SHIFT, CTRL etc keys should be down 
  if(keyScanCode & 0x100){
    shiftDown=true;      
    //send a shift down
    keybd_event(VK_SHIFT,  (BYTE) MapVirtualKeyEx(VK_SHIFT, 0, m_hlocale ), 0,3); //Fixes bug #1208955
  } 

  if(keyScanCode & 0x200){
    ctrlDown=true;       
    //send a ctrl down
    keybd_event(VK_CONTROL,  (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale ), KEYEVENTF_EXTENDEDKEY, 0); 
  } 

  if(keyScanCode & 0x400){
    altDown=true; 
    //send a alt down
    keybd_event(VK_MENU,  (BYTE) MapVirtualKeyEx(VK_MENU, 0, m_hlocale ), KEYEVENTF_EXTENDEDKEY, 0);    
  } 

  // the lower order byte has the key scan code we need.
  keyScanCode =(SHORT)( keyScanCode & 0xFF);

  keybd_event((BYTE)keyScanCode,  (BYTE) MapVirtualKeyEx(keyScanCode, 0, m_hlocale ), 0, 0);      
  keybd_event((BYTE)keyScanCode,  (BYTE) MapVirtualKeyEx(keyScanCode, 0, m_hlocale ), KEYEVENTF_KEYUP, 0);    

  if(shiftDown){
    //send a shift up
    keybd_event(VK_SHIFT,  (BYTE) MapVirtualKeyEx(VK_SHIFT, 0, m_hlocale ), KEYEVENTF_KEYUP, 3); //Fixes bug #1208955
    shiftDown=false;
  }

  if(ctrlDown){
                    
    //send a ctrl up
    keybd_event(VK_CONTROL,  (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale ), KEYEVENTF_KEYUP |KEYEVENTF_EXTENDEDKEY, 0); 
    ctrlDown=false;
  } 

  if(altDown){
    //send a alt up
    keybd_event(VK_MENU,  (BYTE) MapVirtualKeyEx(VK_MENU, 0, m_hlocale ), KEYEVENTF_KEYUP |KEYEVENTF_EXTENDEDKEY, 0); 
    altDown=false;       
  } 
  ::Sleep(m_delay);
#endif /* UNICODE */
  ::Sleep(m_delay);
}



void CKeySend::ResetKeyboardState()
{
	// We need to make sure that the Control Key is still not down. 
	// It will be down while the user presses ctrl-T the shortcut for autotype.

	BYTE keys[256];


	GetKeyboardState((LPBYTE)&keys);

	while((keys[VK_CONTROL] & 0x80)!=0){
		// VK_CONTROL is down so send a key down and an key up...

		keybd_event(VK_CONTROL, (BYTE)MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale), KEYEVENTF_EXTENDEDKEY, 0);

		keybd_event(VK_CONTROL,  (BYTE) MapVirtualKeyEx(VK_CONTROL, 0, m_hlocale), KEYEVENTF_KEYUP|KEYEVENTF_EXTENDEDKEY, 0);

		//now we let the messages be processed by the applications to set the keyboard state
		MSG msg;
		//BOOL m_bCancel=false;
		while (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE) )
		{
			// so there is a message process it.
			if (!AfxGetThread()->PumpMessage())
				break;
		}


		Sleep(10);
		memset((void*)&keys,0,256);
		GetKeyboardState((LPBYTE)&keys);
	}

}

// SetAndDelay allows users to put \d500\d10 in autotype and
// the it will cause a delay of half a second then subsequent
// key stokes will be delayed by 10 ms 
// thedavecollins 2004-08-05

void CKeySend::SetAndDelay(int d){
	SetDelay(d);
	::Sleep(m_delay);
}

void CKeySend::SetDelay(int d){
	m_delay=d;
}

void CKeySend::SetCapsLock(const bool bState)
{
  BYTE keyState[256];

  GetKeyboardState((LPBYTE)&keyState);
  if((bState && !(keyState[VK_CAPITAL] & 1)) ||
     (!bState && (keyState[VK_CAPITAL] & 1))) {
      // Simulate a key press
      keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
      // Simulate a key release
      keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
  }

  MSG msg;
  while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) ) {
    // so there is a message process it.
    if (!AfxGetThread()->PumpMessage())
      break;
  }
}
