#include "project.h"

SOBID sobIndex = 0;
SOBID gpSobIndex = 0;

SOBDATA* menuBarData = NULL;
DWORD styleOverride = 0;

UINT ShowState = SWP_HIDEWINDOW; // SWP_HIDEWINDOW  SWP_SHOWWINDOW;

myUInt sobDisplaying = 1;

SOBDATA* errorRichTextEdit = NULL;

#define defEdge 2
int lEdge = defEdge, rEdge = defEdge   , tEdge = defEdge, bEdge = defEdge ;
RECT sobBnd = { defEdge,defEdge,defEdge,defEdge };

SOBDATA sobData[MAX_NBR_SOBS+1] = { 0 };

// ----------------------------------------------------------------
// tricks to allow the MAINTYPE ENUM to be incremented / decremented
// ----------------------------------------------------------------

CONTYPE sobConType = SOB_CONTAINER_INVALID;

#ifdef __cplusplus
    static CONTYPE operator++(CONTYPE a)
{
    return static_cast<CONTYPE>((static_cast<int>(a) + 1) );
}
static CONTYPE operator--(CONTYPE a)
{
    return static_cast<CONTYPE>((static_cast<int>(a) - 1));
}
#endif

CONTYPE int_next_contype()
{
    sobConType = ++sobConType;
    return  sobConType;
}

void int_return_contype()
{
    if (sobConType NEQU SOB_CONTAINER_INVALID)   sobConType = --sobConType;
}

// ----------------------------------------------------------------
// tricks to allow the MAINTYPE ENUM to be incremented / decremented
// ----------------------------------------------------------------

MAINTYPE sobMainType = SOB_TYPE_INVALID;

#ifdef __cplusplus
    static MAINTYPE operator++(MAINTYPE a)
{
    return static_cast<MAINTYPE>((static_cast<int>(a) + 1));
}
static MAINTYPE operator--(MAINTYPE a)
{
    return static_cast<MAINTYPE>((static_cast<int>(a) - 1));
}
#endif

MAINTYPE int_next_maintype()
{
    sobMainType = ++sobMainType;
    return  sobMainType;
}

void int_return_maintype()
{
    if (sobMainType NEQU SOB_TYPE_INVALID)   sobMainType = --sobMainType;
}

// ---------------------------------

BOOL sob_index_check(SOBID sobId)
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return 1;
    }
    return  0;
}

static void  sob_set_styleOverrid (DWORD tempStyle)
{
    styleOverride  = tempStyle;
}

// ---

SOBID sob_get_pSobIndex()
{
    return gpSobIndex;
}
void  sob_set_pSobIndex(SOBID sobId)
{
    gpSobIndex = sobId;
}
void sob_set_pSobObj(SOBDATA* sData)
{
    gpSobIndex = sData->sIndex;
}

// ---

void sob_close()
{
    // you can not close a TOP LEVEL WINDOW

    if (gpSobIndex < MAX_NBR_SOBS)
    {
        if (sobData[gpSobIndex].pIndex  EQU SOBID_ERROR) return;
        gpSobIndex = sobData[gpSobIndex].pIndex;
    }
    else
    gpSobIndex = SOBID_ERROR;
}

TCHAR* sob_type_to_text(MAINTYPE type)
{
    switch (type)
    {

        case SOB_TYPE_CONTAINER: return (TCHAR*)_TEXT("SOB_TYPE_CONTAINER"); break;
            case SOB_TYPE_INVALID: return (TCHAR*)_TEXT("SOB_TYPE_INVALID"); break;

            default:
            swprintf_s(txtBuffer, szTxtBuffer, L"SOB type value is unknown %04x \n", type);
            return  txtBuffer;
            break;
    }
}

TCHAR* sob_container_to_text(CONTYPE container)
{
    switch (container)
    {
        case SOB_CONTAINER_NO: return (TCHAR*)_TEXT("SOB_CONTAINER_NO"); break;
            case SOB_CONTAINER_WINDOW: return (TCHAR*)_TEXT("SOB_CONTAINER_WINDOW"); break;
            case SOB_CONTAINER_MENU_BAR: return (TCHAR*)_TEXT("SOB_CONTAINER_MENU_BAR"); break;
            case SOB_CONTAINER_MENU_H: return (TCHAR*)_TEXT("SOB_CONTAINER_MENU_H"); break;
            case SOB_CONTAINER_ROW: return (TCHAR*)_TEXT("SOB_CONTAINER_ROW"); break;
            case SOB_CONTAINER_COL: return (TCHAR*)_TEXT("SOB_CONTAINER_COL"); break;
            case SOB_CONTAINER_COL_W: return (TCHAR*)_TEXT("SOB_CONTAINER_COL_W"); break;

            case SOB_CONTAINER_ROWS: return (TCHAR*)_TEXT("SOB_CONTAINER_ROWS"); break;
            case SOB_CONTAINER_COLS: return (TCHAR*)_TEXT("SOB_CONTAINER_COLS"); break;

            case SOB_CONTAINER_INVALID: return (TCHAR*)_TEXT("SOB_CONTAINER_INVALID"); break;

            default:
            swprintf_s(txtBuffer, szTxtBuffer, L"SOB Container value is unknown %04x", container);
            return  txtBuffer;
            break;
    }
}

// ---

static void sob_initialise(SOBID sobId)
{
    SOBDATA* sData;

    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);ReportError(txtBuffer);
        return;
    }
    sData = &sobData[sobId];
    memset(sData, 0, sizeof(SOBDATA));
}

SOBDATA* sob_get_base(SOBID sobId)
{
    if (sob_index_check(sobId)) return NULL;
    return &sobData[sobId];
}

SOBDATA* sob_find_hwnd(HWND hWnd)
{
    SOBID i;
    SOBDATA* cData;
    for (i = 0; i < MAX_NBR_SOBS; i++)
    {
        cData = &sobData[i];
        if ((cData->sIndex)   LAND(cData->hWnd EQU hWnd)) return  cData;
    }
    return NULL;
}

SOBID sob_find_child(SOBID sobId, int childId)
{
    if (sob_index_check(sobId)) return SOBID_ERROR;
    SOBDATA* sData = &sobData[sobId];

    if (!sData->nbrChildren) return SOBID_ERROR;

    if (!(sData->nbrChildren > childId))  return SOBID_ERROR;

    SOBID i;
    SOBDATA* cData;
    for (i = 0; i < MAX_NBR_SOBS; i++)
    {
        cData = &sobData[i];

        if (cData->pIndex EQU sobId)
        {
            if (cData->siblingNbr EQU childId) return  i;
        }
    }
    return SOBID_ERROR;
}

static void sob_unlink(SOBDATA* cData)
{
    if (!cData)  return;
    if (!cData->sIndex)return;

    //   swprintf_s(txtBuffer, szTxtBuffer, L" Unlink    %d\n", cData->sIndex);  ReportError(txtBuffer);

    SOBDATA* pData, * PrevData = NULL, * NextData;

    pData = cData->ppSobData;
    if (pData->headLink   EQU cData)
    {
        NextData = cData->NextSiblingLink;
        pData->headLink = NextData;
        //       swprintf_s(txtBuffer, szTxtBuffer, L" First   %d\n", cData->sIndex);  ReportError(txtBuffer);

        if (! NextData )
        {
            //           swprintf_s(txtBuffer, szTxtBuffer, L" First = Last child    %d\n", cData->sIndex);  ReportError(txtBuffer);

            pData->headLink = NULL;
            pData->tailLink = NULL;
            return;
        }
    }
    else
    {
        PrevData = pData->headLink;
        while ((PrevData)LAND(PrevData->NextSiblingLink   NEQU cData))
        {
            PrevData = PrevData->NextSiblingLink;
        }
        if ( ! PrevData)
        {
            swprintf_s(txtBuffer, szTxtBuffer, L"  Failed on forward unlink %d\n", pData->sIndex);  ReportError(txtBuffer);
            return;
        }

        // swprintf_s(txtBuffer, szTxtBuffer, L"  LATE on forward unlink %d\n", PrevData->sIndex);  ReportError(txtBuffer);

        PrevData->NextSiblingLink = cData->NextSiblingLink;
    }

    if (pData->tailLink   EQU cData)
    {
        PrevData = cData->PreviousSiblingLink;
        pData->tailLink = PrevData;
        if (PrevData EQU pData)
        {
            pData->headLink = NULL;
            pData->tailLink = NULL;
            return;
        }
    }
    else
    {
        NextData = pData->tailLink;
        while ((NextData)LAND(NextData->PreviousSiblingLink NEQU cData))
        {

            NextData = NextData->PreviousSiblingLink;
        }
        if ( (!NextData)  LAND(!PrevData) )
        {
            swprintf_s(txtBuffer, szTxtBuffer, L"  Failed on backward unlink %d\n", PrevData->sIndex); ReportError(txtBuffer);
            return;
        }

        // swprintf_s(txtBuffer, szTxtBuffer, L"  LATE on backward unlink %d\n", NextData->sIndex);  ReportError(txtBuffer);

        NextData->PreviousSiblingLink= cData->PreviousSiblingLink;
    }

    pData->nbrChildren--;
}
// ---------------------------------

// ---------------------------------

TCHAR* sob_get_title(SOBID  sobId)
{
    SOBDATA* sData = sob_get_base(sobId);
    if (sData EQU NULL) return NULL;

    if (sData->type EQU SOB_TYPE_CONTAINER)
    {
        return  (TCHAR*)_TEXT("get title not implemented");
    }
    else
    {
        switch (sData->type)
        {
            case  SOB_TYPE_MENU_V:
                return  sob_menu_title_get(sobId);
                break;

            default:
                return  (TCHAR*)_TEXT("get title not implemented");
                break;
        }
    }
    return NULL;
}

// ----------------------------------------------------------------
//  exposed SOB methods
// ----------------------------------------------------------------

// SOBFNC_SETVALUE  sob_set_stretch;

myUInt  sob_set_stretch(SOBDATA* sData, myUInt value)
{
    SOBDATA* pData = sData->ppSobData;
    if (!pData) return SOB_NOK;

    //   swprintf_s(txtBuffer, szTxtBuffer, L"STRETCH called  %llx  %i  %llx\n",(myUInt) sData , sData->sIndex, value);  ReportError(txtBuffer);

    if (pData->stretchSobData EQU sData)
    {
        pData->stretchSobData = NULL;
        if (value)  pData->stretchSobData = sData;
    }
    else if (value)  pData->stretchSobData = sData;

    return SOB_OK;
}

// SOBFNC_SETVALUE  fnSetStretchGeneric;
static  myUInt  fnSetStretchGeneric(SOBDATA* sData, myUInt value)
{
    SOBDATA* pData = sData->ppSobData;
    if (!pData) return SOB_NOK;
    // swprintf_s(txtBuffer, szTxtBuffer, L"STRETCH called  %llx  %i  %llx\n",(myUInt) sData , sData->sIndex, value);  ReportError(txtBuffer);

    if (pData->stretchSobData EQU sData)
    {
        pData->stretchSobData = NULL;
        if (value)  pData->stretchSobData = sData;
    }
    else if (value)  pData->stretchSobData = sData;

    return SOB_OK;
}
// SOBFNC_SETVALUE  fnSetShareGeneric;
static myUInt  fnSetShareGeneric(SOBDATA* sData, myUInt value)
{
    SOBDATA* pData = sData->ppSobData;
    if (!pData) return SOB_NOK;
    // swprintf_s(txtBuffer, szTxtBuffer, L"STRETCH called  %llx  %i  %llx\n",(myUInt) sData , sData->sIndex, value);  ReportError(txtBuffer);

    sData->share = (int) value;

    return SOB_OK;
}
// ---------------------------------

// SOBFNC_GETVALUEINDIRECT fnGetUserDataGeneric;
myUInt fnGetUserDataGeneric(SOBDATA * sData)
{
    return  sData->userData;

}

// SOBFNC_SETVALUE fnSetUserDataGeneric;
myUInt fnSetUserDataGeneric(SOBDATA* sData, myUInt uData)
{
    sData->userData = uData;
    return 1;
}
// ---------------------------------

//SOBFNC_GETSOB  sob_get_next_child;
SOBDATA* sob_get_next_child(SOBDATA* cData)
{
    if (cData EQU NULL) return NULL;
    return  cData->NextSiblingLink;
}

//SOBFNC_GETSOB fnGetNextChildGeneric;
static SOBDATA* fnGetNextChildGeneric(SOBDATA* cData)
{
    return  sob_get_next_child(cData);
}

// ---------------------------------

// SOBFNC_GETSOB  sob_get_first_child;
SOBDATA* sob_get_first_child(SOBDATA* pData)
{
    if (pData EQU NULL) return NULL;
    return  pData->headLink;
}
// SOBFNC_GETSOB fnGetFirstChildGeneric;
static SOBDATA* fnGetFirstChildGeneric(SOBDATA* cData)
{
    return  sob_get_first_child(cData);
}
// ---------------------------------

static void sob_link_in_to_parent(SOBDATA* pData, SOBDATA* cData)
{
    if (cData->subType EQU SOB_CONTAINER_WINDOW)  return;

    // prevData is the LAST entry in the list BEFORE we add this new entry

    // debugn((TCHAR*)_TEXT("Link in "));
    // debugIntn(pData->sIndex); debugSpaces(2); debugInt (cData->sIndex);

    SOBDATA* prevData = pData->tailLink;
    if (prevData)
    {
        prevData->NextSiblingLink = cData;

        // new entry must point back to previous
        cData->PreviousSiblingLink= prevData;
    }
    else
    {
        pData->headLink = cData;
        cData->PreviousSiblingLink= 0;
    }

    // new entry must point forward to parent container
    cData->NextSiblingLink = 0;

    // parent container must point back to the new entry
    pData->tailLink = cData;

}
static void sob_delete(SOBID sobId)
{
    if (sob_index_check(sobId)) return;
    SOBDATA* sData = &sobData[sobId];
    SOBDATA* pData = sData->ppSobData;

    sob_set_stretch(sData, 0);

    sob_unlink(sData);

    if (sData->type EQU SOB_TYPE_CONTAINER)
    {
        sData->pVtbl->Update(sData);
    }
    else
    {
        sData->ppSobData->pVtbl->Update(sData->ppSobData);
    }

    sData->sIndex = 0;
}

void   sobObj_delete(SOBDATA* sData)
{
    sob_delete(sData->sIndex);
}

// SOBFNC_SHAKE  fnDeleteGeneric;
static myUInt fnDeleteGeneric(SOBDATA* sData)
{
    SOBDATA* pData = sData->ppSobData;
    sob_unlink(sData);

    if (sData->type EQU SOB_TYPE_CONTAINER)  sData->pVtbl->Update(sData);

    else  sData->ppSobData->pVtbl->Update(sData->ppSobData);

    sData->sIndex = 0;

    return 1;
}

static myUInt fnDeleteChildrenGeneric(SOBDATA* conObject)
{

    if (conObject->type NEQU SOB_TYPE_CONTAINER)   return SOB_NOK;

    SOBDATA* childObject = CONTAINER.FirstChild(conObject);
    while (childObject)
    {

        if (childObject->type EQU SOB_TYPE_CONTAINER)  fnDeleteChildrenGeneric(childObject);

        if (childObject->fncCustomDelete) childObject->fncCustomDelete(childObject);
        sob_unlink(childObject);

        childObject = CONTAINER.FirstChild(conObject) ;
    }
    conObject->pVtbl->Update(conObject);

    return 1;
}

// ---------------------------------

// SOBFNC_GETTEXT  fnGetTypeTextGeneric;
static TCHAR * fnGetTypeTextGeneric(SOBDATA* sData)
{
    return sob_type_to_text(sData->type);
}

// ---------------------------------

// SOBFNC_GETTEXT  fnGetSubTypeTextGeneric;
static TCHAR* fnGetSubTypeTextGeneric(SOBDATA* sData)
{
    return sob_container_to_text(sData->subType);
}

// ---------------------------------
// SOBFNC_CREATE  sob_new_new;

// SOBFNC_CREATE  fnCreateSobGeneric;
static SOBDATA* fnCreateSobGeneric(MAINTYPE sobType, CONTYPE conType, int width, int height)
{
    return sob_new_new(sobType, conType, width, height);
}

// -----------------------------

void sob_display_sob(SOBDATA* sData)
{
    //  UINT ShowState = SWP_SHOWWINDOW; // SWP_HIDEWINDOW  SWP_SHOWWINDOW;

    if (sData->showState EQU  SW_HIDE)  return;

    if (sData->subType EQU SOB_CONTAINER_WINDOW)
    {
        SetWindowPos
        (sData->hWnd            // handle to window
        , HWND_TOP                                             // placement-order handle
        , sData->displayX                                     // horizontal position
        , sData->displayY                                       // vertical position
        , sData->displayWidth - sData->left - sData->right    // width
        , sData->displayHeight - sData->top - sData->bottom          // height
        , SWP_NOOWNERZORDER BOR(UINT)ShowState                // window-positioning flags
        );
    }
    else if(sData->type EQU COMBOId)
    {
        SetWindowPos
        (sData->hWnd            // handle to window
        , HWND_TOP                                             // placement-order handle
        , sData->displayX                                     // horizontal position
        , sData->displayY                                       // vertical position
        , sData->displayWidth - sData->left - sData->right    // width
        , sData->comboHeight   - sData->top - sData->bottom  // height
        , SWP_NOOWNERZORDER BOR(UINT)ShowState                // window-positioning flags
        );
    }
    else
    {
        SetWindowPos
        (sData->hWnd            // handle to window
        , HWND_TOP                                             // placement-order handle
        , sData->displayX                                     // horizontal position
        , sData->displayY                                       // vertical position
        , sData->displayWidth     // width
        , sData->displayHeight        // height
        , SWP_NOOWNERZORDER BOR(UINT)ShowState                // window-positioning flags
        );
    }

    #if DebuggingAid

        swprintf_s(txtBuffer, szTxtBuffer,
        L"Display     %04x  0x%llx   x/y  %04x %04x    w/h  %04x %04x     p/c   %04x %04x  fwd 0x%llx  0x%llx  bkw 0x%llx  0x%llx\n"
        , sData->sIndex
        , (myUInt)sData
        , sData->displayX
        , sData->displayY
        , sData->displayWidth
        , sData->displayHeight
        , sData->pIndex
        , sData->sIndex
        , (myUInt)sData->headLink
        , (myUInt)sData->startBlk.bkwLink

        , (myUInt)sData->endBlk.fwdLink
        , (myUInt)sData->tailLink
        );

        ReportError(txtBuffer);
    #endif
}

// SOBFNC_SHAKE fnDisplayGeneric;
static myUInt fnDisplayGeneric(SOBDATA* sData)
{
    sob_display_sob(sData);
    return SOB_OK;
}

// -----------------------------

void sobObj_SetShow(SOBDATA* sData, myUInt value)
{

    if (value)  value = SWP_SHOWWINDOW;
    else value = SW_HIDE;

    ShowWindow(sData->hWnd, (UINT) value);
    sData->showState = (UINT)value;

}

// SOBFNC_SETVALUE fnSetShowGeneric;
static myUInt fnSetShowGeneric(SOBDATA* sData, myUInt value)
{
    if (sData->fncCustomShow NEQU NULL)
    {
        sData->fncCustomShow(sData, value);
        return SOB_OK;
    }
    if (value)  value = SWP_SHOWWINDOW;
    else value = SW_HIDE;

    ShowWindow(sData->hWnd, (UINT)value);
    sData->showState = (UINT)value;

    return SOB_OK;
}

// -----------------------------

SOBID sob_get_pIndex (SOBID sobId)
{
    if  ( sob_index_check(sobId) ) return SOBID_ERROR;
    return  sobData[sobId].pIndex;
}

// -----------------------------

CONTYPE sob_get_subtype(SOBID sobId)
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return SOB_CONTAINER_INVALID;
    }
    return sobData[sobId].subType;
}

// -----------------------------

MAINTYPE sob_get_type (SOBID sobId)
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return SOB_TYPE_INVALID;
    }
    return sobData[sobId].type;
}

// -----------------------------

HWND sob_get_HWND(SOBID sobId)
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return NULL;
    }
    return sobData[sobId].hWnd;
}
// -----------------------------

HWND sob_get_HWND_parent(SOBID sobId )
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return NULL;
    }
    return sobData[sobData[sobId].pIndex].hWnd;
}

void sob_set_HWND(SOBID sobId, HWND value)
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return;
    }
    sobData[sobId].hWnd = value;
}

// -----------------------------

EVENTHANDLER sob_get_EVENTHANDLER(SOBID sobId)
{
    if (sobId > MAX_NBR_SOBS)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"SOB index out of range %04x \n", sobId);

        ReportError(txtBuffer);
        return NULL;
    }
    return sobData[sobId].eventHandler;
}

void sob_CreateWindowExNew(SOBDATA * sData , DWORD dwExStyle, PCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle)
{
    SOBDATA* pData = sob_get_base(sData->pIndex);

/*
    Predefined Windows class names
    for lpClassName
    BUTTON
    COMBOBOX
    EDIT
    LISTBOX
    MDICLIENT
    RichEdit
    RICHEDIT_CLASS
    SCROLLBAR
    STATIC    // use for labels
*/

    if (styleOverride)
    {
        dwStyle = styleOverride;
        styleOverride = 0;
    }

    sData->hWnd = CreateWindowExW
    ( 0
    , lpClassName
    , lpWindowName
    , dwStyle  BOR  WS_CLIPCHILDREN
    , sData->displayX
    , sData->displayY
    , sData->displayWidth
    , sData->displayHeight
    , pData->hWnd
    , pData->hMenu
    , appHInst
    , NULL
    );

    #if DebuggingAid
        swprintf_s(txtBuffer, szTxtBuffer, L"Create --%ws----%ws--   %04x %04x      %04x %04x        %04x %04x  %llx\n"
        , lpWindowName
        , lpClassName
        , sData->displayX
        , sData->displayY
        , sData->displayWidth
        , sData->displayHeight
        , sData->sIndex
        , sData->pIndex
        , (myUInt) pData->hWnd
        );

        ReportError(txtBuffer);
    #endif

    return;
}

static void sob_display_container(SOBDATA* sData)
{
    //   swprintf_s(txtBuffer, szTxtBuffer, L"sob_display_container %04x \n", sData->sIndex); ReportError(txtBuffer);
    //   InvalidateRect(sData->hWnd, (RECT*)0, TRUE);

    if (sData->subType  EQU  SOB_CONTAINER_MENU_BAR) return;
    if (sData->subType  EQU  SOB_CONTAINER_MENU_H) return;

    //   if ((sData ->type EQU SOB_TYPE_CONTAINER) LAND(sData->pVtbl) LAND(sData->pVtbl->ReSize))  sData->pVtbl->ReSize(sData);

    if (sData->subType EQU  SOB_CONTAINER_WINDOW)
    {
        // we need to allow for the surround of a top level window
        // and the height added by a MENUBAR

        WINDOWINFO myWI = {0};
        myWI.cbSize = sizeof(WINDOWINFO);
        GetWindowInfo(sData->hWnd, &myWI);

        // these two lines are okay
        sData->displayX = myWI.rcWindow.left;
        sData->displayY = myWI.rcWindow.top;

        int bordersWidth   = abs( (myWI.rcWindow.right  - myWI.rcWindow.left) - (myWI.rcClient.right - myWI.rcClient.left));
        int bordersHeight  = abs( (myWI.rcWindow.bottom - myWI.rcWindow.top)  - (myWI.rcClient.bottom - myWI.rcClient.top));
        UINT ShowState = SWP_SHOWWINDOW; // SWP_HIDEWINDOW  SWP_SHOWWINDOW;

        SetWindowPos
        ( sData->hWnd            // handle to window
        , HWND_TOP                                             // placement-order handle
        , sData->displayX                                     // horizontal position
        , sData->displayY                                       // vertical position
        , sData->displayWidth  + bordersWidth                                  // width
        , sData->displayHeight + bordersHeight                               // height
        , SWP_NOOWNERZORDER BOR(UINT)ShowState                // window-positioning flags
        ) ;

    }
    else
    {
        sob_display_sob(sData);
    }

    SOBDATA* cData = sob_get_first_child(sData);

    while (cData)
    {

        if (cData->type EQU SOB_TYPE_CONTAINER) sob_display_container(cData);
        else  sob_display_sob(cData);
        cData = sob_get_next_child(cData);
    }
}

int displayFlag = 0;

void sob_display_top_level_window (SOBID sobId)
{

    if (displayFlag) return;
    if (sob_index_check(sobId)) return;
    SOBDATA* sData = &sobData[sobId];

    while (sData->pIndex NEQU SOBID_ERROR)  sData = &sobData[sData->pIndex];

    sob_display_container(sData);

}

// -----------------------------

void int_sob_text_size(SOBDATA* sData, TCHAR* txt, int* w, int* h)
{
    TEXTMETRIC tm;
    int len;
    SIZE       lpSize = {0};
    HDC        lhDC;

    lhDC = GetDC(sData->hWnd);

    //    hfontPrev = SelectObject(lhDC, (HGDIOBJ)thisFONT);

    GetTextMetrics(lhDC, &tm);

    len = (int)_tcslen(txt);

    if (len)
    {
        GetTextExtentPoint32
        (lhDC
        , txt
        , (int)len
        , &lpSize
        );
    }

    ReleaseDC(sData->hWnd, lhDC);

    *w = max(lpSize.cx, tm.tmAveCharWidth * len);
    *h = max(lpSize.cy, tm.tmHeight + tm.tmInternalLeading + tm.tmExternalLeading);
}
// -----------------------------
// SOBFNC_SHAKE fnReportGeneric;
static myUInt  fnReportGeneric(SOBDATA* sData)
{

    addText((TCHAR*) L"------\n");

    if ((!sData) LOR(!sData->sIndex)) return SOB_NOK;;

    swprintf_s(txtBuffer, szTxtBuffer, L"         \t%004llx\n", (myUInt) sData ); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" Self Index  \t%04i\n", sData->sIndex); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" Parent Index\t%04i\n", sData->pIndex); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" Sibling Nbr \t%04i\n", sData->siblingNbr); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" Children    \t%04i\n", sData->nbrChildren); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" Type        \t%04i   ", sData->type); addText(txtBuffer);
    addText(sData->fncCustomGetTypeText(sData)); addText( (TCHAR *)L"\n");

    swprintf_s(txtBuffer, szTxtBuffer, L" SubType     \t%04i   ", sData->type); addText(txtBuffer);
    addText(sData->fncCustomGetSubTypeText(sData)); addText((TCHAR*)L"\n");

    swprintf_s(txtBuffer, szTxtBuffer, L" W Min      \t%04i\n", sData->minWidth); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" H Min      \t%04i\n", sData->minHeight); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" X Display  \t%04i\n", sData->displayX); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" Y Display  \t%04i\n", sData->displayY); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" W Display  \t%04i\n", sData->displayWidth); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" H Display  \t%04i\n", sData->displayHeight); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" hMenu       \t0x%004llx\n", (myUInt) sData ->hMenu ); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" hWnd        \t0x%004llx\n", (myUInt) sData->hWnd); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" fwdLink     \t0x%004llx\n", (myUInt)sData->NextSiblingLink); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" bkwLink     \t0x%004llx\n", (myUInt)sData->PreviousSiblingLink); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" Stretch     \t0x%004llx\n", (myUInt)sData->stretchSobData); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" Share       \t0x%004llx\n", (myUInt)sData->share ); addText(txtBuffer);
    swprintf_s(txtBuffer, szTxtBuffer, L" Parent ptr  \t0x%004llx\n", (myUInt)sData->ppSobData); addText(txtBuffer);

    swprintf_s(txtBuffer, szTxtBuffer, L" Custom Show \t0x%004lli\n", (myUInt)sData->fncCustomShow); addText(txtBuffer);

    if ( 0 LAND (sData->type EQU SOB_TYPE_CONTAINER) )
    {
        swprintf_s(txtBuffer, szTxtBuffer, L" W effective \t%004lli\n", (myUInt)sData->effectiveWidth); addText(txtBuffer);
        swprintf_s(txtBuffer, szTxtBuffer, L" H effective \t%004lli\n", (myUInt)sData->effectiveHeight); addText(txtBuffer);

        swprintf_s(txtBuffer, szTxtBuffer, L" fwdLink top \t%004lli\n", (myUInt)sData->headLink); addText(txtBuffer);

        swprintf_s(txtBuffer, szTxtBuffer, L" bkwLink end \t%004lli\n", (myUInt)sData->tailLink); addText(txtBuffer);

    }
    swprintf_s(txtBuffer, szTxtBuffer, L" CTBL         \t%004lli\n", (myUInt)sData->cVtbl); addText(txtBuffer);

    if (sData->type EQU SOB_TYPE_CONTAINER)
    {

        swprintf_s(txtBuffer, szTxtBuffer, L" Bnd L-R \t%04i %04i\n", sData->left, sData->right); addText(txtBuffer);
        swprintf_s(txtBuffer, szTxtBuffer, L" Bnd T-B \t%04i %04i\n", sData->top, sData->bottom); addText(txtBuffer);
    }

    return SOB_OK;
}

int sobInitState = 0;
static void sob_init()
{
    if (sobInitState) return;

    debug((TCHAR*)_TEXT("INIT"));

    SOBID loop = 0;
    while (loop < MAX_NBR_SOBS)  sobData[loop++].sIndex = 0 ;

    sobInitState = 1;
    gpSobIndex = SOBID_ERROR;

    sobMainType = SOB_TYPE_INVALID;
    CClrButton();
}

void codecontrol_sob(SYS_STATE state)
{
    switch (state)
    {
        case SS_POWER_UP:
            sob_init();
            break;

        case SS_POWER_DOWN:
            break;

        case SS_RESTART:
            break;
    }
}
// ----------------------------------------------
//
// "Generic" functions MUST use SOBDATA *
// simply because they can not know which specific OBJECT * to use
// So the  caller  must map its  OBJECT *  to a  SOBDATA *
//
// ----------------------------------------------
static SOBDATA* fnGetContainer()
{
    return sob_get_base(gpSobIndex);

}

// SOBFNC_SHAKE fnEmptyGeneric;

static myUInt  fnEmptyGeneric(SOBDATA* cData )
{
    return SOB_OK;
}

myUInt  fnSetEventHandlerGeneric(SOBDATA* cData, EVENTHANDLER value)
{
    cData->eventHandler = (EVENTHANDLER) value;
    return 1;
}

// ----------------------------------------------

TCHAR* fnGetTitleGeneric(SOBDATA* cData)
{
    #define  TitleMax 255
    static TCHAR title[TitleMax + 1] = { 0 };
    GetWindowText(cData->hWnd, title, TitleMax);
    return title;
}

// ----------------------------------------------

// SOBFNC_SETTEXT fnSetTitleGeneric;
myUInt  fnSetTitleGeneric(SOBDATA* cData,  TCHAR* title)
{
    return SetWindowText(cData->hWnd, title);
}

// ----------------------------------------------

// SOBFNC_GETTEXT  fnGetTypeGeneric;
static TCHAR* fnGetTypeGeneric(SOBDATA* sData)
{
    return (TCHAR*)_TEXT("not implemented");
}

// ----------------------------------------------
void initialisePVTBL(PVTBL* Table)
{

}

void initialiseSOBMETHODSTABLE(CVTBL* Table)
{
    Table->GetSobTitle = fnGetTitleGeneric;
    Table->SetSobTitle = fnSetTitleGeneric;
    Table->GetSobType = fnGetTypeGeneric;
}

myUInt fnInvalidSobGeneric(SOBDATA* cData)
{
    if (cData < &sobData[0]) return 1;
    myUInt delta = (myUInt)&sobData[MAX_NBR_SOBS + 1] + sizeof(SOBDATA) - (myUInt)cData;
    if ( ( delta /  sizeof(SOBDATA) )   >  (MAX_NBR_SOBS + 1) )   return 1 ;

    if (   delta %  sizeof(SOBDATA) ) return 1;
    return (cData->sIndex EQU 0);

}

static myUInt  fnOpen (SOBDATA* thisObject)
{
    if ((thisObject->sIndex)  LAND(thisObject->type EQU SOB_TYPE_CONTAINER ))
    {
        sob_set_pSobObj(thisObject);
        return SOB_OK;
    }
    return SOB_NOK;
}

static SOBDATA* fnGetParent(SOBDATA* thisObject)
{
    return thisObject->ppSobData;
}

// SOBFNC_GETSOBDATA fnGetSobObj;
static SOBDATA* fnGetSobObj(SOBDATA* thisObject )
{
    return thisObject;
}

CVTBL* get_container_ctbl_generic ()
{
    return NULL;
    static CVTBL  Table;
    static int   TableFlag = 0;

    if (!TableFlag)
    {
        initialiseSOBMETHODSTABLE(&Table);
        Table.GetSOBOBJ = fnGetSobObj;
        //       Table.GetSobTitle = fnGetTitleGeneric;
        //       Table.SetSobTitle = fnSetTitleGeneric;

        TableFlag = 1;
    }
    return &Table;
}

// ----------------------------------------------

myUInt sob_SetRGB(SOBDATA * thisSob, COLORREF rgbValue)
{

    LOGBRUSH  bkg_style = {0};
    HBRUSH    bkg_brush, old_brush;

    HWND lWnd = thisSob->hWnd;

    old_brush = thisSob->bgBrush;
    if (old_brush) DeleteObject(old_brush);

    bkg_style.lbColor = (COLORREF)rgbValue;
    bkg_style.lbStyle = BS_SOLID;
    bkg_style.lbHatch = 0;
    if (rgbValue)
    {
        bkg_brush = CreateBrushIndirect(&bkg_style);
        thisSob->bgBrush = bkg_brush;
        thisSob->bgRGB = rgbValue;
    }

    InvalidateRect(lWnd, (RECT*)NULL, TRUE);
    return 1;
}

// ----------------------------------------------
//  SOB external view
// ----------------------------------------------
SOBMETHODSTABLE SOB =
{
    SOBMETHODSGENERIC
};

void fnInheritSOBmethods(SOBMETHODSTABLE* pMethodTable)
{
    //  create a COPY of the default SOB METHODS TABLE in the caller's code scope
    memcpy(pMethodTable, & SOB, sizeof(SOBMETHODSTABLE));
}

SOBDATA* sob_new_new(MAINTYPE sobType, CONTYPE conType, int width, int height)
{
    SOBDATA* sData, * pData;

    // -----------------------------
    // handle the 2 special cases
    // related to  SOB_CONTAINER_WINDOW

    if (gpSobIndex EQU SOBID_ERROR)
    {
        if (conType NEQU SOB_CONTAINER_WINDOW)
        {
            swprintf_s(txtBuffer, szTxtBuffer, (TCHAR*)L"First SOB not a WINDOW  %ws  %ws\n", sob_type_to_text(sobType), sob_container_to_text(conType));
            addText(txtBuffer);
            return NULL;
        }
    }

    if (conType EQU SOB_CONTAINER_WINDOW) gpSobIndex = SOBID_ERROR;

    // -----------------------------
    // Find a FREE data structure

    sobIndex = 1;
    while (sobIndex < MAX_NBR_SOBS)
    {
        sData = &sobData[sobIndex];

        if (sData->sIndex EQU 0)
        { // found a FREE data structure

            sob_initialise(sobIndex);
            sData->sIndex = sobIndex;  // mark it as  inuse
            sData->showState = 1;

            break;
        }
        sobIndex++;
    }

    if (!(sobIndex < MAX_NBR_SOBS))
    {
        ReportError((TCHAR*)L"Too many Screen OBjects\n");
        return NULL;
    }
    // -----------------------------
    // remember we have initialised the SOBDATA  contents to 0

    sData = &sobData[sobIndex]; // to avoid a compiler error

    sData->psSobData = sData;

    sData->type = sobType;
    sData->subType = conType;

    sData->effectiveWidth = width;
    sData->effectiveHeight = height;

    sData->minWidth = width;
    sData->minHeight = height;

    // when a widget is created its constructor should overwrite this
    // to provide ta widget dependent destructor, if there is one!
    sData->fncCustomDelete = fnDeleteGeneric;

    // when a widget is created its constructor should overwrite this
    // to provide the text name of the widget

    sData->fncCustomGetTypeText = fnGetTypeTextGeneric;
    sData->fncCustomGetSubTypeText = fnGetSubTypeTextGeneric;

    // to allow a widget to change the default handling of a SOB
    //  1) create a COPY of the default SOB METHODS TABLE
    //  2) overwrite the functional pointer, in that COPY, to the address of an
    //     appropriate custom function.
    //     We do that when the first instance of this widget class is instantiated.
    //  3) and then the pointer, in the SOB data, to the default  SOB METHODS TABLE
    //     with a pointer to our COPY
    //     We do that, after step 2, when the first instance of this widget class is instantiated.

    // here make SPACE for the COPY of the SOB METHODS TABLE
    sData->sVtbl = & SOB;

    // ---------------------------------
    // Now create space to hold the widget specific METHODS TABLE
    //
    // BE WARNED: DO NOT forget that these are pointers so they all need a   *
    // ---------------------------------

    // -----------------------------

    int siblingNbr = 0;
    // -----------------------------
    // update the PARENT  SOBDATA

    if (gpSobIndex  EQU SOBID_ERROR)
    {
        // happens when creating a TOP LEVEL WINDOW
        sData->ppSobData = NULL;
    }
    else
    {
        pData = &sobData[gpSobIndex];
        sData->ppSobData = pData;

        sData->siblingNbr = pData->nbrChildren++;

        sob_link_in_to_parent(pData, sData);

    }
    sData->pIndex = gpSobIndex;
    // -----------------------------

    sData->sIndex = sobIndex;

    //   if (sData->ppSobData NEQU  NULL)   sData->ppSobData->pVtbl->Add(sData);
    if (sData->ppSobData NEQU  NULL)   CONTAINER.AddChild(sData);

    // -----------------------------
    // if this is a CONTAINER, then we must make it the new PARENT

    if (sobType EQU SOB_TYPE_CONTAINER)   gpSobIndex = sobIndex;

    return  sData;
}

// ----------------------------------------------
//  CONTAINER things
// ----------------------------------------------

CONTAINERINFORM container_Add;
static myUInt  container_Add(SOBDATA* childObject)
{
    return  childObject->ppSobData->pVtbl->AddChild(childObject);
}

CONTAINERINFORM container_Update;
static  myUInt  container_Update(SOBDATA* conData)
{
    return  conData->pVtbl->Update(conData);
}

CONTAINERSETVALUE container_Stretch;
static myUInt   container_Stretch(SOBDATA* childObject, myUInt value)
{
    if (childObject->ppSobData->pVtbl->Stretch)
    return  childObject->ppSobData->pVtbl->Stretch(childObject, value);

    else return SOB_NOK;
}

CONTAINERINFORM container_Delete;
static  myUInt  container_Delete(SOBDATA* conData)
{
    return  conData->pVtbl->Delete(conData);
}

CONTAINERINFORM container_Display;
static myUInt  container_Display(SOBDATA* conData)
{
    return  conData->pVtbl->Display(conData);
}
CONTAINERGETTEXT container_TypeText;
static  TCHAR * container_TypeText(SOBDATA* conData)
{
    return conData->pVtbl->TypeText(conData); ;
}

CONTAINERCREATE container_Create;
static SOBDATA* container_Create()
{
    return  NULL;
}

CONTAINERINFORM container_ReSize;
static myUInt  container_ReSize(SOBDATA* conData)
{
    return  SOB_OK ;
}

CONTAINERINFORM container_UpDateChildren;
static myUInt  container_UpDateChildren(SOBDATA* conData)
{
    return conData->pVtbl->UpDateChildren   (conData); ;
}

CONTAINERINFORM container_UpDateChildren;
static myUInt  container_Open(SOBDATA* conData)
{
    return conData->pVtbl->Open(conData); ;
}

CONTAINERCREATE container_LastWidget;
static SOBDATA*   container_LastWidget ( )
{
    return NULL;
}

CONTAINERINFORM container_DeleteChildren;
static myUInt  container_DeleteChildren(SOBDATA* conData)
{
    return fnDeleteChildrenGeneric(conData);
}

CONTAINERGETSOBDATA container_FirstChild;
static SOBDATA * container_FirstChild (SOBDATA* conData)
{
    return  fnGetFirstChildGeneric(  conData);
}

// ---------------------------------
// this is the VTABLE structure for a SOB CONTAINER
// we only need a few functions / methods

MNGR_METHOD_TABLE_GENERIC CONTAINER =
{
    container_Add
    , container_Update
    , container_Delete
    , container_Display
    , container_TypeText
    , container_Stretch

    , container_ReSize
    , container_UpDateChildren
    , container_Open
    , container_LastWidget
    , container_FirstChild
    , container_DeleteChildren

};
