/*
 * $Id$
 */

/*
 * Harbour Project source code:
 * Clipper Tools like window system
 *
 * Copyright 2006 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 * www - http://www.harbour-project.org
 *
 * 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, 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 software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

/* NOTE: User programs should never call this layer directly! */

/* This definition has to be placed before #include "hbapigt.h" */
#define HB_GT_NAME      CTW

#include "hbgtcore.h"
#include "hbinit.h"
#include "hbapiitm.h"

#include "ctwin.h"

static int           s_GtId;
static HB_GT_FUNCS   SuperTable;
#define HB_GTSUPER   (&SuperTable)
#define HB_GTID_PTR  (&s_GtId)

#define HB_CTWIN_ALLOC        16
#define HB_CTWIN_MINROWS      1
#define HB_CTWIN_MINCOLS      1
#define HB_CTWIN_MAXROWS      255
#define HB_CTWIN_MAXCOLS      255

typedef struct
{
   int iHandle;

   BOOL fHidden;

   int iShadowAttr;
   int iCursorStyle;

   int iRow;
   int iCol;

   int iTopMargin;
   int iLeftMargin;
   int iBottomMargin;
   int iRightMargin;

   int iHeight;
   int iWidth;

   int iFirstRow;
   int iFirstCol;

   int iColorIndex;
   int iColorCount;
   int * piColors;

   PHB_SCREENCELL screenBuffer;

} HB_CT_WND, * PHB_CT_WND;

static BOOL s_fInit           = FALSE;

static int s_iShadowWidth     = 2;
static int s_iShadowAttr      = -1;

static int s_iOpenWindows     = 0;
static int s_iCurrWindow      = 0;
static int s_iMaxWindow       = 0;

static int s_fBoardSet        = FALSE;
static int s_iBoardTop        = 0;
static int s_iBoardLeft       = 0;
static int s_iBoardBottom     = 0;
static int s_iBoardRight      = 0;

static int s_fBoardTop        = FALSE;
static int s_fBoardLeft       = FALSE;
static int s_fBoardBottom     = FALSE;
static int s_fBoardRight      = FALSE;

static int s_iMoveMode        = 1;
static int s_iVerticalStep    = 2;
static int s_iHorizontalStep  = 5;

static PHB_CT_WND * s_windows = NULL;
static int * s_windowStack    = NULL;
static int * s_pWindowMap     = NULL;
static int * s_pShadowMap     = NULL;
static int s_iMapWidth        = 0;
static int s_iMapHeight       = 0;

static int s_iLastKey         = 0;


static int hb_ctw_CalcShadowWidth( int iRows, int iCols )
{
   if( iRows + iRows >= iCols )
      return 1;
   else
      return 2;
}

static void hb_ctw_SetMap( PHB_GT pGT, int * piMap, int iWindow, int iTop, int iLeft, int iBottom, int iRight )
{
   ULONG lIndex;
   int i;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SetMap(%p,%p,%d,%d,%d,%d,%d)", pGT, piMap, iWindow, iTop, iLeft, iBottom, iRight));

   HB_SYMBOL_UNUSED( pGT );

   if( iTop < 0 )
      iTop = 0;
   if( iBottom >= s_iMapHeight )
      iBottom = s_iMapHeight - 1;
   if( iLeft < 0 )
      iLeft = 0;
   if( iRight >= s_iMapWidth )
      iRight = s_iMapWidth - 1;

   while( iTop <= iBottom )
   {
      lIndex = iTop * s_iMapWidth + iLeft;
      for( i = iLeft; i <= iRight; ++i, ++lIndex )
         piMap[ lIndex ] = iWindow;
      ++iTop;
   }
}

static void hb_ctw_ClearMap( PHB_GT pGT )
{
   ULONG ulSize;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_ClearMap(%p)", pGT));

   HB_SYMBOL_UNUSED( pGT );

   ulSize = ( ULONG ) s_iMapHeight * s_iMapWidth * sizeof( int );
   memset( s_pWindowMap, 0, ulSize );
   memset( s_pShadowMap, 0, ulSize );
}

static void hb_ctw_WindowMap( PHB_GT pGT, int iWindow, BOOL fExpose )
{
   PHB_CT_WND pWnd;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_WindowMap(%p,%d,%d)", pGT, iWindow, (int) fExpose));

   pWnd = s_windows[ iWindow ];

   if( ! pWnd->fHidden )
   {
      int iLastRow = pWnd->iFirstRow + pWnd->iHeight - 1,
          iLastCol = pWnd->iFirstCol + pWnd->iWidth - 1;

      hb_ctw_SetMap( pGT, s_pWindowMap, iWindow, 
                     pWnd->iFirstRow, pWnd->iFirstCol,
                     iLastRow, iLastCol );
      hb_ctw_SetMap( pGT, s_pShadowMap, 0,
                     pWnd->iFirstRow, pWnd->iFirstCol,
                     iLastRow, iLastCol );
      if( pWnd->iShadowAttr >= 0 &&
          iLastRow >= s_iBoardTop && iLastCol >= s_iBoardLeft &&
          pWnd->iFirstRow <= s_iBoardBottom && pWnd->iFirstCol <= s_iBoardRight )
      {
         iLastRow += 1;
         iLastCol += s_iShadowWidth;
         hb_ctw_SetMap( pGT, s_pShadowMap, iWindow,
                        iLastRow, pWnd->iFirstCol + s_iShadowWidth,
                        iLastRow, iLastCol );
         hb_ctw_SetMap( pGT, s_pShadowMap, iWindow,
                        pWnd->iFirstRow + 1, pWnd->iFirstCol + pWnd->iWidth,
                        iLastRow - 1, iLastCol );
      }
      if( fExpose )
      {
         HB_GTSUPER_EXPOSEAREA( pGT, pWnd->iFirstRow, pWnd->iFirstCol,
                                iLastRow, iLastCol );
      }
   }
}

static void hb_ctw_RemapAllWindows( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_RemapAllWindows(%p)", pGT));

   if( s_iMaxWindow )
   {
      int i;

      hb_ctw_ClearMap( pGT );
      for( i = 0; i < s_iOpenWindows; ++i )
         hb_ctw_WindowMap( pGT, s_windowStack[ i ], FALSE );
      HB_GTSUPER_EXPOSEAREA( pGT, 0, 0, s_iMapHeight, s_iMapWidth );
   }
}

static BOOL hb_ctw_Init( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_Init(%p)",pGT));

   if( ! s_fInit )
   {
      s_fInit = hb_gtLoad( HB_GT_DRVNAME( HB_GT_NAME ), NULL );
      if( s_fInit )
      {
         int iRow, iCol;

         HB_GTSUPER_GETSIZE( pGT, &s_iMapHeight, &s_iMapWidth );

         /* update cursor position to the rules used by CTWIN */
         HB_GTSELF_GETPOS( pGT, &iRow, &iCol );
         HB_GTSELF_SETPOS( pGT, iRow, iCol );
      }
   }

   return s_fInit;
}

static int hb_ctw_SetShadowAttr( PHB_GT pGT, int iAttr )
{
   int iOldAttr = s_iShadowAttr;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SetShadowAttr(%p,%d)", pGT, iAttr));

   HB_SYMBOL_UNUSED( pGT );

   if( iAttr >= -1 )
      s_iShadowAttr = iAttr;

   return iOldAttr;
}

static int hb_ctw_SetMoveMode( PHB_GT pGT, int iMode )
{
   int iOldMode = s_iMoveMode;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SetMoveMode(%p,%d)", pGT, iMode));

   HB_SYMBOL_UNUSED( pGT );

   if( iMode >= 0 )
      s_iMoveMode = iMode;

   return iOldMode;
}

static int hb_ctw_SetMoveStep( PHB_GT pGT, int iVertical, int iHorizontal )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SetMoveStep(%p,%d,%d)", pGT, iVertical, iHorizontal));

   if( s_fInit || hb_ctw_Init( pGT ) )
   {
      if( iVertical < s_iMapHeight && iHorizontal < s_iMapWidth )
      {
         s_iVerticalStep   = iVertical;
         s_iHorizontalStep = iHorizontal;

         return 0;
      }
   }

   return -1;
}

static int hb_ctw_SetWindowBoard( PHB_GT pGT, int iTop, int iLeft, int iBottom, int iRight )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SetWindowBoard(%d,%d,%d,%d)", iTop, iLeft, iBottom, iRight));

   if( s_fInit || hb_ctw_Init( pGT ) )
   {
      /*
       * This limitation is only for strict CT3 compatibility, the CTW GTs
       * can work in practice with any virtual board size and position and
       * is limited only by available physical memory, [druzus]
       */
      if( iBottom >= s_iMapHeight )
         iBottom = s_iMapHeight - 1;
      if( iRight >= s_iMapWidth )
         iRight = s_iMapWidth - 1;
      if( iTop >= 0 && iLeft >= 0 && iTop < iBottom && iLeft < iRight )
      {
         s_iBoardTop     = iTop;
         s_iBoardLeft    = iLeft;
         s_iBoardBottom  = iBottom;
         s_iBoardRight   = iRight;
         s_fBoardSet     = TRUE;
         hb_ctw_RemapAllWindows( pGT );

         return 0;
      }
   }

   return -1;
}

static int  hb_ctw_SetBorderMode( PHB_GT pGT, int iTop, int iLeft, int iBottom, int iRight )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SetBorderMode(%p,%d,%d,%d,%d)", pGT, iTop, iLeft, iBottom, iRight));

   if( s_fInit || hb_ctw_Init( pGT ) )
   {
      if( iTop >= 0 )
         s_fBoardTop     = iTop != 0;
      if( iLeft >= 0 )
         s_fBoardLeft    = iLeft != 0;
      if( iBottom >= 0 )
         s_fBoardBottom  = iBottom != 0;
      if( iRight >= 0 )
         s_fBoardRight   = iRight != 0;

      return 0;
   }

   return -1;
}

static int hb_ctw_CurrentWindow( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_CurrentWindow(%p)", pGT));

   HB_SYMBOL_UNUSED( pGT );

   return s_iCurrWindow;
}

static int hb_ctw_SelectWindow( PHB_GT pGT, int iWindow )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_SelectWindow(%p,%d)", pGT, iWindow));

   if( iWindow == 0 )
      s_iCurrWindow = iWindow;
   else if( iWindow != s_iCurrWindow &&
            iWindow > 0 && iWindow <= s_iMaxWindow &&
            s_windows[ iWindow ] != NULL )
   {
      s_iCurrWindow = iWindow;
      if( iWindow != s_windowStack[ s_iOpenWindows - 1 ] )
      {
         int i;

         for( i = 0; i < s_iOpenWindows; ++i )
         {
            if( s_windowStack[ i ] == iWindow )
               break;
         }
         while( i < s_iOpenWindows - 1 )
         {
            s_windowStack[ i ] = s_windowStack[ i + 1 ];
            ++i;
         }
         s_windowStack[ s_iOpenWindows - 1 ] = iWindow;
         /* INFO: CT effectively calls hb_ctw_RemapAllWindows() here */
         hb_ctw_WindowMap( pGT, iWindow, TRUE );
      }
   }

   return s_iCurrWindow;
}

static int hb_ctw_MaxWindow( PHB_GT pGT )
{
   int i, iMaxHandle = 0;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_MaxWindow(%p)", pGT));

   HB_SYMBOL_UNUSED( pGT );

   for( i = 0; i < s_iOpenWindows; ++i )
   {
      if( iMaxHandle < s_windowStack[ i ] )
         iMaxHandle = s_windowStack[ i ];
   }

   return iMaxHandle;
}

static int hb_ctw_CreateWindow( PHB_GT pGT, int iTop, int iLeft, int iBottom, int iRight, BOOL fClear, int iColor )
{
   PHB_CT_WND pWnd;
   BYTE bAttr, bColor;
   USHORT usChar;
   int iRow, iCol, iHeight, iWidth, iTmp;
   long lIndex;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_CreateWindow(%p,%d,%d,%d,%d,%d,%d)", pGT, iTop, iLeft, iBottom, iRight, (int) fClear, iColor));

   if( s_iOpenWindows == s_iMaxWindow )
   {
      int i = s_iMaxWindow;

      if( s_iMaxWindow == 0 )
      {
         ULONG ulSize;

         if( !s_fInit )
         {
            if( ! hb_ctw_Init( pGT ) )
               return -1;
         }

         HB_GTSUPER_GETSIZE( pGT, &s_iMapHeight, &s_iMapWidth );
         s_iShadowWidth = hb_ctw_CalcShadowWidth( s_iMapHeight, s_iMapWidth );
         if( !s_fBoardSet )
            hb_ctw_SetWindowBoard( pGT, 0, 0, s_iMapHeight - 1, s_iMapWidth - 1 );
         ulSize = ( ULONG ) s_iMapHeight * s_iMapWidth * sizeof( int );
         s_pWindowMap = ( int * ) hb_xgrab( ulSize );
         s_pShadowMap = ( int * ) hb_xgrab( ulSize );
         hb_ctw_ClearMap( pGT );

         s_iMaxWindow = HB_CTWIN_ALLOC;
         s_windows = ( PHB_CT_WND * ) hb_xgrab( ( HB_CTWIN_ALLOC + 1 ) * sizeof( PHB_CT_WND ) );
         s_windowStack = ( int * ) hb_xgrab( HB_CTWIN_ALLOC * sizeof( int ) );
         s_windows[ 0 ] = NULL;
      }
      else
      {
         s_iMaxWindow += HB_CTWIN_ALLOC;
         s_windows = ( PHB_CT_WND * ) hb_xrealloc( s_windows, ( s_iMaxWindow + 1 ) * sizeof( PHB_CT_WND ) );
         s_windowStack = ( int * ) hb_xrealloc( s_windowStack, s_iMaxWindow * sizeof( int ) );
      }
      do
      {
         s_windows[ i + 1 ] = NULL;
         s_windowStack[ i ] = 0;
      }
      while( ++i < s_iMaxWindow );
   }

   iHeight = iBottom - iTop + 1;
   iWidth  = iRight - iLeft + 1;
   iRow = iTop;
   iCol = iLeft;

   if( iHeight > s_iBoardBottom - s_iBoardTop + 1 )
      iHeight = s_iBoardBottom - s_iBoardTop + 1;
   if( iWidth > s_iBoardRight - s_iBoardLeft + 1 )
      iWidth = s_iBoardRight - s_iBoardLeft + 1;

   if( iHeight < HB_CTWIN_MINROWS || iWidth < HB_CTWIN_MINCOLS ||
       iHeight > HB_CTWIN_MAXROWS || iWidth > HB_CTWIN_MAXCOLS )
      return -1;

   iTop    = s_iBoardTop - ( s_fBoardTop ? iHeight : 0 );
   iBottom = s_iBoardBottom + 1 - ( s_fBoardBottom ? 0 : iHeight );
   iLeft   = s_iBoardLeft - ( s_fBoardLeft ? iWidth : 0 );
   iRight  = s_iBoardRight + 1 - ( s_fBoardRight ? 0 : iWidth );

   if( iRow < iTop )
      iRow = iTop;
   else if( iRow > iBottom )
      iRow = iBottom;
   if( iCol < iLeft )
      iCol = iLeft;
   else if( iCol > iRight )
      iCol = iRight;

   pWnd = ( PHB_CT_WND ) hb_xgrab( sizeof( HB_CT_WND ) );
   memset( pWnd, 0,  sizeof( HB_CT_WND ) );

   pWnd->fHidden = FALSE;
   pWnd->iShadowAttr = s_iShadowAttr;
   pWnd->iCursorStyle = HB_GTSELF_GETCURSORSTYLE( pGT );

   pWnd->iHeight = iHeight;
   pWnd->iWidth  = iWidth;
   pWnd->iFirstRow = iRow;
   pWnd->iFirstCol = iCol;

   HB_GTSELF_GETCOLORDATA( pGT, &pWnd->piColors, &pWnd->iColorCount, &pWnd->iColorIndex );

   pWnd->screenBuffer = ( PHB_SCREENCELL ) hb_xgrab( ( ULONG ) pWnd->iHeight *
                                    pWnd->iWidth * sizeof( HB_SCREENCELL ) );

   if( pWnd->iShadowAttr >= 0 )
      fClear = TRUE;
   bAttr  = 0;
   bColor = iColor ? iColor : HB_GTSELF_GETCOLOR( pGT );
   usChar = ( USHORT ) HB_GTSELF_GETCLEARCHAR( pGT );

   lIndex = 0;
   for( iRow = pWnd->iFirstRow; iRow < pWnd->iFirstRow + pWnd->iHeight; ++iRow )
   {
      for( iCol = pWnd->iFirstCol; iCol < pWnd->iFirstCol + pWnd->iWidth; ++iCol )
      {
         if( !fClear && !HB_GTSELF_GETSCRCHAR( pGT, iRow, iCol, &bColor, &bAttr, &usChar ) )
         {
            usChar = ( USHORT ) HB_GTSELF_GETCLEARCHAR( pGT );
            bColor = ( BYTE ) HB_GTSELF_GETCOLOR( pGT );
            bAttr  = 0;
         }
         pWnd->screenBuffer[ lIndex ].c.usChar = usChar;
         pWnd->screenBuffer[ lIndex ].c.bColor = bColor;
         pWnd->screenBuffer[ lIndex ].c.bAttr  = 0;
         ++lIndex;
      }
   }

   for( iTmp = 1; iTmp < s_iMaxWindow; ++iTmp )
   {
      if( s_windows[ iTmp ] == NULL )
         break;
   }
   pWnd->iHandle = iTmp;

   s_windows[ pWnd->iHandle ] = pWnd;
   s_windowStack[ s_iOpenWindows++ ] = pWnd->iHandle;
   s_iCurrWindow = pWnd->iHandle;

   hb_ctw_WindowMap( pGT, pWnd->iHandle, TRUE );

   return pWnd->iHandle;
}

static int hb_ctw_CloseWindow( PHB_GT pGT, int iWindow )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_CloseWindow(%p,%d)", pGT, iWindow));

   if( iWindow > 0 && iWindow <= s_iMaxWindow && s_windows[ iWindow ] )
   {
      PHB_CT_WND pWnd = s_windows[ iWindow ];
      int i, iWnd, iLast;
      BOOL fHidden = pWnd->fHidden;

      hb_xfree( pWnd->screenBuffer );
      if( pWnd->iColorCount )
         hb_xfree( pWnd->piColors );
      hb_xfree( pWnd );
      s_windows[ iWindow ] = NULL;

      iWnd = 0;
      i = --s_iOpenWindows;
      do
      {
         iLast = s_windowStack[ i ];
         s_windowStack[ i ] = iWnd;
         if( iLast == iWindow )
            break;
         iWnd = iLast;
      }
      while( --i >= 0 );

      if( iWindow == s_iCurrWindow )
         s_iCurrWindow = s_iOpenWindows > 0 ? s_windowStack[ s_iOpenWindows - 1 ] : 0;

      if( !fHidden )
         hb_ctw_RemapAllWindows( pGT );
      return s_iCurrWindow;
   }

   return -1;
}

static int hb_ctw_CloseAllWindows( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_CloseAllWindows(%p)", pGT));

   if( s_iOpenWindows > 0 )
   {
      PHB_CT_WND pWnd;
      int i, iWindow;

      for( i = 0; i < s_iOpenWindows; ++i )
      {
         iWindow = s_windowStack[ i ];
         pWnd = s_windows[ iWindow ];
         s_windowStack[ i ] = 0;
         s_windows[ iWindow ] = NULL;
         hb_xfree( pWnd->screenBuffer );
         if( pWnd->iColorCount )
            hb_xfree( pWnd->piColors );
         hb_xfree( pWnd );
      }
      s_iOpenWindows = s_iCurrWindow = 0;
      hb_ctw_RemapAllWindows( pGT );
      return 0;
   }

   return -1;
}

static int hb_ctw_CenterWindow( PHB_GT pGT, int iWindow, BOOL fCenter )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_CenterWindow(%p,%d,%d)", pGT, iWindow, (int) fCenter));

   HB_SYMBOL_UNUSED( pGT );

   if( iWindow > 0 && iWindow <= s_iOpenWindows )
   {
      PHB_CT_WND pWnd = s_windows[ iWindow ];

      if( pWnd )
      {
         if( fCenter )
         {
            int iHeight = s_iBoardBottom - s_iBoardTop + 1,
                iWidth = s_iBoardRight - s_iBoardLeft + 1;

            pWnd->iFirstRow = s_iBoardTop;
            pWnd->iFirstCol = s_iBoardLeft;

            if( iHeight > pWnd->iHeight )
               pWnd->iFirstRow += ( iHeight - pWnd->iHeight ) >> 1;
            if( iWidth > pWnd->iWidth )
               pWnd->iFirstCol += ( iWidth - pWnd->iWidth ) >> 1;
         }
         else
         {
            if( pWnd->iFirstRow > s_iBoardBottom - pWnd->iHeight + 1 )
               pWnd->iFirstRow = s_iBoardBottom - pWnd->iHeight + 1;
            if( pWnd->iFirstRow < s_iBoardTop )
               pWnd->iFirstRow = s_iBoardTop;
            if( pWnd->iFirstCol > s_iBoardRight - pWnd->iWidth + 1 )
               pWnd->iFirstCol = s_iBoardRight - pWnd->iWidth + 1;
            if( pWnd->iFirstCol < s_iBoardLeft )
               pWnd->iFirstCol = s_iBoardLeft;
         }
         return iWindow;
      }
   }

   return -1;
}

static int hb_ctw_MoveWindow( PHB_GT pGT, int iWindow, int iRow, int iCol )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_MoveWindow(%p,%d,%d,%d)", pGT, iWindow, iRow, iCol));

   if( iWindow > 0 && iWindow <= s_iOpenWindows )
   {
      PHB_CT_WND pWnd = s_windows[ iWindow ];

      if( pWnd )
      {
         if( ( iRow + ( s_fBoardTop ? pWnd->iHeight : 0 ) >= s_iBoardTop ) &&
             ( iRow + ( s_fBoardBottom ? 0 : pWnd->iHeight ) <= s_iBoardBottom + 1 ) &&
             ( iCol + ( s_fBoardLeft ? pWnd->iWidth : 0 ) >= s_iBoardLeft ) &&
             ( iCol + ( s_fBoardRight ? 0 : pWnd->iWidth ) <= s_iBoardRight + 1 ) )
         {
            pWnd->iFirstRow = iRow;
            pWnd->iFirstCol = iCol;
            if( ! pWnd->fHidden )
               hb_ctw_RemapAllWindows( pGT );
            return iWindow;
         }
      }
   }

   return -1;
}

static int hb_ctw_ChangeMargins( PHB_GT pGT, int iWindow, int iTop, int iLeft, int iBottom, int iRight )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_ChangeMargins(%p,%d,%d,%d,%d,%d)", pGT, iWindow, iTop, iLeft, iBottom, iRight));

   HB_SYMBOL_UNUSED( pGT );

   if( iWindow > 0 && iWindow <= s_iOpenWindows )
   {
      PHB_CT_WND pWnd = s_windows[ iWindow ];

      if( pWnd )
      {
         if( ( iTop += pWnd->iTopMargin ) < 0 )
            iTop = 0;
         if( ( iLeft += pWnd->iLeftMargin ) < 0 )
            iLeft = 0;
         if( ( iBottom += pWnd->iBottomMargin ) < 0 )
            iBottom = 0;
         if( ( iRight += pWnd->iRightMargin ) < 0 )
            iRight = 0;

         if( iTop + iBottom < pWnd->iHeight && iLeft + iRight < pWnd->iWidth )
         {
            pWnd->iTopMargin    = iTop;
            pWnd->iLeftMargin   = iLeft;
            pWnd->iBottomMargin = iBottom;
            pWnd->iRightMargin  = iRight;

            return iWindow;
         }
      }
   }

   return -1;
}

static int hb_ctw_GetWindowCords( PHB_GT pGT, int iWindow, BOOL fCenter, int * piTop, int * piLeft, int * piBottom, int * piRight )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_GetWindowCords(%p,%d,%d,%p,%p,%p,%p)", pGT, iWindow, (int) fCenter, piTop, piLeft, piBottom, piRight));

   if( iWindow > 0 && iWindow <= s_iOpenWindows )
   {
      PHB_CT_WND pWnd = s_windows[ iWindow ];

      if( pWnd )
      {
         if( fCenter )
         {
            int iHeight = s_iBoardBottom - s_iBoardTop + 1,
                iWidth = s_iBoardRight - s_iBoardLeft + 1;

            *piTop  = s_iBoardTop;
            *piLeft = s_iBoardLeft;

            if( iHeight > pWnd->iHeight )
               *piTop += ( iHeight - pWnd->iHeight ) >> 1;
            if( iWidth > pWnd->iWidth )
               *piLeft += ( iWidth - pWnd->iWidth ) >> 1;
         }
         else
         {
            *piTop  = pWnd->iFirstRow;
            *piLeft = pWnd->iFirstCol;
         }
         *piBottom = *piTop + pWnd->iHeight - 1;
         *piRight  = *piLeft + pWnd->iWidth - 1;

         return iWindow;
      }
   }

   *piTop = *piLeft = 0;
   *piBottom = HB_GTSELF_MAXROW( pGT );
   *piRight  = HB_GTSELF_MAXCOL( pGT );

   return -1;
}

static int hb_ctw_GetFormatCords( PHB_GT pGT, int iWindow, BOOL fRelative, int * piTop, int * piLeft, int * piBottom, int * piRight )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_GetFormatCords(%p,%d,%d,%p,%p,%p,%p)", pGT, iWindow, (int) fRelative, piTop, piLeft, piBottom, piRight));

   if( iWindow > 0 && iWindow <= s_iOpenWindows )
   {
      PHB_CT_WND pWnd = s_windows[ iWindow ];

      if( pWnd )
      {
         if( fRelative )
         {
            *piTop    = pWnd->iTopMargin;
            *piLeft   = pWnd->iLeftMargin;
            *piBottom = pWnd->iBottomMargin;
            *piRight  = pWnd->iRightMargin;
         }
         else
         {
            *piTop    = pWnd->iFirstRow + pWnd->iTopMargin;
            *piLeft   = pWnd->iFirstCol + pWnd->iLeftMargin;
            *piBottom = pWnd->iFirstRow + pWnd->iHeight - pWnd->iBottomMargin - 1;
            *piRight  = pWnd->iFirstCol + pWnd->iWidth - pWnd->iRightMargin - 1;
         }
         return iWindow;
      }
   }

   if( fRelative )
   {
      *piTop = *piLeft = *piBottom = *piRight = 0;
   }
   else
   {
      *piTop = *piLeft = 0;
      *piBottom = HB_GTSELF_MAXROW( pGT );
      *piRight  = HB_GTSELF_MAXCOL( pGT );
   }

   return -1;
}

/* ********************************************************************** */

static int hb_ctw_AddWindowBox( PHB_GT pGT, int iWindow, BYTE * szBox, int iColor )
{
   int iMaxRow, iMaxCol;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_AddWindowBox(%p,%d,%p,%d)", pGT, iWindow, szBox, iColor));

   iMaxRow = HB_GTSELF_MAXROW( pGT );
   iMaxCol = HB_GTSELF_MAXCOL( pGT );

   if( iMaxRow > 1 && iMaxCol > 1 )
   {
      if( iColor == 0 )
         iColor = HB_GTSELF_GETCOLOR( pGT );
      HB_GTSELF_BOX( pGT, 0, 0, iMaxRow, iMaxCol, szBox, iColor );
      if( iWindow > 0 && iWindow <= s_iOpenWindows &&
          s_windows[ iWindow ] != NULL )
      {
         HB_GTSELF_SETPOS( pGT, 0, 0 );
         hb_ctw_ChangeMargins( pGT, iWindow, 1, 1, 1, 1 );
      }
      else
         HB_GTSELF_SETPOS( pGT, 1, 1 );

      return 0;
   }

   return -1;
}

/* ********************************************************************** */

static void hb_ctw_gt_Init( PHB_GT pGT, FHANDLE hFilenoStdin, FHANDLE hFilenoStdout, FHANDLE hFilenoStderr )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_Init(%p,%p,%p,%p)", pGT, hFilenoStdin, hFilenoStdout, hFilenoStderr));

   HB_GTSUPER_INIT( pGT, hFilenoStdin, hFilenoStdout, hFilenoStderr );

   s_fInit = TRUE;
}

static void hb_ctw_gt_Exit( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_Exit(%p)", pGT));

   if( s_iMaxWindow > 0 )
   {
      hb_ctw_CloseAllWindows( pGT );

      hb_xfree( s_windows );
      hb_xfree( s_windowStack );
      hb_xfree( s_pWindowMap );
      hb_xfree( s_pShadowMap );
      s_windows = NULL;
      s_windowStack = NULL;
      s_pWindowMap = s_pShadowMap = NULL;
      s_iMaxWindow = s_iOpenWindows = s_iCurrWindow =
      s_iMapWidth = s_iMapHeight = 0;
      s_fBoardSet = s_fInit = FALSE;
   }

   HB_GTSUPER_EXIT( pGT );
}

static int hb_ctw_MouseRow( PHB_GT pGT )
{
   int iRow;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_MouseRow(%p)", pGT));

   iRow = HB_GTSUPER_MOUSEROW( pGT );

   if( s_iCurrWindow > 0 )
      iRow -= s_windows[ s_iCurrWindow ]->iFirstRow +
              s_windows[ s_iCurrWindow ]->iTopMargin;

   return iRow;
}

static int hb_ctw_MouseCol( PHB_GT pGT )
{
   int iCol;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_MouseCol(%p)", pGT));

   iCol = HB_GTSUPER_MOUSECOL( pGT );

   if( s_iCurrWindow > 0 )
      iCol -= s_windows[ s_iCurrWindow ]->iFirstCol +
              s_windows[ s_iCurrWindow ]->iLeftMargin;

   return iCol;
}

static void hb_ctw_gt_GetPos( PHB_GT pGT, int * piRow, int * piCol )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetPos(%p,%p,%p)", pGT, piRow, piCol));

   if( s_iCurrWindow > 0 )
   {
      *piRow = s_windows[ s_iCurrWindow ]->iRow;
      *piCol = s_windows[ s_iCurrWindow ]->iCol;
   }
   else
      HB_GTSUPER_GETPOS( pGT, piRow, piCol );
}

/*
 * CTWIN uses differ rules when set cursor position out of screen visible
 * area then standard Clipper's GT drivers so we have to replicate it in
 * SETPOS() method, [druzus]
 */
static void hb_ctw_gt_SetPos( PHB_GT pGT, int iRow, int iCol )
{
   int iHeight, iWidth;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetPos(%p,%d,%d)", pGT, iRow, iCol));

   iHeight = HB_GTSELF_MAXROW( pGT ) + 1;
   iWidth = HB_GTSELF_MAXCOL( pGT ) + 1;

   if( iCol > iWidth )
      iCol = iWidth;
   else if( iCol < 0 )
   {
      iRow += iCol / iWidth - 1;
      iCol = iWidth + iCol % iWidth;
   }
   if( iRow > iHeight )
      iRow = iHeight;

   if( s_iCurrWindow > 0 )
   {
      if( iRow < - s_windows[ s_iCurrWindow ]->iTopMargin )
         iRow = - s_windows[ s_iCurrWindow ]->iTopMargin;
      s_windows[ s_iCurrWindow ]->iRow = iRow;
      s_windows[ s_iCurrWindow ]->iCol = iCol;
   }
   else
   {
      if( iRow < 0 )
         iRow = 0;
      HB_GTSUPER_SETPOS( pGT, iRow, iCol );
   }
}

static int hb_ctw_gt_MaxCol( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_MaxCol(%p)", pGT));

   if( s_iCurrWindow > 0 )
      return s_windows[ s_iCurrWindow ]->iWidth -
             s_windows[ s_iCurrWindow ]->iLeftMargin -
             s_windows[ s_iCurrWindow ]->iRightMargin - 1;
   else
      return HB_GTSUPER_MAXCOL( pGT );
}

static int hb_ctw_gt_MaxRow( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_MaxRow(%p)", pGT));

   if( s_iCurrWindow > 0 )
      return s_windows[ s_iCurrWindow ]->iHeight -
             s_windows[ s_iCurrWindow ]->iTopMargin -
             s_windows[ s_iCurrWindow ]->iBottomMargin - 1;
   else
      return HB_GTSUPER_MAXROW( pGT );
}

/*
 * CTWIN uses differ rules in console output then standard Clipper's
 * GT drivers so we have to overload WRITECON() method, [druzus]
 */
#define WRITECON_BUFFER_SIZE 512

static void hb_ctw_gt_WriteCon( PHB_GT pGT, BYTE * pText, ULONG ulLength )
{
   int iLen = 0;
   BOOL bDisp = FALSE;
   BOOL bBell = FALSE;
   int iRow, iCol, iMaxRow, iMaxCol;
   BYTE szString[ WRITECON_BUFFER_SIZE ];

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_WriteCon(%p,%p,%lu)", pGT, pText, ulLength));

   iMaxRow = HB_GTSELF_MAXROW( pGT );
   iMaxCol = HB_GTSELF_MAXCOL( pGT );

   HB_GTSELF_GETPOS( pGT, &iRow, &iCol );

   if( iRow > iMaxRow || iCol > iMaxCol )
   {
      if( iRow > iMaxRow )
         iRow = iMaxRow;
      if( iCol > iMaxCol )
         iCol = iMaxCol;
      HB_GTSELF_SETPOS( pGT, iRow, iCol );
   }

   while( ulLength-- )
   {
      BYTE ch = *pText++;

      switch( ch )
      {
         case HB_CHAR_BEL:
            bDisp = bBell = TRUE;
            break;

         case HB_CHAR_BS:
            if( iCol > 0 )
            {
               --iCol;
               bDisp = TRUE;
            }
            else if( iRow > 0 )
            {
               iCol = iMaxCol;
               --iRow;
               bDisp = TRUE;
            }
            if( bDisp )
            {
               if( iLen )
                  szString[ iLen - 1 ] = ' ';
               else
               {
                  HB_GTSELF_SETPOS( pGT, iRow, iCol );
                  szString[ iLen++ ] = ' ';
               }
            }
            break;

         case HB_CHAR_LF:
            iCol = 0;
            ++iRow;
            bDisp = TRUE;
            break;

         case HB_CHAR_CR:
            iCol = 0;
            if( *pText == HB_CHAR_LF )
            {
               ++iRow;
               ++pText;
               --ulLength;
            }
            bDisp = TRUE;
            break;

         default:
            szString[ iLen++ ] = ch;
            if( ++iCol > iMaxCol )
            {
               iCol = 0;
               ++iRow;
               bDisp = TRUE;
            }
            else if( iLen >= WRITECON_BUFFER_SIZE )
               bDisp = TRUE;
      }

      if( bDisp || ulLength == 0 )
      {
         if( iLen )
            HB_GTSELF_WRITE( pGT, szString, iLen );

         iLen = 0;
         if( iRow > iMaxRow )
         {
            HB_GTSELF_SCROLL( pGT, 0, 0, iMaxRow, iMaxCol,
                              HB_GTSELF_GETCOLOR( pGT ),
                              HB_GTSELF_GETCLEARCHAR( pGT ),
                          iRow - iMaxRow, 0 );
            iRow = iMaxRow;
            iCol = 0;
         }
         HB_GTSELF_SETPOS( pGT, iRow, iCol );
         bDisp = FALSE;

         /* To emulate scrolling */
         HB_GTSELF_FLUSH( pGT );

         if( bBell )
         {
            HB_GTSELF_BELL( pGT );
            bBell = FALSE;
         }
      }
   }
}


static int hb_ctw_gt_GetCursorStyle( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetCursorStyle(%p)", pGT));

   if( s_iCurrWindow > 0 )
      return s_windows[ s_iCurrWindow ]->iCursorStyle;
   else
      return HB_GTSUPER_GETCURSORSTYLE( pGT );
}

static void hb_ctw_gt_SetCursorStyle( PHB_GT pGT, int iStyle )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_SetCursorStyle(%p,%d)", pGT, iStyle));

   if( s_iCurrWindow > 0 )
   {
      switch( iStyle )
      {
         case SC_NONE:
         case SC_NORMAL:
         case SC_INSERT:
         case SC_SPECIAL1:
         case SC_SPECIAL2:
            s_windows[ s_iCurrWindow ]->iCursorStyle = iStyle;
            break;
         default:
            s_windows[ s_iCurrWindow ]->iCursorStyle = SC_NORMAL;
            break;
      }
   }
   else
      HB_GTSUPER_SETCURSORSTYLE( pGT, iStyle );
}

static void hb_ctw_gt_GetColorStr( PHB_GT pGT, char * pszColorString )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetColorStr(%p,%p)", pGT, pszColorString));

   if( s_iCurrWindow > 0 )
   {
      PHB_CT_WND pWnd = s_windows[ s_iCurrWindow ];
      HB_GTSUPER_COLORSTOSTRING( pGT, pWnd->piColors, pWnd->iColorCount, pszColorString, CLR_STRLEN );
   }
   else
      HB_GTSUPER_GETCOLORSTR( pGT, pszColorString );
}

static void hb_ctw_gt_SetColorStr( PHB_GT pGT, const char * szColorString )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_SetColorStr(%p,%s)", pGT, szColorString));

   if( s_iCurrWindow > 0 )
   {
      PHB_CT_WND pWnd = s_windows[ s_iCurrWindow ];
      HB_GTSUPER_STRINGTOCOLORS( pGT, szColorString, &pWnd->piColors, &pWnd->iColorCount );
      pWnd->iColorIndex = HB_CLR_STANDARD;
   }
   else
      HB_GTSUPER_SETCOLORSTR( pGT, szColorString );
}

static void hb_ctw_gt_ColorSelect( PHB_GT pGT, int iColorIndex )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_ColorSelect(%p,%d)", pGT, iColorIndex));

   if( s_iCurrWindow > 0 )
   {
      PHB_CT_WND pWnd = s_windows[ s_iCurrWindow ];
      if( iColorIndex >= 0 && iColorIndex < pWnd->iColorCount )
         pWnd->iColorIndex = iColorIndex;
   }
   else
      HB_GTSUPER_COLORSELECT( pGT, iColorIndex );
}

static int hb_ctw_gt_GetColor( PHB_GT pGT )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetColor(%p)", pGT));

   if( s_iCurrWindow > 0 )
   {
      PHB_CT_WND pWnd = s_windows[ s_iCurrWindow ];
      return pWnd->piColors[ pWnd->iColorIndex ];
   }
   else
      return HB_GTSUPER_GETCOLOR( pGT );
}

static void hb_ctw_gt_GetColorData( PHB_GT pGT, int ** pColorsPtr, int * piColorCount, int * piColorIndex )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetColor(%p,%p,%p,%p)", pGT, pColorsPtr, piColorCount, piColorIndex));

   if( s_iCurrWindow > 0 )
   {
      PHB_CT_WND pWnd = s_windows[ s_iCurrWindow ];

      *pColorsPtr = ( int * ) hb_xgrab( pWnd->iColorCount * sizeof( int ) );
      memcpy( *pColorsPtr, pWnd->piColors, pWnd->iColorCount * sizeof( int ) );
      *piColorCount = pWnd->iColorCount;
      *piColorIndex = pWnd->iColorIndex;
   }
   else
      HB_GTSUPER_GETCOLORDATA( pGT, pColorsPtr, piColorCount, piColorIndex );
}

static void hb_ctw_gt_GetScrCursor( PHB_GT pGT, int * piRow, int * piCol, int * piStyle )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetScrCursor(%p,%p,%p,%p)", pGT, piRow, piCol, piStyle));

   HB_GTSUPER_GETSCRCURSOR( pGT, piRow, piCol, piStyle );
   if( s_iCurrWindow > 0 )
   {
      *piRow += s_windows[ s_iCurrWindow ]->iFirstRow +
                s_windows[ s_iCurrWindow ]->iTopMargin;
      *piCol += s_windows[ s_iCurrWindow ]->iFirstCol +
                s_windows[ s_iCurrWindow ]->iLeftMargin;
      if( *piStyle != SC_NONE &&
          ( *piRow < s_iBoardTop  || *piRow > s_iBoardBottom ||
            *piCol < s_iBoardLeft || *piCol > s_iBoardRight ) )
         *piStyle = SC_NONE;
   }
}

static BOOL hb_ctw_gt_GetScrChar( PHB_GT pGT, int iRow, int iCol,
                                  BYTE * pbColor, BYTE * pbAttr, USHORT * pusChar )
{
   int iWindow = s_iCurrWindow, iShadow = 0;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetScrChar(%p,%d,%d,%p,%p,%p)", pGT, iRow, iCol, pbColor, pbAttr, pusChar));

   if( s_iOpenWindows > 0 )
   {
      if( iRow < s_iBoardTop  || iRow > s_iBoardBottom ||
          iCol < s_iBoardLeft || iCol > s_iBoardRight )
         iWindow = 0;
      else
      {
         long lIndex = ( long ) iRow * s_iMapWidth + iCol;
         iWindow = s_pWindowMap[ lIndex ];
         iShadow = s_pShadowMap[ lIndex ];
      }
   }

   if( iWindow > 0 )
   {
      iRow -= s_windows[ iWindow ]->iFirstRow;
      iCol -= s_windows[ iWindow ]->iFirstCol;
      if( iCol >= 0 && iRow >= 0 && iRow < s_windows[ iWindow ]->iHeight &&
          iCol < s_windows[ iWindow ]->iWidth )
      {
         long lIndex = ( long ) iRow * s_windows[ iWindow ]->iWidth + iCol;
         *pusChar = s_windows[ iWindow ]->screenBuffer[ lIndex ].c.usChar;
         *pbColor = s_windows[ iWindow ]->screenBuffer[ lIndex ].c.bColor;
         *pbAttr  = s_windows[ iWindow ]->screenBuffer[ lIndex ].c.bAttr;
      }
      else
         return FALSE;
   }
   else if( ! HB_GTSUPER_GETSCRCHAR( pGT, iRow, iCol, pbColor, pbAttr, pusChar ) )
      return FALSE;

   if( iShadow > 0 )
   {
      if( s_windows[ iShadow ]->iShadowAttr >= 0 )
         *pbColor = ( BYTE ) s_windows[ iShadow ]->iShadowAttr;
      *pbAttr |= HB_GT_ATTR_SHADOW;
   }

   return TRUE;
}

static BOOL hb_ctw_gt_GetChar( PHB_GT pGT, int iRow, int iCol,
                               BYTE * pbColor, BYTE * pbAttr, USHORT * pusChar )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_GetChar(%p,%d,%d,%p,%p,%p)", pGT, iRow, iCol, pbColor, pbAttr, pusChar));

   if( s_iCurrWindow == 0 )
      /* TODO: it may badly interacts with character translations */
      return HB_GTSELF_GETSCRCHAR( pGT, iRow, iCol, pbColor, pbAttr, pusChar );

   iRow += s_windows[ s_iCurrWindow ]->iTopMargin;
   iCol += s_windows[ s_iCurrWindow ]->iLeftMargin;

   if( iCol >= 0 && iRow >= 0 &&
       iRow < s_windows[ s_iCurrWindow ]->iHeight &&
       iCol < s_windows[ s_iCurrWindow ]->iWidth )
   {
      long lIndex = ( long ) iRow * s_windows[ s_iCurrWindow ]->iWidth + iCol;
      *pusChar = s_windows[ s_iCurrWindow ]->screenBuffer[ lIndex ].c.usChar;
      *pbColor = s_windows[ s_iCurrWindow ]->screenBuffer[ lIndex ].c.bColor;
      *pbAttr  = s_windows[ s_iCurrWindow ]->screenBuffer[ lIndex ].c.bAttr;
      return TRUE;
   }

   return FALSE;
}

static BOOL hb_ctw_gt_PutChar( PHB_GT pGT, int iRow, int iCol,
                               BYTE bColor, BYTE bAttr, USHORT usChar )
{
   int iWindow = s_iCurrWindow;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_PutChar(%p,%d,%d,%d,%d,%hu)", pGT, iRow, iCol, (int)bColor, (int)bAttr, (int)usChar));

   if( s_iOpenWindows > 0 && iWindow == 0 )
   {
      if( iRow >= s_iBoardTop  && iRow <= s_iBoardBottom &&
          iCol >= s_iBoardLeft && iCol <= s_iBoardRight )
      {
         long lIndex = ( long ) iRow * s_iMapWidth + iCol;
         iWindow = s_pWindowMap[ lIndex ];
#if 0
         /* When window with shadow is closed CT3 restores attributes
          * which existed before shadow was displayed. In some application
          * which switches to window 0 for pass-throw output it causes that
          * wrong attributes appears after this operation. In Harbour it's
          * fixed so such problem do not exist. Anyhow some code may switch
          * to window 0, make savescreen()/restscreen() and in such case
          * all shadow attributes are copied to window 0 buffer. The code
          * below is workaround for it. [druzus]
          */
         if( s_pShadowMap[ lIndex ] != 0 )
         {
            int iShadow = s_pShadowMap[ lIndex ];
            if( s_windows[ iShadow ]->iShadowAttr >= 0 &&
                ( BYTE ) s_windows[ iShadow ]->iShadowAttr == bColor )
            {
               BYTE bClr, bAtr;
               USHORT usCh;
               if( HB_GTSELF_GETSCRCHAR( pGT, iRow, iCol, &bClr, &bAtr, &usCh ) )
               {
                  if( usCh == usChar && bClr == bColor )
                     return TRUE;
               }
            }
         }
#endif
         s_pShadowMap[ lIndex ] = 0;
      }
   }

   if( iWindow > 0 )
   {
      int iWndRow, iWndCol, iWndHeight, iWndWidth;

      if( s_iCurrWindow == 0 )
      {
         iWndRow = iRow - s_windows[ iWindow ]->iFirstRow;
         iWndCol = iCol - s_windows[ iWindow ]->iFirstCol;
         iWndHeight = s_windows[ iWindow ]->iWidth;
         iWndWidth  = s_windows[ iWindow ]->iWidth;
      }
      else
      {
         iWndRow = iRow + s_windows[ iWindow ]->iTopMargin;
         iWndCol = iCol + s_windows[ iWindow ]->iLeftMargin;
         iRow = iWndRow + s_windows[ iWindow ]->iFirstRow;
         iCol = iWndCol + s_windows[ iWindow ]->iFirstCol;
         iWndHeight = s_windows[ iWindow ]->iHeight -
                      s_windows[ iWindow ]->iBottomMargin;
         iWndWidth  = s_windows[ iWindow ]->iWidth -
                      s_windows[ iWindow ]->iRightMargin;
      }
      if( iWndCol >= 0 && iWndRow >= 0 &&
          iWndRow < iWndHeight &&
          iWndCol < iWndWidth )
      {
         long lIndex = ( long ) iWndRow * s_windows[ iWindow ]->iWidth + iWndCol;

         s_windows[ iWindow ]->screenBuffer[ lIndex ].c.usChar = usChar;
         s_windows[ iWindow ]->screenBuffer[ lIndex ].c.bColor = bColor;
         s_windows[ iWindow ]->screenBuffer[ lIndex ].c.bAttr  = bAttr;
         if( ! s_windows[ iWindow ]->fHidden )
         {
            if( s_iCurrWindow == 0 )
               HB_GTSUPER_TOUCHCELL( pGT, iRow, iCol );
            else if( iRow >= s_iBoardTop  && iRow <= s_iBoardBottom &&
                     iCol >= s_iBoardLeft && iCol <= s_iBoardRight )
               HB_GTSUPER_TOUCHCELL( pGT, iRow, iCol );
         }
         return TRUE;
      }
      return FALSE;
   }

   return HB_GTSUPER_PUTCHAR( pGT, iRow, iCol, bColor, bAttr, usChar );
}

static BOOL hb_ctw_gt_Resize( PHB_GT pGT, int iRows, int iCols )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_Resize(%p,%d,%d)", pGT, iRows, iCols));

   if( HB_GTSUPER_RESIZE( pGT, iRows, iCols ) )
   {
      if( s_iMaxWindow > 0 )
      {
         ULONG ulSize;

         s_iMapHeight = iRows;
         s_iMapWidth  = iCols;
         s_iShadowWidth = hb_ctw_CalcShadowWidth( s_iMapHeight, s_iMapWidth );
         ulSize = ( ULONG ) s_iMapHeight * s_iMapWidth * sizeof( int );
         s_pWindowMap = ( int * ) hb_xrealloc( s_pWindowMap, ulSize );
         s_pShadowMap = ( int * ) hb_xrealloc( s_pShadowMap, ulSize );
      }
      if( s_fBoardSet )
         hb_ctw_SetWindowBoard( pGT, 0, 0, iRows - 1, iCols - 1 );
      return TRUE;
   }
   return FALSE;
}

static BOOL hb_ctw_gt_Info( PHB_GT pGT, int iType, PHB_GT_INFO pInfo )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_Info(%p,%d,%p)", pGT, iType, pInfo));

   switch ( iType )
   {
      case GTI_ISCTWIN:
         pInfo->pResult = hb_itemPutL( pInfo->pResult, TRUE );
         break;

      case GTI_NEWWIN:
      {
         BOOL fResult;

         hb_ctw_SelectWindow( pGT, 0 );
         fResult = HB_GTSUPER_INFO( pGT, iType, pInfo );

         if( fResult && hb_arrayLen( pInfo->pResult ) >= 8 )
            hb_itemPutNI( hb_arrayGetItemPtr( pInfo->pResult, 8 ),
                          s_iCurrWindow );
         return fResult;
      }
      case GTI_GETWIN:
      {
         BOOL fResult;
         int iWindow = s_iCurrWindow;

         hb_ctw_SelectWindow( pGT, 0 );
         fResult = HB_GTSUPER_INFO( pGT, iType, pInfo );
         if( fResult && hb_arrayLen( pInfo->pResult ) >= 8 )
            hb_itemPutNI( hb_arrayGetItemPtr( pInfo->pResult, 8 ), iWindow );
         return fResult;
      }
      case GTI_SETWIN:
      {
         BOOL fResult;

         hb_ctw_SelectWindow( pGT, 0 );
         fResult = HB_GTSUPER_INFO( pGT, iType, pInfo );
         if( hb_arrayLen( pInfo->pNewVal ) == 8 )
            hb_ctw_SelectWindow( pGT, hb_arrayGetNI( pInfo->pNewVal, 8 ) );
         return fResult;
      }
      default:
         return HB_GTSUPER_INFO( pGT, iType, pInfo );
   }

   return TRUE;
}

static int hb_ctw_gt_Alert( PHB_GT pGT, PHB_ITEM pMessage, PHB_ITEM pOptions,
                            int iClrNorm, int iClrHigh, double dDelay )
{
   int iOptions, iRet = 0;

   HB_TRACE( HB_TR_DEBUG, ("hb_ctw_gt_Alert(%p,%p,%p,%d,%d,%f)", pGT, pMessage, pOptions, iClrNorm, iClrHigh, dDelay ) );

   iOptions = ( int ) hb_arrayLen( pOptions );

   if( HB_IS_STRING( pMessage ) && iOptions > 0 )
   {
      int iRows, iCols;
      BOOL fScreen;

      HB_GTSELF_GETSIZE( pGT, &iRows, &iCols );
      if( iCols <= 4 || iRows <= 4 )
         fScreen = FALSE;
      else
      {
         HB_GT_INFO gtInfo;
         gtInfo.pNewVal = gtInfo.pResult = NULL;
         HB_GTSELF_INFO( pGT, GTI_FULLSCREEN, &gtInfo );
         fScreen = gtInfo.pResult && hb_itemGetL( gtInfo.pResult );
         HB_GTSELF_INFO( pGT, GTI_KBDSUPPORT, &gtInfo );
         if( gtInfo.pResult )
         {
            if( !hb_itemGetL( gtInfo.pResult ) )
               fScreen = FALSE;
            hb_itemRelease( gtInfo.pResult );
         }
      }
      if( fScreen )
      {
         ULONG ulWidth = 0, ulCurrWidth = 0, ul = 0, ul2, ulMaxWidth, ulLast;
         int iKey, iDspCount, iLines = 0, iTop, iLeft, iBottom, iRight,
             iMnuCol, iPos, iClr, iWnd, i;
         char * szMessage = hb_itemGetCPtr( pMessage );
         ULONG ulLen = hb_itemGetCLen( pMessage );

         ulMaxWidth = iCols - 4;
         while( ul < ulLen )
         {
            if( szMessage[ ul ] == '\n' )
            {
               ++iLines;
               if( ulCurrWidth > ulWidth )
                  ulWidth = ulCurrWidth;
               ulCurrWidth = 0;
            }
            else
               ++ulCurrWidth;
            ++ul;
         }
         if( ulCurrWidth )
            ++iLines;
         if( ulCurrWidth > ulWidth )
            ulWidth = ulCurrWidth;
         ulCurrWidth = 0;
         for( i = 1; i <= iOptions; ++i )
         {
            ulCurrWidth += hb_arrayGetCLen( pOptions, i ) + ( i > 1 ? 3 : 0 );
         }
         if( ulCurrWidth > ulWidth )
            ulWidth = ulCurrWidth;
         if( ulWidth > ulMaxWidth )
            ulWidth = ulMaxWidth;
         if( iRows < iLines + 4 )
            iLines = iRows - 4;
         iTop = ( iRows - iLines - 4 ) >> 1;
         iLeft = ( iCols - ulWidth - 4 ) >> 1;
         iBottom = iTop + iLines + 3;
         iRight = iLeft + ulWidth + 3;
         if( iClrNorm == 0 )
            iClrNorm = 79;
         if( iClrHigh == 0 )
            iClrHigh = 31;

         iDspCount = HB_GTSELF_DISPCOUNT( pGT );
         if( iDspCount == 0 )
            HB_GTSELF_DISPBEGIN( pGT );

         iWnd = hb_ctw_CreateWindow( pGT, iTop, iLeft, iBottom, iRight, TRUE, iClrNorm );
         hb_ctw_AddWindowBox( pGT, iWnd, ( BYTE * ) _B_SINGLE, iClrNorm );
         HB_GTSELF_SETCURSORSTYLE( pGT, SC_NONE );
         ulLast = 0;
         i = 0;
         for( ul = 0; ul < ulLen; ++ul )
         {
            if( szMessage[ ul ] == '\n' )
            {
               if( ul > ulLast )
               {
                  ul2 = ul - ulLast;
                  if( ul2 > ulWidth )
                     ul2 = ulWidth;
                  HB_GTSELF_PUTTEXT( pGT, i, ( ( ulWidth - ul2 + 1 ) >> 1 ) + 1, iClrNorm,
                                     ( BYTE * ) szMessage + ulLast, ul2 );
               }
               ulLast = ul + 1;
               if( ++i >= iLines )
                  break;
            }
         }
         if( ul > ulLast && i < iLines )
         {
            ul2 = ul - ulLast;
            if( ul2 > ulWidth )
               ul2 = ulWidth;
            HB_GTSELF_PUTTEXT( pGT, i, ( ( ulWidth - ul2 + 1 ) >> 1 ) + 1, iClrNorm,
                               ( BYTE * ) szMessage + ulLast, ul2 );
         }

         iPos = 1;
         while( iRet == 0 )
         {
            HB_GTSELF_DISPBEGIN( pGT );
            iMnuCol = ( ( ulWidth - ulCurrWidth ) >> 1 ) + 1;
            for( i = 1; i <= iOptions; ++i )
            {
               iClr = i == iPos ? iClrHigh : iClrNorm;
               ulLen = hb_arrayGetCLen( pOptions, i );
               HB_GTSELF_PUTTEXT( pGT, iLines + 1, iMnuCol, iClr,
                                  ( BYTE * ) hb_arrayGetCPtr( pOptions, i ), ulLen );
               iMnuCol += ulLen + 3;
            }
            while( HB_GTSELF_DISPCOUNT( pGT ) )
               HB_GTSELF_DISPEND( pGT );
            HB_GTSELF_REFRESH( pGT );

            iKey = HB_GTSELF_INKEYGET( pGT, TRUE, dDelay, INKEY_ALL );
            /* TODO: add support for SET KEY blocks */

            if( iKey == K_ESC )
               break;
            else if( iKey == K_ENTER || iKey == K_SPACE || iKey == 0 )
            {
               iRet = iPos;
            }
            else if( iKey == K_LEFT || iKey == K_SH_TAB )
            {
               if( --iPos == 0 )
                  iPos = iOptions;
               dDelay = 0.0;
            }
            else if( iKey == K_RIGHT || iKey == K_TAB )
            {
               if( ++iPos > iOptions )
                  iPos = 1;
               dDelay = 0.0;
            }
#ifdef HB_COMPAT_C53
            else if( iKey == K_LBUTTONDOWN )
            {
               int iMRow = HB_GTSELF_MOUSEROW( pGT ),
                   iMCol = HB_GTSELF_MOUSECOL( pGT );
               if( iMRow == iLines + 1 )
               {
                  iMnuCol = ( ( ulWidth - ulCurrWidth ) >> 1 ) + 1;
                  for( i = 1; i <= iOptions; ++i )
                  {
                     ulLen = hb_arrayGetCLen( pOptions, i );
                     if( iMCol >= iMnuCol && iMCol < iMnuCol + ( int ) ulLen )
                     {
                        iRet = i;
                        break;
                     }
                     iMnuCol += ulLen + 3;
                  }
               }
            }
#endif
            else if( iKey >= 32 && iKey <= 255 )
            {
               int iUp = hb_charUpper( iKey );
               for( i = 1; i <= iOptions; ++i )
               {
                  char *szValue = hb_arrayGetCPtr( pOptions, i );
                  if( szValue && iUp == hb_charUpper( *szValue ) )
                  {
                     iRet = i;
                     break;
                  }
               }
            }
         }

         hb_ctw_CloseWindow( pGT, iWnd );
         HB_GTSELF_REFRESH( pGT );

         while( HB_GTSELF_DISPCOUNT( pGT ) < iDspCount )
            HB_GTSELF_DISPBEGIN( pGT );

         return iRet;
      }
   }

   return HB_GTSUPER_ALERT( pGT, pMessage, pOptions, iClrNorm, iClrHigh, dDelay );
}

static int hb_ctw_gt_ReadKey( PHB_GT pGT, int iEventMask )
{
   int iKey;

   HB_TRACE(HB_TR_DEBUG, ("hb_ctw_gt_ReadKey(%p,%d)", pGT, iEventMask));

   iKey = HB_GTSUPER_READKEY( pGT, iEventMask );

   if( iKey != 0 )
      s_iLastKey = iKey;

   return iKey;
}

/* PUBLIC FUNCTIONS */

BOOL hb_ctwInit( void )
{
   PHB_GT pGT = hb_gt_Base();
   BOOL fResult = FALSE;
   if( pGT )
      fResult = hb_ctw_Init( pGT );
   return fResult;
}

int  hb_ctwSetShadowAttr( int iAttr )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_SetShadowAttr( pGT, iAttr );
   return iResult;
}

int  hb_ctwSetMoveMode( int iMode )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_SetMoveMode( pGT, iMode );
   return iResult;
}

int  hb_ctwSetMoveStep( int iVertical, int iHorizontal )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_SetMoveStep( pGT, iVertical, iHorizontal );
   return iResult;
}

int  hb_ctwSetWindowBoard( int iTop, int iLeft, int iBottom, int iRight )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
   {
      iResult = hb_ctw_SetWindowBoard( pGT, iTop, iLeft, iBottom, iRight );
      HB_GTSELF_FLUSH( pGT );
   }
   return iResult;
}

int  hb_ctwSetBorderMode( int iTop, int iLeft, int iBottom, int iRight )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_SetBorderMode( pGT, iTop, iLeft, iBottom, iRight );
   return iResult;
}

int  hb_ctwCreateWindow( int iTop, int iLeft, int iBottom, int iRight, BOOL fClear, int iColor )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
   {
      iResult = hb_ctw_CreateWindow( pGT, iTop, iLeft, iBottom, iRight, fClear, iColor );
      HB_GTSELF_FLUSH( pGT );
   }
   return iResult;
}

int  hb_ctwCloseAllWindows( void )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
   {
      iResult = hb_ctw_CloseAllWindows( pGT );
      HB_GTSELF_FLUSH( pGT );
   }
   return iResult;
}

int  hb_ctwCloseWindow( int iWindow )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
   {
      iResult = hb_ctw_CloseWindow( pGT, iWindow );
      HB_GTSELF_FLUSH( pGT );
   }
   return iResult;
}

int  hb_ctwCurrentWindow( void )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = 0;
   if( pGT )
      iResult = hb_ctw_CurrentWindow( pGT );
   return iResult;
}

int  hb_ctwSelectWindow( int iWindow )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = 0;
   if( pGT )
   {
      iResult = hb_ctw_SelectWindow( pGT, iWindow );
      HB_GTSELF_FLUSH( pGT );
   }
   return iResult;
}

int  hb_ctwMaxWindow( void )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = 0;
   if( pGT )
      iResult = hb_ctw_MaxWindow( pGT );
   return iResult;
}

int  hb_ctwChangeMargins( int iWindow, int iTop, int iLeft, int iBottom, int iRight )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_ChangeMargins( pGT, iWindow, iTop, iLeft, iBottom, iRight );
   return iResult;
}

int  hb_ctwGetWindowCords( int iWindow, BOOL fCenter, int * piTop, int * piLeft, int * piBottom, int * piRight )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_GetWindowCords( pGT, iWindow, fCenter, piTop, piLeft, piBottom, piRight );
   return iResult;
}

int  hb_ctwGetFormatCords( int iWindow, BOOL fRelative, int * piTop, int * piLeft, int * piBottom, int * piRight )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_GetFormatCords( pGT, iWindow, fRelative, piTop, piLeft, piBottom, piRight );
   return iResult;
}

int  hb_ctwMoveWindow( int iWindow, int iRow, int iCol )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_MoveWindow( pGT, iWindow, iRow, iCol );
   return iResult;
}

int  hb_ctwCenterWindow( int iWindow, BOOL fCenter )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
      iResult = hb_ctw_CenterWindow( pGT, iWindow, fCenter );
   return iResult;
}

int  hb_ctwAddWindowBox( int iWindow, BYTE * szBox, int iColor )
{
   PHB_GT pGT = hb_gt_Base();
   int iResult = -1;
   if( pGT )
   {
      iResult = hb_ctw_AddWindowBox( pGT, iWindow, szBox, iColor );
      HB_GTSELF_FLUSH( pGT );
   }
   return iResult;
}

int  hb_ctwLastKey( void )
{
   /* keyread() in CT3 uses 64512 bytes length buffer
    * when it reach this limit and new key is added the
    * buffer size is decreased by 1024 to 63488 bytes
    * before adding key. TODO: check id buffer is shifted
    */
   if( !s_fInit )
   {
      PHB_GT pGT = hb_gt_Base();
      if( pGT )
         hb_ctw_Init( pGT );
   }
   return s_iLastKey;
}

static BOOL hb_gt_FuncInit( PHB_GT_FUNCS pFuncTable )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_gt_FuncInit(%p)", pFuncTable));

   pFuncTable->Init                       = hb_ctw_gt_Init;
   pFuncTable->Exit                       = hb_ctw_gt_Exit;
   pFuncTable->MouseRow                   = hb_ctw_MouseRow;
   pFuncTable->MouseCol                   = hb_ctw_MouseCol;
   pFuncTable->MaxCol                     = hb_ctw_gt_MaxCol;
   pFuncTable->MaxRow                     = hb_ctw_gt_MaxRow;
   pFuncTable->GetPos                     = hb_ctw_gt_GetPos;
   pFuncTable->SetPos                     = hb_ctw_gt_SetPos;
   pFuncTable->WriteCon                   = hb_ctw_gt_WriteCon;
   pFuncTable->GetCursorStyle             = hb_ctw_gt_GetCursorStyle;
   pFuncTable->SetCursorStyle             = hb_ctw_gt_SetCursorStyle;
   pFuncTable->GetColorStr                = hb_ctw_gt_GetColorStr;
   pFuncTable->SetColorStr                = hb_ctw_gt_SetColorStr;
   pFuncTable->ColorSelect                = hb_ctw_gt_ColorSelect;
   pFuncTable->GetColor                   = hb_ctw_gt_GetColor;
   pFuncTable->GetColorData               = hb_ctw_gt_GetColorData;
   pFuncTable->GetScrCursor               = hb_ctw_gt_GetScrCursor;
   pFuncTable->GetScrChar                 = hb_ctw_gt_GetScrChar;
   pFuncTable->GetChar                    = hb_ctw_gt_GetChar;
   pFuncTable->PutChar                    = hb_ctw_gt_PutChar;
   pFuncTable->Resize                     = hb_ctw_gt_Resize;
   pFuncTable->Info                       = hb_ctw_gt_Info;
   pFuncTable->Alert                      = hb_ctw_gt_Alert;
   pFuncTable->ReadKey                    = hb_ctw_gt_ReadKey;

   return TRUE;
}

/* ********************************************************************** */

static const HB_GT_INIT gtInit = { HB_GT_DRVNAME( HB_GT_NAME ),
                                   hb_gt_FuncInit,
                                   HB_GTSUPER,
                                   HB_GTID_PTR };

HB_GT_ANNOUNCE( HB_GT_NAME )

HB_CALL_ON_STARTUP_BEGIN( _hb_startup_gt_Init_ )
   hb_gtRegister( &gtInit );
HB_CALL_ON_STARTUP_END( _hb_startup_gt_Init_ )

#if defined( HB_PRAGMA_STARTUP )
   #pragma startup _hb_startup_gt_Init_
#elif defined(HB_MSC_STARTUP)
   #pragma data_seg( HB_MSC_START_SEGMENT )
   static HB_$INITSYM hb_vm_auto__hb_startup_gt_Init_ = _hb_startup_gt_Init_;
   #pragma data_seg()
#endif

/* *********************************************************************** */
