/*
 * ReactOS Generic Framebuffer acclations display driver
 *
 * Copyright (C) 2004 Filip Navara
 * Copyright (C) 2007 Magnus Olsen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "framebufacc.h"

BOOL 
InitSurface(PPDEV ppdev,
            BOOL bForcemapping)
{
   VIDEO_MEMORY VideoMemory;
   VIDEO_MEMORY_INFORMATION VideoMemoryInfo;
   ULONG returnedDataLength;
   ULONG RemappingNeeded = 0;
   ULONG PointerMaxWidth = 0;
   ULONG PointerMaxHeight = 0;

   /*
    * Set video mode of our adapter.
    */

   if (EngDeviceIoControl(ppdev->hDriver,
                          IOCTL_VIDEO_SET_CURRENT_MODE,
                          &(ppdev->ModeIndex),
                          sizeof(ULONG),
                          &RemappingNeeded,
                          sizeof(ULONG),
                          &returnedDataLength))
   {
      return FALSE;
   }

   /* Check if mapping is need it */
   if ((!bForcemapping) && 
       (!RemappingNeeded))
   {
       return TRUE;
   }
   

   /*
    * Map the framebuffer into our memory.
    */

   VideoMemory.RequestedVirtualAddress = NULL;
   if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_MAP_VIDEO_MEMORY,
                          &VideoMemory, sizeof(VIDEO_MEMORY),
                          &VideoMemoryInfo, sizeof(VIDEO_MEMORY_INFORMATION),
                          &returnedDataLength))
   {
      return FALSE;
   }

   /*
    * Save the real video memory
    */
    ppdev->pRealVideoMem = VideoMemoryInfo.FrameBufferBase;
    ppdev->VideoMemSize = VideoMemoryInfo.VideoRamLength;


   /*
    * Video memory cached
    *
    * We maby should only ask max 8MB as cached ?, think of the video ram length is 256MB 
    */

    ppdev->pVideoMemCache = NULL;
#ifdef EXPERIMENTAL_ACC_SUPPORT

   ppdev->pVideoMemCache = EngAllocMem(0, (ULONG)VideoMemoryInfo.VideoRamLength, ALLOC_TAG);
   if (ppdev->pVideoMemCache == NULL)
   {
        /* cached off for no avail system memory */
        ppdev->ScreenPtr = VideoMemoryInfo.FrameBufferBase;
   }
   else

#endif
   {
        /* cached on, system memory is avail */
        ppdev->ScreenPtr = ppdev->pRealVideoMem;
   }

   /* hw mouse pointer support */
   PointerMaxHeight = ppdev->PointerCapabilities.MaxHeight;
   PointerMaxWidth = ppdev->PointerCapabilities.MaxWidth * sizeof(ULONG);
   if (ppdev->PointerCapabilities.Flags & VIDEO_MODE_COLOR_POINTER)
   {
        PointerMaxWidth = (ppdev->PointerCapabilities.MaxWidth + 7) / 8;
   }

   ppdev->PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) + ((sizeof(UCHAR) * PointerMaxWidth * PointerMaxHeight) << 1);

   ppdev->pPointerAttributes = EngAllocMem(0, ppdev->PointerAttributesSize, ALLOC_TAG);

   if (ppdev->pPointerAttributes != NULL)
   {
        ppdev->pPointerAttributes->Flags = ppdev->PointerCapabilities.Flags;
        ppdev->pPointerAttributes->WidthInBytes = PointerMaxWidth;
        ppdev->pPointerAttributes->Width = ppdev->PointerCapabilities.MaxWidth;
        ppdev->pPointerAttributes->Height = PointerMaxHeight;
        ppdev->pPointerAttributes->Column = 0;
        ppdev->pPointerAttributes->Row = 0;
        ppdev->pPointerAttributes->Enable = 0;
   }
   else
   {
       /* no hw mouse was avail */
       ppdev->PointerAttributesSize = 0;
   }

   return TRUE;
}

/*
 * DrvEnableSurface
 *
 * Create engine bitmap around frame buffer and set the video mode requested
 * when PDEV was initialized.
 *
 * Status
 *    @implemented
 */

HSURF APIENTRY
DrvEnableSurface(
   IN DHPDEV dhpdev)
{
   PPDEV ppdev = (PPDEV)dhpdev;
   HSURF hSurface;
   ULONG BitmapType;
   SIZEL ScreenSize;


   /* Setup surface and force the mapping */
   if (!InitSurface(ppdev, TRUE))
   {
       return FALSE;
   }

   /* Rest the desktop vitual position */
   ppdev->ScreenOffsetXY.x = 0;
   ppdev->ScreenOffsetXY.y = 0;


   switch (ppdev->BitsPerPixel)
   {
      case 8:
         IntSetPalette(dhpdev, ppdev->PaletteEntries, 0, 256);
         BitmapType = BMF_8BPP;
         break;

      case 16:
         BitmapType = BMF_16BPP;
         break;

      case 24:
         BitmapType = BMF_24BPP;
         break;

      case 32:
         BitmapType = BMF_32BPP;
         break;

      default:
         return FALSE;
   }

   ppdev->iDitherFormat = BitmapType;

   ScreenSize.cx = ppdev->ScreenWidth;
   ScreenSize.cy = ppdev->ScreenHeight;

   hSurface = (HSURF)EngCreateBitmap(ScreenSize, ppdev->ScreenDelta, BitmapType,
                                     (ppdev->ScreenDelta > 0) ? BMF_TOPDOWN : 0,
                                     ppdev->ScreenPtr);
   if (hSurface == NULL)
   {
      return FALSE;
   }

   /* Which api we hooking to */
   ppdev->dwHooks = HOOK_BITBLT | HOOK_COPYBITS | HOOK_FILLPATH | HOOK_TEXTOUT | HOOK_STROKEPATH | HOOK_LINETO ;

   /*
    * Associate the surface with our device.
    */

                
   if (!EngAssociateSurface(hSurface, ppdev->hDevEng, ppdev->dwHooks))
   {
      EngDeleteSurface(hSurface);
      return FALSE;
   }

   ppdev->hSurfEng = hSurface;

   return hSurface;
}

/*
 * DrvDisableSurface
 *
 * Used by GDI to notify a driver that the surface created by DrvEnableSurface
 * for the current device is no longer needed.
 *
 * Status
 *    @implemented
 */

VOID APIENTRY
DrvDisableSurface(
   IN DHPDEV dhpdev)
{
   DWORD ulTemp;
   VIDEO_MEMORY VideoMemory;
   PPDEV ppdev = (PPDEV)dhpdev;

   EngDeleteSurface(ppdev->hSurfEng);
   ppdev->hSurfEng = NULL;


   /* Free the video memory cache */
   if (ppdev->pVideoMemCache)
   {
        EngFreeMem(ppdev->pVideoMemCache);
   }

   /*
    * Unmap the framebuffer.
    */

   VideoMemory.RequestedVirtualAddress = ppdev->pRealVideoMem;
   EngDeviceIoControl(((PPDEV)dhpdev)->hDriver, IOCTL_VIDEO_UNMAP_VIDEO_MEMORY,
                      &VideoMemory, sizeof(VIDEO_MEMORY), NULL, 0, &ulTemp);

   ppdev->pRealVideoMem  = NULL;
   ppdev->pVideoMemCache = NULL;

}

/*
 * DrvAssertMode
 *
 * Sets the mode of the specified physical device to either the mode specified
 * when the PDEV was initialized or to the default mode of the hardware.
 *
 * Status
 *    @implemented
 */

BOOL APIENTRY
DrvAssertMode(
   IN DHPDEV dhpdev,
   IN BOOL bEnable)
{
   PPDEV ppdev = (PPDEV)dhpdev;
   ULONG ulTemp;
   BOOLEAN Result = TRUE;

   if (bEnable)
   {
      PVOID pRealVideoMem = ppdev->pRealVideoMem;

      /* Setup surface and remapping if it need it */
      if (!InitSurface(ppdev, FALSE))
      {
            Result = FALSE;
      }
      else
      {
            /* Check if we got same surface or not */
            if (pRealVideoMem != ppdev->pRealVideoMem)
            {
                PVOID pVideoMem= NULL;

                if (ppdev->pVideoMemCache == NULL)
                {
                    pVideoMem = ppdev->pRealVideoMem;
                }
                else
                {
                    pVideoMem = ppdev->pVideoMemCache;
                }

                Result = !EngModifySurface(ppdev->hSurfEng, ppdev->hDevEng,
                                           ppdev->dwHooks | HOOK_SYNCHRONIZE,
                                           0, (DHSURF)ppdev, pVideoMem,
                                           ppdev->ScreenDelta, NULL);
            }

            /* if the pRealVideoMem == ppdev->pRealVideoMem are 
             * the Result is then TRUE
             */
      }

   }
   else
   {
      /*
       * Call the miniport driver to reset the device to a known state.
       */

      Result = !EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_RESET_DEVICE,
                                 NULL, 0, NULL, 0, &ulTemp);
   }

   return Result;
}
