// TypeAction.cpp : Implementation of cTypeAction
#include "stdafx.h"
#include "DecalInput.h"
#include "TypeAction.h"

/////////////////////////////////////////////////////////////////////////////
// cTypeAction

HRESULT cTypeAction::extractKey( LPTSTR &szIter, long nShiftState, IInputService *pService )
{
   HRESULT hRes;

   switch( *szIter )
   {
   case _T( '\0' ):
      return E_INVALIDARG;

   case _T( '{' ):
      // Make sure this is a good key
      {
         for( TCHAR *szChar = szIter + 1; *szChar != _T( '\0' ); ++ szChar )
         {
            if( *szChar == _T( '}' ) )
            {
               // Look up this character in the site table
               std::string strspecial( szIter + 1, szChar - ( szIter + 1 ) );
               long nVK;

               hRes = pService->get_KeyByName( _bstr_t( strspecial.c_str() ), &nVK );
               if( FAILED( hRes ) )
               {
                  _ASSERTE( FALSE );
                  return E_INVALIDARG;
               }

               cKey key = { nShiftState, nVK };
               m_keys.push_back( key );
               szIter = szChar + 1;
               break;
            }
         }

         if( *szChar == _T( '\0' ) )
         {
            // Unterminated constant
            return E_INVALIDARG;
         }
      }
      break;

   case _T( '<' ):
      {
         for( TCHAR *szChar = szIter + 1; *szChar != _T( '\0' ); ++ szChar )
         {
            if( *szChar == _T( '>' ) )
            {
               // Look up this character in the site table
               std::string strspecial( szIter + 1, szChar - ( szIter + 1 ) );
               long nVK;

               hRes = pService->get_CommandKey( _bstr_t( strspecial.c_str() ), &nVK );
               if( hRes != S_OK )
               {
                  _ASSERTE( FALSE );
                  return E_INVALIDARG;
               }

               cKey key = { nShiftState, nVK };
               m_keys.push_back( key );
               szIter = szChar + 1;
               break;
            }
         }

         if( *szChar == _T( '\0' ) )
         {
            // Unterminated constant
            return E_INVALIDARG;
         }
      }
  break;

   default:
      {
         // Convert the character
         WORD wVkScan = VkKeyScan( *szIter );

         cKey key = { nShiftState | ( wVkScan & SHIFT_MASK ), 
            wVkScan & VKEY_MASK };
         m_keys.push_back( key );

         ++ szIter;
      }
   }

   return S_OK;
}

HRESULT cTypeAction::onLoad( LPTSTR szData )
{
   USES_CONVERSION;

   typedef std::stack< long > cShiftStack;
   cShiftStack shifts;
   long nCurrentShift = 0;

   CComPtr< IInputService > pService;
   HRESULT hRes = m_pSite->get_Service( &pService );
   if( FAILED( hRes ) )
      return hRes;

   for( TCHAR *szIter = szData; *szIter != _T( '\0' ); )
   {
      switch( *szIter )
      {
      case _T( '+' ):
         if( szIter[ 1 ] == _T( '(' ) )
         {
            szIter += 2;
            shifts.push( nCurrentShift );
            nCurrentShift |= MASK_SHIFT;
         }
         else
         {
            ++ szIter;
            hRes = extractKey( szIter, nCurrentShift | MASK_SHIFT, pService );
            if( FAILED( hRes ) )
               return hRes;
         }
         break;

      case _T( '^' ):
         if( szIter[ 1 ] == _T( '(' ) )
         {
            szIter += 2;
            shifts.push( nCurrentShift );
            nCurrentShift |= MASK_CTRL;
         }
         else
         {
            ++ szIter;
            hRes = extractKey( szIter, nCurrentShift | MASK_SHIFT, pService );
            if( FAILED( hRes ) )
               return hRes;
         }
         break;

      case _T( '%' ):
         if( szIter[ 1 ] == _T( '(' ) )
         {
            szIter += 2;
            shifts.push( nCurrentShift );
            nCurrentShift |= MASK_ALT;
         }
         else
         {
            ++ szIter;
            hRes = extractKey( szIter, nCurrentShift | MASK_SHIFT, pService );
            if( FAILED( hRes ) )
               return hRes;
         }
         break;

      case _T( ')' ):
         {
            if( shifts.size() == 0 )
            {
               // Misplaced ')'
               _ASSERT( FALSE );
               return E_INVALIDARG;
            }

            nCurrentShift = shifts.top();
            shifts.pop();

            ++ szIter;
         }
         break;

      default:
         hRes = extractKey( szIter, nCurrentShift, pService );
         if( FAILED( hRes ) )
            return hRes;

         break;
      }
   }

   if( shifts.size() > 0 )
   {
      // Unterminated shift
      _ASSERT( FALSE );
      return E_INVALIDARG;
   }

   // Successful parsing WTG!
   return S_OK;
}

STDMETHODIMP cTypeAction::Execute()
{
   long nShift = 0;
   for( cKeyList::iterator i = m_keys.begin(); i != m_keys.end(); ++ i )
   {
      transitionShift( nShift, i->m_nShiftState );
      pressKey( i->m_nVK, true );
      pressKey( i->m_nVK, false );

      nShift = i->m_nShiftState;
   }

   // Make sure all shift keys are released
   transitionShift( nShift, 0 );
   flushInput();

   return S_OK;
}

STDMETHODIMP cTypeAction::Push()
{
   doInput( m_keys.begin(), m_keys.end(), true );

   return S_OK;
}

STDMETHODIMP cTypeAction::Pop()
{
   doInput( m_keys.rbegin(), m_keys.rend(), false );

   return S_OK;
}
