/*======================================================
FILE:  OEMAppFrame.c

SERVICES:  OEM functions for implementing the AppFrame interface

GENERAL DESCRIPTION:
   This provides a reference implementation for the AppFrame
   interface, which is used by BREW to dynamically resize BREW's
   virtual screen.

        Copyright 2003 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=====================================================*/

#include "AEE_OEM.h"
#include "OEMAppFrame.h"
#include "OEMDisp.h"
#include "AEEStdLib.h"
#include "OEMBitmapFuncs_priv.h"

#if defined(AEE_SIMULATOR)
#include "EmuDisp.h"
#endif

static uint32 OEMAppFrame_AddRef(IAppFrame *po);
static uint32 OEMAppFrame_Release(IAppFrame *po);
static int OEMAppFrame_QueryInterface(IAppFrame *po, AEECLSID clsid, void **ppNew);
static int OEMAppFrame_Init(IAppFrame *po, IBitmap *pbmDev, IBitmap *pbmApp, const char *pchDisp, int cDisp);
static int OEMAppFrame_Redraw(IAppFrame *po);
static int OEMAppFrame_Update(IAppFrame *po);
static int OEMAppFrame_Disable(IAppFrame *po);

static const VTBL(IAppFrame) gOEMAppFrameFuncs = {
   OEMAppFrame_AddRef,
   OEMAppFrame_Release,
   OEMAppFrame_QueryInterface,
   OEMAppFrame_Init,
   OEMAppFrame_Redraw,
   OEMAppFrame_Update,
   OEMAppFrame_Disable
};

typedef struct OEMAppFrame OEMAppFrame;

struct OEMAppFrame {
   const AEEVTBL(IAppFrame) *pvt;
   uint32 uRefs;
   AEERect rcAppRegion;
   AEEBitmapInfo bi;
   IBitmap *pbmDev;
   IBitmap *pbmApp;
#if defined(AEE_SIMULATOR)
   AEECLSID clsEmuDisp;
#endif
#if defined(IMPLEMENT_MULTIDEPTH)
   RGBVAL rgbBackground;  // color to clear the screen
#endif
};


#define ELEMENT_DONE          0
#define ELEMENT_ERROR         1
#define ELEMENT_OK            2

static int GetElement(const char **pchIn, int *pcIn, const char **pchKey, int *pcKey, const char **pchVal, int *pcVal);
static int GetElementInt(const char *pchVal, int cVal, int nOrigVal, int nDefaultVal, int nMaxVal);

int OEMAppFrame_New(IShell * piShell, AEECLSID cls, void **ppif)
{
   OEMAppFrame *pMe = NULL;
   int nResult = SUCCESS;

   pMe = (OEMAppFrame*)MALLOC(sizeof(OEMAppFrame));
   if (pMe == NULL) {
      nResult = ENOMEMORY;
      goto Error;
   }

   pMe->pvt = &gOEMAppFrameFuncs;
   pMe->uRefs = 1;

#if defined(AEE_SIMULATOR)
   switch (cls) {
   case AEECLSID_APPFRAME2:
      pMe->clsEmuDisp = AEECLSID_EMUDISP2;
      break;
   case AEECLSID_APPFRAME3:
      pMe->clsEmuDisp = AEECLSID_EMUDISP3;
      break;
   case AEECLSID_APPFRAME4:
      pMe->clsEmuDisp = AEECLSID_EMUDISP4;
      break;
   default:
      pMe->clsEmuDisp = AEECLSID_EMUDISP1;
      break;
   }
#endif

#if defined(IMPLEMENT_MULTIDEPTH)
   {
      RGBVAL aClrs[CLR_USER_BACKGROUND + 1];
      int nItem;
      int nSize = sizeof(aClrs);

      switch (cls) {
      case AEECLSID_APPFRAME2:
         nItem = AEE_DEVICEITEM_SYS_COLORS_DISP2;
         break;
      case AEECLSID_APPFRAME3:
         nItem = AEE_DEVICEITEM_SYS_COLORS_DISP3;
         break;
      case AEECLSID_APPFRAME4:
         nItem = AEE_DEVICEITEM_SYS_COLORS_DISP4;
         break;
      default:
         nItem = AEE_DEVICEITEM_SYS_COLORS_DISP1;
         break;
      }

      if (SUCCESS != ISHELL_GetDeviceInfoEx(piShell, nItem, aClrs, &nSize)) {
         OEMAppFrame_Release((IAppFrame*)pMe);
         return EFAILED;
      }

      pMe->rgbBackground = aClrs[CLR_USER_BACKGROUND];
   }
#endif

Error:
   *ppif = pMe;
   return nResult;
}


static uint32 OEMAppFrame_AddRef(IAppFrame *po)
{
   OEMAppFrame *pMe = (OEMAppFrame*)po;
   return ++(pMe->uRefs);
}

static uint32 OEMAppFrame_Release(IAppFrame *po)
{
   OEMAppFrame *pMe = (OEMAppFrame*)po;

   if (!pMe->uRefs)
      return 0;

   if (--pMe->uRefs > 0)
      return pMe->uRefs;

   // Ref count is zero, so clean up

   if (pMe->pbmDev) {
      IBITMAP_Release(pMe->pbmDev);
   }

   if (pMe->pbmApp) {
      IBITMAP_Release(pMe->pbmApp);
   }

   FREE(pMe);

   return 0;
}

static int OEMAppFrame_QueryInterface(IAppFrame *po, AEECLSID clsid, void **ppNew)
{
   if (clsid == AEEIID_APPFRAME || clsid == AEECLSID_QUERYINTERFACE) {
      *ppNew = (void*)po;
      OEMAppFrame_AddRef(po);
      return SUCCESS;
   }

   return ECLASSNOTSUPPORT;
}

static int OEMAppFrame_Init(IAppFrame *po, IBitmap *pbmDev, IBitmap *pbmApp, const char *pchDisp, int cDisp)
{
   OEMAppFrame *pMe = (OEMAppFrame*)po;
   IBitmapCtl *piBitmapCtl = 0;
   IBitmapDev *piBitmapDev = 0;
#if defined(IMPLEMENT_MULTIDEPTH)
   int nMaxScheme; // Maximum available color scheme
   int nDefScheme; // Default color scheme
   int nScheme;    // Initial color scheme, used if nothing is specified.  Initialized to nDefScheme.
#endif
   int nWidth, nHeight, nAnnun = 1;
   int nErr = SUCCESS;

   if (pbmDev == NULL || pbmApp == NULL) {
      nErr = EBADPARM;
      goto bail;
   }

   nErr = IBITMAP_QueryInterface(pbmApp, AEEIID_BITMAPCTL, (void**)&piBitmapCtl);
   if (SUCCESS != nErr) {
      goto bail;
   }

   nErr = IBITMAP_QueryInterface(pbmApp, AEEIID_BITMAPDEV, (void**)&piBitmapDev);
   if (SUCCESS != nErr) {
      goto bail;
   }

#if defined(IMPLEMENT_MULTIDEPTH) && defined(AEE_SIMULATOR)
   {
      IEmuDisp *pEmuDisp;
      AEEBitmapInfo bi;

      nErr = AEE_CreateInstanceSys(pMe->clsEmuDisp, (void**)&pEmuDisp);
      if (SUCCESS != nErr) {
         goto bail;
      }

      nErr = IEMUDISP_GetInfo(pEmuDisp, &bi, sizeof(bi));
      IEMUDISP_Release(pEmuDisp);
      if (SUCCESS != nErr) {
         goto bail;
      }
      nMaxScheme = nDefScheme = bi.nDepth;
      if (nDefScheme > 16) {
         // displays with depths greater than 16 must default to 16 (5-6-5) DIB-compatible mode
         nDefScheme = 16;
      }
      nScheme = nDefScheme;
   }
#endif // IMPLEMENT_MULTIDEPTH && AEE_SIMULATOR

   nErr = IBITMAP_GetInfo(pbmDev, &pMe->bi, sizeof(pMe->bi));
   if (SUCCESS != nErr) {
      goto bail;
   }

   nWidth = pMe->bi.cx;
   nHeight = pMe->bi.cy;

   pMe->rcAppRegion.x = 0;
   pMe->rcAppRegion.y = 0;
   pMe->rcAppRegion.dx = nWidth;
   pMe->rcAppRegion.dy = nHeight;

   nErr = IBITMAPCTL_Restrict(piBitmapCtl, &pMe->rcAppRegion);
   if (SUCCESS != nErr) {
      goto bail;
   }

   for (;;) {
      int nElResult;
      const char *pchKey, *pchVal;
      int cKey, cVal;

      nElResult = GetElement(&pchDisp, &cDisp, &pchKey, &cKey, &pchVal, &cVal);
      if (ELEMENT_DONE == nElResult) {
         break;
      }

      if (ELEMENT_OK != nElResult) {
         // bad element, ignore
         continue;
      }

      if (1 != cKey) {
         // all of the keys we define have length 1, ignore undefined keys
         continue;
      }

      switch (pchKey[0]) {
      case 'w':
         nWidth = GetElementInt(pchVal, cVal, nWidth, nWidth, nWidth);
         break;
      case 'h':
         nHeight = GetElementInt(pchVal, cVal, nHeight, nHeight, nHeight);
         break;
#if defined(IMPLEMENT_MULTIDEPTH)
      case 'c':
         nScheme = GetElementInt(pchVal, cVal, 0, nScheme, nMaxScheme);
         break;
#endif
      case 'a':
         nAnnun = GetElementInt(pchVal, cVal, nAnnun, nAnnun, nAnnun);
         break;
      }
   }

#if defined(IMPLEMENT_MULTIDEPTH)
   if (nScheme != 0) { // 0 is our own private way of saying don't change mode
      IBitmap *pbmToClone = NULL;

      // OEM should determine which bit depths are supported

      if (nScheme != 1 && nScheme != 2 && nScheme != 8 && nScheme != 12 &&
          nScheme != 16 && nScheme != 18 && nScheme != 24) {
         nScheme = nDefScheme;
      }

      if (nScheme > nMaxScheme) {
         // don't allow mode to be set to something not supported by skin
         nScheme = nMaxScheme;
      }

      // We instantiate this bitmap as a child because:
      //    a) We need it to be the same size as the global device bitmap for clone to work right.
      //    b) Children don't take up much memory.
      switch (nScheme) {
      case 1:
         OEMBitmap1_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      case 2:
         OEMBitmap2_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      case 8:
         OEMBitmap8_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      case 12:
         OEMBitmap12_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      case 16:
         OEMBitmap16_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      case 18:
         OEMBitmap18_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      case 24:
         OEMBitmap24_New_Child(pbmDev, NULL, &pbmToClone);
         break;
      }

      if (NULL != pbmToClone) {
         OEMBitmap_CopyMode(pbmApp, pbmToClone);
         IBITMAP_Release(pbmToClone);
      }


   }
#endif // IMPLEMENT_MULTIDEPTH

   {
      AEEBitmapInfo biApp;

      nErr = IBITMAP_GetInfo(pbmApp, &biApp, sizeof(biApp));
      if (SUCCESS != nErr) {
         goto bail;
      }

      if (nWidth <= 0 || nWidth > (int)pMe->bi.cx) {
         nWidth = pMe->bi.cx;
      }
      if (nHeight <= 0 || nHeight > (int)pMe->bi.cy) {
         nHeight = pMe->bi.cy;
      }

      pMe->rcAppRegion.x = 0;
      pMe->rcAppRegion.y = 0;
      pMe->rcAppRegion.dx = nWidth;
      pMe->rcAppRegion.dy = nHeight;
      if (biApp.nDepth < 8) {
         // The supplied restrict implementation for these bit depths only supports
         // byte boundries for x (for effeciency reasons).
         pMe->rcAppRegion.x = (int16)(((pMe->bi.cx - nWidth) / 2) / (8 / biApp.nDepth) * (8 / biApp.nDepth));
      } else {
         pMe->rcAppRegion.x = (int16)((pMe->bi.cx - nWidth) / 2);
      }
      pMe->rcAppRegion.y = (int16)((pMe->bi.cy - nHeight) / 2);

      nErr = IBITMAPCTL_Restrict(piBitmapCtl, &pMe->rcAppRegion);
      if (SUCCESS != nErr) {
         goto bail;
      }

   }

   IBITMAP_AddRef(pbmDev);
   if ((IBitmap*)0 != pMe->pbmDev) {
      IBITMAP_Release(pMe->pbmDev);
   }
   pMe->pbmDev = pbmDev;

   IBITMAP_AddRef(pbmApp);
   if ((IBitmap*)0 != pMe->pbmApp) {
      IBITMAP_Release(pMe->pbmApp);
   }
   pMe->pbmApp = pbmApp;

bail:
   if ((IBitmapDev *)0 != piBitmapDev) {
      IBITMAPDEV_Release(piBitmapDev);
   }
   if ((IBitmapCtl *)0 != piBitmapCtl) {
      IBITMAPCTL_Release(piBitmapCtl);
   }
   return nErr;
}

static int OEMAppFrame_Redraw(IAppFrame *po)
{
   OEMAppFrame *pMe = (OEMAppFrame*)po;
   NativeColor nc;
   AEERect rc;

   if (pMe->pbmDev == NULL || pMe->pbmApp == NULL) {
      return EFAILED;
   }

#if defined(IMPLEMENT_MULTIDEPTH)
   if (AEEGETPVTBL(pMe->pbmDev, IBitmap) != AEEGETPVTBL(pMe->pbmApp, IBitmap)) {
      NativeColor c;
      OEMBitmap_CopyMode(pMe->pbmDev, pMe->pbmApp);
      c = IBITMAP_RGBToNative(pMe->pbmDev, pMe->rgbBackground);
      IBITMAP_FillRect(pMe->pbmDev, &pMe->rcAppRegion, c, AEE_RO_COPY);
   }
#endif // IMPLEMENT_MULTIDEPTH

   nc = IBITMAP_RGBToNative(pMe->pbmDev, MAKE_RGB(0x00, 0x00, 0x00));

   rc.x = 0;
   rc.y = 0;
   rc.dx = (int16)pMe->bi.cx;
   rc.dy = pMe->rcAppRegion.y;
   IBITMAP_FillRect(pMe->pbmDev, &rc, nc, AEE_RO_COPY);

   rc.y = pMe->rcAppRegion.y + pMe->rcAppRegion.dy;
   rc.dy = (int16)(pMe->bi.cy - rc.y);
   IBITMAP_FillRect(pMe->pbmDev, &rc, nc, AEE_RO_COPY);

   rc.y = pMe->rcAppRegion.y;
   rc.dx = pMe->rcAppRegion.x;
   rc.dy = pMe->rcAppRegion.dy;
   IBITMAP_FillRect(pMe->pbmDev, &rc, nc, AEE_RO_COPY);

   rc.x = pMe->rcAppRegion.x + pMe->rcAppRegion.dx;
   rc.dx = (int16)(pMe->bi.cx - rc.x);
   IBITMAP_FillRect(pMe->pbmDev, &rc, nc, AEE_RO_COPY);

   return SUCCESS;
}

static int OEMAppFrame_Update(IAppFrame *po)
{
   return SUCCESS;
}

static int OEMAppFrame_Disable(IAppFrame *po)
{
   // Since the only place we redraw is in OEMAppFrame_Redraw, there is nothing to do here.
   return SUCCESS;
}




static int GetElement(const char **ppchIn, int *pcIn, const char **ppchKey, int *pcKey, const char **ppchVal, int *pcVal)
{
   while (*pcIn && *ppchIn[0] == ',') {
      (*pcIn)--;
      (*ppchIn)++;
   }

   if (0 == *pcIn) {
      return ELEMENT_DONE;
   }

   *ppchKey = *ppchIn;
   *pcKey = 0;

   while (*pcIn && *ppchIn[0] != ':' && *ppchIn[0] != ',') {
      (*pcIn)--;
      (*ppchIn)++;
      (*pcKey)++;
   }

   // pass : char
   if (*pcIn && *ppchIn[0] == ':') {
      (*pcIn)--;
      (*ppchIn)++;
   }

   *ppchVal = *ppchIn;
   *pcVal = 0;

   while (*pcIn && *ppchIn[0] != ',') {
      (*pcIn)--;
      (*ppchIn)++;
      (*pcVal)++;
   }

   return ELEMENT_OK;
}

static int GetElementInt(const char *pchVal, int cVal, int nOrigVal, int nDefaultVal, int nMaxVal)
{
   int nAcc = 0;
   boolean bNegate = FALSE;

   if (1 == cVal) {
      if ('*' == *pchVal) {
         return nMaxVal;
      }
      if ('p' == *pchVal) {
         // preserve
         return nOrigVal;
      }
   }

   if (!cVal) {
      return nDefaultVal;
   }

   while (cVal--) {
      if (*pchVal < '0' || *pchVal > '9') {
         return nDefaultVal;
      }
      nAcc = nAcc * 10 + *pchVal - '0';
      pchVal++;
   }

   return bNegate ? -nAcc: nAcc;
}
