/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/

//////////////////////////////////////////////////////////////////////
// AccActionBase.cpp: implementation of the CAccActionBase class.
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"

#include "AccActionBase.h"
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/accessibility/XAccessibleContext.hpp>

#include "AccessibleKeyStroke.h"

#ifndef __ACCCOMMON_H_
#include "acccommon.h"
#endif

using namespace com::sun::star::accessibility::AccessibleRole;
using namespace com::sun::star::accessibility;
using namespace com::sun::star::uno;
using namespace com::sun::star::awt;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAccActionBase::CAccActionBase()
{}

CAccActionBase::~CAccActionBase()
{}

/**
 * Helper function used for getting default action by UNO role.
 *
 * @param    pRContext    UNO context interface pointer.
 * @param    pRet         the corresponding string to be returned.
 */
void GetDfActionByUNORole(XAccessibleContext* pRContext, BSTR* pRet)
{
    // #CHECK#
    if(pRContext == NULL || pRet == NULL)
    {
        return;
    }

    long Role = pRContext->getAccessibleRole();

    switch(Role)
    {
    case PUSH_BUTTON:
        *pRet = ::SysAllocString(PRESS);
        break;
    case RADIO_BUTTON:
    case MENU_ITEM:
    case LIST_ITEM:
        *pRet = ::SysAllocString(SELECT);
        break;
    case CHECK_BOX:
        {
            Reference< XAccessibleStateSet > pRState = pRContext->getAccessibleStateSet();
            if( !pRState.is() )
            {
                return;
            }

            Sequence<short> pStates = pRState->getStates();
            int count = pStates.getLength();
            *pRet = ::SysAllocString(CHECK);
            for( int iIndex = 0;iIndex < count;iIndex++ )
            {
                if( pStates[iIndex] == AccessibleStateType::CHECKED )
                {
                    SAFE_SYSFREESTRING(*pRet);
                    *pRet = ::SysAllocString(UNCHECK);
                    break;
                }
            }
            break;
        }
    }
}

/**
 * Returns the number of action.
 *
 * @param    nActions    the number of action.
 */
STDMETHODIMP CAccActionBase::nActions(/*[out,retval]*/long* nActions)
{
    
	CHECK_ENABLE_INF

    ENTER_PROTECTED_BLOCK

    // #CHECK#
    if( pRXAct.is() && nActions != NULL )
    {
        *nActions = GetXInterface()->getAccessibleActionCount();
        return S_OK;
    }
    *nActions = 0;

    return S_OK;

    LEAVE_PROTECTED_BLOCK
}

/**
 * Performs specified action on the object.
 *
 * @param    actionIndex    the index of action.
 */
STDMETHODIMP CAccActionBase::doAction(/* [in] */ long actionIndex)
{
    
	CHECK_ENABLE_INF

    ENTER_PROTECTED_BLOCK

    if( pRXAct.is() )
    {
        return GetXInterface()->doAccessibleAction( actionIndex )?S_OK:E_FAIL;
    }
    return E_FAIL;

    LEAVE_PROTECTED_BLOCK
}

/**
 * Gets description of specified action.
 *
 * @param    actionIndex    the index of action.
 * @param    description    the description string of the specified action.
 */
STDMETHODIMP CAccActionBase::get_description(long actionIndex,BSTR __RPC_FAR *description)
{
    
	CHECK_ENABLE_INF

    ENTER_PROTECTED_BLOCK

    // #CHECK#
    if(description == NULL)
        return E_INVALIDARG;

    // #CHECK XInterface#
    if(!pRXAct.is())
        return E_FAIL;

    ::rtl::OUString ouStr = GetXInterface()->getAccessibleActionDescription(actionIndex);
    // #CHECK#

    SAFE_SYSFREESTRING(*description);
    *description = SysAllocString((OLECHAR*)ouStr.getStr());

    return S_OK;

    LEAVE_PROTECTED_BLOCK
}

STDMETHODIMP CAccActionBase::get_name( long, BSTR __RPC_FAR *)
{
    return E_NOTIMPL;
}

STDMETHODIMP CAccActionBase::get_localizedName( long, BSTR __RPC_FAR *)
{
    return E_NOTIMPL;
}

/**
 * Returns key binding object (if any) associated with specified action 
 * key binding is string.
 * e.g. "alt+d" (like IAccessible::get_accKeyboardShortcut).
 *
 * @param    actionIndex    the index of action.
 * @param    nMaxBinding    the max number of key binding.
 * @param    keyBinding     the key binding array.
 * @param    nBinding       the actual number of key binding returned.
 */
STDMETHODIMP CAccActionBase::get_keyBinding(
    /* [in] */ long actionIndex,
    /* [in] */ long,
    /* [length_is][length_is][size_is][size_is][out] */ BSTR __RPC_FAR *__RPC_FAR *keyBinding,
    /* [retval][out] */ long __RPC_FAR *nBinding)
{
    
	CHECK_ENABLE_INF

    ENTER_PROTECTED_BLOCK

    if( !keyBinding || !nBinding)
        return E_INVALIDARG;

    if( !pRXAct.is() )
        return E_FAIL;

    Reference< XAccessibleKeyBinding > binding = GetXInterface()->getAccessibleActionKeyBinding(actionIndex);
    if( !binding.is() )
        return E_FAIL;

    long nCount = (binding.get())->getAccessibleKeyBindingCount();

    OLECHAR wString[64];

    *keyBinding = (BSTR*)::CoTaskMemAlloc(nCount*sizeof(BSTR));

    // #CHECK Memory Allocation#
    if(*keyBinding == NULL)
        return E_FAIL;

    for( int index = 0;index < nCount;index++ )
    {
        memset(wString,0,sizeof(wString));
        GetkeyBindingStrByXkeyBinding( (binding.get())->getAccessibleKeyBinding(index), wString );

        (*keyBinding)[index] = SysAllocString(wString);
    }

    *nBinding = nCount;
    return S_OK;

    LEAVE_PROTECTED_BLOCK
}

/**
 * Overide of IUNOXWrapper.
 *
 * @param    pXInterface    the pointer of UNO interface.
 */
STDMETHODIMP CAccActionBase::put_XInterface(long pXInterface)
{
    

    ENTER_PROTECTED_BLOCK

    CUNOXWrapper::put_XInterface(pXInterface);

    //special query.
    if(pUNOInterface == NULL)
        return E_FAIL;
    Reference<XAccessibleContext> pRContext = pUNOInterface->getAccessibleContext();
    if( !pRContext.is() )
        return E_FAIL;

    Reference<XAccessibleAction> pRXI(pRContext,UNO_QUERY);
    if( !pRXI.is() )
        pRXAct = NULL;
    else
        pRXAct = pRXI.get();
    return S_OK;

    LEAVE_PROTECTED_BLOCK
}

/**
 * Helper function used for converting keybinding to string.
 *
 * @param    keySet    the key stroke sequence.
 * @param    pString   the output keybinding string.
 */
void CAccActionBase::GetkeyBindingStrByXkeyBinding( const Sequence< KeyStroke > &keySet, OLECHAR* pString )
{
    // #CHECK#
    if(pString == NULL)
        return;

    for( int iIndex = 0;iIndex < keySet.getLength();iIndex++ )
    {
        KeyStroke stroke = keySet[iIndex];
        OLECHAR wString[64] = {NULL};
        if(iIndex>0)
            wcscat( wString, OLESTR("  ") );
        if((stroke.Modifiers & MODIFIER_SHIFT) == MODIFIER_SHIFT)
            wcscat( wString, OLESTR("Shift+") );
        if((stroke.Modifiers & MODIFIER_CTRL) == MODIFIER_CTRL)
            wcscat( wString, OLESTR("Ctrl+") );
        if((stroke.Modifiers & MODIFIER_ALT) == MODIFIER_ALT)
            wcscat( wString, OLESTR("Alt+") );
        if(stroke.KeyCode)
        {
            OLECHAR* pChar = getOLECHARFromKeyCode(stroke.KeyCode);
            if(pChar != NULL)
                wcscat( wString, pChar );
            else if (stroke.KeyCode <= 255)
            {
                OLECHAR pChar[4] = {NULL};
                swprintf( pChar, L"%c", stroke.KeyCode);
                wcscat( wString, pChar);
            }
            else
            {
                OLECHAR pChar[4] = {NULL};
                swprintf( pChar, L"%d", stroke.KeyCode);
                wcscat( wString, pChar);
            }
        }

        wcscat( pString, wString);
    }
}

/**
 * Helper function used for converting key code to ole string.
 *
 * @param    key    the key code.
 */
OLECHAR* CAccActionBase::getOLECHARFromKeyCode(long key)
{
    static struct keyMap
    {
        int keyCode;
        OLECHAR* key;
    }
    map[] =
        {
            {MODIFIER_SHIFT, L"SHIFT" },
            {MODIFIER_CTRL, L"CTRL" },
            {MODIFIER_ALT, L"ALT" },
            CODEENTRY(NUM0),CODEENTRY(NUM1),CODEENTRY(NUM2),CODEENTRY(NUM3),CODEENTRY(NUM4),CODEENTRY(NUM5),
            CODEENTRY(NUM6),CODEENTRY(NUM7),CODEENTRY(NUM8),CODEENTRY(NUM9),
            CODEENTRY(A),CODEENTRY(B),CODEENTRY(C),CODEENTRY(D),CODEENTRY(E),CODEENTRY(F),
            CODEENTRY(G),CODEENTRY(H),CODEENTRY(I),CODEENTRY(J),CODEENTRY(K),CODEENTRY(L),
            CODEENTRY(M),CODEENTRY(N),CODEENTRY(O),CODEENTRY(P),CODEENTRY(Q),CODEENTRY(R),
            CODEENTRY(S),CODEENTRY(T),CODEENTRY(U),CODEENTRY(V),CODEENTRY(W),CODEENTRY(X),
            CODEENTRY(Y),CODEENTRY(Z),
            CODEENTRY(F1),CODEENTRY(F2),CODEENTRY(F3),CODEENTRY(F4),CODEENTRY(F5),CODEENTRY(F6),
            CODEENTRY(F7),CODEENTRY(F8),CODEENTRY(F9),CODEENTRY(F10),CODEENTRY(F11),CODEENTRY(F12),
            CODEENTRY(F13),CODEENTRY(F14),CODEENTRY(F15),CODEENTRY(F16),CODEENTRY(F17),CODEENTRY(F18),
            CODEENTRY(F19),CODEENTRY(F20),CODEENTRY(F21),CODEENTRY(F22),CODEENTRY(F23),CODEENTRY(F24),
            CODEENTRY(F25),CODEENTRY(F26),

            { KEYCODE_DOWN, L"DOWN" },
            { KEYCODE_UP, L"UP" },
            { KEYCODE_LEFT, L"LEFT" },
            { KEYCODE_RIGHT, L"RIGHT" },
            { KEYCODE_HOME, L"HOME" },
            { KEYCODE_END, L"END" },
            { KEYCODE_PAGEUP, L"PAGEUP" },
            { KEYCODE_PAGEDOWN, L"PAGEDOWN" },
            { KEYCODE_RETURN, L"RETURN" },
            { KEYCODE_ESCAPE, L"ESCAPE" },
            { KEYCODE_TAB, L"TAB" },
            { KEYCODE_BACKSPACE, L"BACKSPACE" },
            { KEYCODE_SPACE, L"SPACE" },
            { KEYCODE_INSERT, L"INSERT" },
            { KEYCODE_DELETE, L"DELETE" },
            { KEYCODE_ADD, L"ADD" },
            { KEYCODE_SUBTRACT, L"SUBTRACT" },
            { KEYCODE_MULTIPLY, L"MULTIPLY" },
            { KEYCODE_DIVIDE, L"DIVIDE" },
            { KEYCODE_POINT, L"POINT" },
            { KEYCODE_COMMA, L"COMMA" },
            { KEYCODE_LESS, L"LESS" },
            { KEYCODE_GREATER, L"GREATER" },
            { KEYCODE_EQUAL, L"EQUAL" },
            { KEYCODE_OPEN, L"OPEN" },
            { KEYCODE_CUT, L"CUT" },
            { KEYCODE_COPY, L"COPY" },
            { KEYCODE_PASTE, L"PASTE" },
            { KEYCODE_UNDO, L"UNDO" },
            { KEYCODE_REPEAT, L"REPEAT" },
            { KEYCODE_FIND, L"FIND" },
            { KEYCODE_PROPERTIES, L"PROPERTIES" },
            { KEYCODE_FRONT, L"FRONT" },
            { KEYCODE_CONTEXTMENU, L"CONTEXTMENU" },
            { KEYCODE_HELP, L"HELP" },
        };
    static long nCount = countof(map);

    long min = 0;
    long max = nCount-1;
    long mid = nCount/2;
    while(min<max)
    {
        if(key<map[mid].keyCode)
            max = mid-1;
        else if(key>map[mid].keyCode)
            min = mid+1;
        else
            break;
        mid = (min+max)/2;
    }

    if(key == map[mid].keyCode)
    {
        return map[mid].key;
    }
    else
    {
        return NULL;
    }
}