/**
 * SetToTransformFunction is essentially a giant switch statement that fans
 * out to many smaller helper functions.
 */
void
nsStyleTransformMatrix::SetToTransformFunction(const nsCSSValue::Array * aData,
                                               nsStyleContext* aContext,
                                               nsPresContext* aPresContext,
                                               PRBool& aCanStoreInRuleTree)
{
  NS_PRECONDITION(aData, "Why did you want to get data from a null array?");
  NS_PRECONDITION(aContext, "Need a context for unit conversion!");
  NS_PRECONDITION(aPresContext, "Need a context for unit conversion!");
  
  /* Reset the matrix to the identity so that each subfunction can just
   * worry about its own components.
   */
  SetToIdentity();

  /* Get the keyword for the transform. */
  nsAutoString keyword;
  aData->Item(0).GetStringValue(keyword);
  switch (nsCSSKeywords::LookupKeyword(keyword)) {
  case eCSSKeyword_translatex:
    ProcessTranslateX(mDelta, mX, aData, aContext, aPresContext,
                      aCanStoreInRuleTree);
    break;
  case eCSSKeyword_translatey:
    ProcessTranslateY(mDelta, mY, aData, aContext, aPresContext,
                      aCanStoreInRuleTree);
    break;
  case eCSSKeyword_translate:
    ProcessTranslate(mDelta, mX, mY, aData, aContext, aPresContext,
                     aCanStoreInRuleTree);
    break;
  case eCSSKeyword_scalex:
    ProcessScaleX(mMain, aData);
    break;
  case eCSSKeyword_scaley:
    ProcessScaleY(mMain, aData);
    break;
  case eCSSKeyword_scale:
    ProcessScale(mMain, aData);
    break;
  case eCSSKeyword_skewx:
    ProcessSkewX(mMain, aData);
    break;
  case eCSSKeyword_skewy:
    ProcessSkewY(mMain, aData);
    break;
  case eCSSKeyword_skew:
    ProcessSkew(mMain, aData);
    break;
  case eCSSKeyword_rotate:
    ProcessRotate(mMain, aData);
    break;
  case eCSSKeyword_matrix:
    ProcessMatrix(mMain, mDelta, mX, mY, aData, aContext, aPresContext,
                  aCanStoreInRuleTree);
    break;
  default:
    NS_NOTREACHED("Unknown transform function!");
  }
}
// Main function of this class decodes gesture information
// in:
//      hWnd        window handle
//      wParam      message parameter (message-specific)
//      lParam      message parameter (message-specific)
bool CGestures::ProcessGestureMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)
{
	if ((uMsg != WM_GESTURENOTIFY) && (uMsg != WM_GESTURE))
		return false;

	if (!_isGestures)
	{
		_ASSERTE(_isGestures);
		gpConEmu->LogString(L"Gesture message received but not allowed, skipping");
		return false;
	}

	if (uMsg == WM_GESTURENOTIFY)
	{
		// This is the right place to define the list of gestures that this
		// application will support. By populating GESTURECONFIG structure
		// and calling SetGestureConfig function. We can choose gestures
		// that we want to handle in our application. In this app we
		// decide to handle all gestures.
		GESTURECONFIG gc[] = {
			{GID_ZOOM, GC_ZOOM},
			{GID_ROTATE, GC_ROTATE},
			{GID_PAN,
				GC_PAN|GC_PAN_WITH_GUTTER|GC_PAN_WITH_INERTIA,
				GC_PAN_WITH_SINGLE_FINGER_VERTICALLY|GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY
			},
			{GID_PRESSANDTAP, GC_PRESSANDTAP},
			{GID_TWOFINGERTAP, GC_TWOFINGERTAP},
		};

		BOOL bResult = _SetGestureConfig(hWnd, 0, countof(gc), gc, sizeof(GESTURECONFIG));
		DWORD dwErr = GetLastError();

		if (gpSetCls->isAdvLogging)
		{
			wchar_t szNotify[60];
			_wsprintf(szNotify, SKIPLEN(countof(szNotify)) L"SetGestureConfig -> %u,%u", bResult, dwErr);
			gpConEmu->LogString(szNotify);
		}

		if (!bResult)
		{
			DisplayLastError(L"Error in execution of SetGestureConfig", dwErr);
		}

		lResult = ::DefWindowProc(hWnd, WM_GESTURENOTIFY, wParam, lParam);

		return true;
	}

	// Остался только WM_GESTURE
	Assert(uMsg==WM_GESTURE);

	// helper variables
	POINT ptZoomCenter;
	double k;

	GESTUREINFO gi = {sizeof(gi)};
	// Checking for compiler alignment errors
	if (gi.cbSize != WIN3264TEST(48,56))
	{
		// Struct member alignment must be 8bytes even on x86
		Assert(sizeof(GESTUREINFO)==WIN3264TEST(48,56));
		_isGestures = false;
		return false;
	}
	BOOL bResult = _GetGestureInfo((HGESTUREINFO)lParam, &gi);

	if (!bResult)
	{
		//_ASSERT(L"_GetGestureInfo failed!" && 0);
		DWORD dwErr = GetLastError();
		DisplayLastError(L"Error in execution of _GetGestureInfo", dwErr);
		return FALSE;
	}

	#ifdef USE_DUMPGEST
	bool bLog = (gpSetCls->isAdvLogging >= 2);
	UNREFERENCED_PARAMETER(bLog);
	bLog = true;
	#endif

	#define DUMPGEST(tp) DumpGesture(tp, gi)

	//#ifdef USE_DUMPGEST
	//	wchar_t szDump[256];
	//	#define DUMPGEST(tp)
	//		_wsprintf(szDump, SKIPLEN(countof(szDump))
	//			L"Gesture(x%08X {%i,%i} %s",
	//			(DWORD)gi.hwndTarget, gi.ptsLocation.x, gi.ptsLocation.y,
	//			tp);
	//		if (gi.dwID==GID_PRESSANDTAP) {
	//			DWORD h = LODWORD(gi.ullArguments); _wsprintf(szDump+_tcslen(szDump), SKIPLEN(32)
	//				L" Dist={%i,%i}", (int)(short)LOWORD(h), (int)(short)HIWORD(h)); }
	//		if (gi.dwID==GID_ROTATE) {
	//			DWORD h = LODWORD(gi.ullArguments); _wsprintf(szDump+_tcslen(szDump), SKIPLEN(32)
	//				L" %i", (int)LOWORD(h)); }
	//		if (gi.dwFlags&GF_BEGIN) wcscat_c(szDump, L" GF_BEGIN");
	//		if (gi.dwFlags&GF_END) wcscat_c(szDump, L" GF_END");
	//		if (gi.dwFlags&GF_INERTIA) { wcscat_c(szDump, L" GF_INERTIA");
	//			DWORD h = HIDWORD(gi.ullArguments); _wsprintf(szDump+_tcslen(szDump), SKIPLEN(32)
	//				L" {%i,%i}", (int)(short)LOWORD(h), (int)(short)HIWORD(h)); }
	//		wcscat_c(szDump, L")\n");
	//		DEBUGSTR(szDump)
	//#else
	//#define DUMPGEST(s)
	//#endif

	switch (gi.dwID)
	{
	case GID_BEGIN:
		DUMPGEST(L"GID_BEGIN");
		break;

	case GID_END:
		DUMPGEST(L"GID_END");
		break;

	case GID_ZOOM:
		DUMPGEST(L"GID_ZOOM");
		if (gi.dwFlags & GF_BEGIN)
		{
			_dwArguments = LODWORD(gi.ullArguments);
			_ptFirst.x = gi.ptsLocation.x;
			_ptFirst.y = gi.ptsLocation.y;
			ScreenToClient(hWnd,&_ptFirst);
		}
		else
		{
			// We read here the second point of the gesture. This is middle point between
			// fingers in this new position.
			_ptSecond.x = gi.ptsLocation.x;
			_ptSecond.y = gi.ptsLocation.y;
			ScreenToClient(hWnd,&_ptSecond);

			// We have to calculate zoom center point
			ptZoomCenter.x = (_ptFirst.x + _ptSecond.x)/2;
			ptZoomCenter.y = (_ptFirst.y + _ptSecond.y)/2;

			// The zoom factor is the ratio between the new and the old distance.
			// The new distance between two fingers is stored in gi.ullArguments
			// (lower DWORD) and the old distance is stored in _dwArguments.
			k = (double)(LODWORD(gi.ullArguments))/(double)(_dwArguments);

			// Now we process zooming in/out of the object
			ProcessZoom(hWnd, k, ptZoomCenter.x, ptZoomCenter.y);

			// Now we have to store new information as a starting information
			// for the next step in this gesture.
			_ptFirst = _ptSecond;
			_dwArguments = LODWORD(gi.ullArguments);
		}
		break;

	case GID_PAN:
		DUMPGEST(L"GID_PAN");
		if (gi.dwFlags & GF_BEGIN)
		{
			_ptFirst.x = gi.ptsLocation.x;
			_ptFirst.y = gi.ptsLocation.y;
			_ptBegin.x = gi.ptsLocation.x;
			_ptBegin.y = gi.ptsLocation.y;
			ScreenToClient(hWnd, &_ptFirst);
		}
		else
		{
			// We read the second point of this gesture. It is a middle point
			// between fingers in this new position
			_ptSecond.x = gi.ptsLocation.x;
			_ptSecond.y = gi.ptsLocation.y;
			ScreenToClient(hWnd, &_ptSecond);

			if (!(gi.dwFlags & (GF_END/*|GF_INERTIA*/)))
			{
				// We apply move operation of the object
				if (ProcessMove(hWnd, _ptSecond.x-_ptFirst.x, _ptSecond.y-_ptFirst.y))
				{
					// We have to copy second point into first one to prepare
					// for the next step of this gesture.
					_ptFirst = _ptSecond;
				}
			}

		}
		break;

	case GID_ROTATE:
		DUMPGEST(L"GID_ROTATE");
		if (gi.dwFlags & GF_BEGIN)
		{
			_inRotate = false;
			_dwArguments = LODWORD(gi.ullArguments); // Запомним начальный угол
		}
		else
		{
			_ptFirst.x = gi.ptsLocation.x;
			_ptFirst.y = gi.ptsLocation.y;
			ScreenToClient(hWnd, &_ptFirst);
			// Пока угол не станет достаточным для смены таба - игнорируем
			if (ProcessRotate(hWnd,
					LODWORD(gi.ullArguments) - _dwArguments,
					_ptFirst.x,_ptFirst.y, ((gi.dwFlags & GF_END) == GF_END)))
			{
				_dwArguments = LODWORD(gi.ullArguments);
			}
		}
		break;

	case GID_TWOFINGERTAP:
		DUMPGEST(L"GID_TWOFINGERTAP");
		_ptFirst.x = gi.ptsLocation.x;
		_ptFirst.y = gi.ptsLocation.y;
		ScreenToClient(hWnd,&_ptFirst);
		ProcessTwoFingerTap(hWnd, _ptFirst.x, _ptFirst.y, LODWORD(gi.ullArguments));
		break;

	case GID_PRESSANDTAP:
		DUMPGEST(L"GID_PRESSANDTAP");
		if (gi.dwFlags & GF_BEGIN)
		{
			_ptFirst.x = gi.ptsLocation.x;
			_ptFirst.y = gi.ptsLocation.y;
			ScreenToClient(hWnd,&_ptFirst);
			DWORD nDelta = LODWORD(gi.ullArguments);
			short nDeltaX = (short)LOWORD(nDelta);
			short nDeltaY = (short)HIWORD(nDelta);
			ProcessPressAndTap(hWnd, _ptFirst.x, _ptFirst.y, nDeltaX, nDeltaY);
		}
		break;
	default:
		DUMPGEST(L"GID_<UNKNOWN>");
	}

	_CloseGestureInfoHandle((HGESTUREINFO)lParam);

	return TRUE;
}