/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */

#include "system_headers.h"

struct MUIP_AddChildWindowMessage {
    ULONG MethodID;
    APTR window;
};

struct MUIP_RemChildWindowMessage {
    ULONG MethodID;
    APTR window;
};

static void CloseChildWindow( struct ParentWinData *pwd,
                              struct ChildWindowNode *cwn )
{
    APTR app;

    nnset(cwn->cwn_ChildWindow, MUIA_Window_Open, FALSE);

    get(cwn->cwn_ChildWindow, MUIA_ApplicationObject, &app);
    DoMethod(app, OM_REMMEMBER, cwn->cwn_ChildWindow);

    MUI_DisposeObject(cwn->cwn_ChildWindow);
    tbFreeVecPooled(pwd->pwd_MemoryPool, cwn);
}

static void CloseChildWindows( struct ParentWinData *pwd )
{
    struct ChildWindowNode *cwn;

    ObtainSemaphore(&pwd->pwd_ChildWindowListSema);

    while (cwn = (struct ChildWindowNode *)RemHead((struct List *)&pwd->pwd_ChildWindowList)) {
        CloseChildWindow(pwd, cwn);
    }
    pwd->pwd_ChildWindowCount = 0;

    ReleaseSemaphore(&pwd->pwd_ChildWindowListSema);
}

static void CloseSuperflousChildWindows( struct IClass *cl,
                                         Object *obj )

{
    struct ParentWinData *pwd = INST_DATA(cl, obj);

    if (pwd->pwd_MaxChildWindowCount > 0) {
        APTR app;

        get(obj, MUIA_ApplicationObject, &app);

        while(pwd->pwd_ChildWindowCount > pwd->pwd_MaxChildWindowCount) {
            struct ChildWindowNode *cwn;

            cwn = (struct ChildWindowNode *)RemHead((struct List *)&pwd->pwd_ChildWindowList);
            DoMethod(app, OM_REMMEMBER, cwn->cwn_ChildWindow);
            CloseChildWindow(pwd, cwn);
            pwd->pwd_ChildWindowCount--;
        }
    }
}

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    if (obj = (Object *)DoSuperNew(cl, obj, TAG_MORE, msg->ops_AttrList)) {
        struct ParentWinData *pwd = INST_DATA(cl, obj);

        NewList((struct List *)&pwd->pwd_ChildWindowList);
        InitSemaphore(&pwd->pwd_ChildWindowListSema);

        pwd->pwd_MaxChildWindowCount = GetTagData(MUIA_Window_MaxChildWindowCount, 0, msg->ops_AttrList);

        if (pwd->pwd_MemoryPool = tbCreatePool(MEMF_CLEAR | MEMF_PUBLIC, 4096, 4096)) {
            DoMethod(obj, MUIM_Notify,  MUIA_Window_CloseRequest, TRUE,  MUIV_Notify_Self, 1, MUIM_Window_CloseChildWindows);
        } else {
            CoerceMethod(cl, obj, OM_DISPOSE);
            obj = NULL;
        }
    }

    return (ULONG)obj;
}

static ULONG __saveds mSet( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    struct ParentWinData *pwd = INST_DATA(cl, obj);
    struct TagItem *tags, *tag;

    for (tags = msg->ops_AttrList; tag = NextTagItem(&tags); ) {
        switch (tag->ti_Tag) {
            case MUIA_Window_MaxChildWindowCount:
                pwd->pwd_MaxChildWindowCount = tag->ti_Data;
                CloseSuperflousChildWindows(cl, obj);
                break;
        }
    }

    return (DoSuperMethodA(cl,obj,msg));
}


static ULONG __saveds mGet( struct IClass *cl,
                            Object *obj,
                            struct opGet *msg )
{
    struct ParentWinData *pwd = INST_DATA(cl, obj);

    switch (msg->opg_AttrID) {
        case MUIA_Window_MaxChildWindowCount:
            *msg->opg_Storage = pwd->pwd_MaxChildWindowCount; return (TRUE);
            break;
    }

    return (DoSuperMethodA(cl, obj, msg));
}


static ULONG __saveds mDispose( struct IClass *cl,
                                Object *obj,
                                struct opSet *msg )
{
    struct ParentWinData *pwd = INST_DATA(cl, obj);

    CloseChildWindows(pwd);
    if (pwd->pwd_MemoryPool) tbDeletePool(pwd->pwd_MemoryPool);

    nnset(obj, MUIA_Window_Open, FALSE);

    return (DoSuperMethodA(cl, obj, msg));
}

static ULONG __saveds mAddChildWindow( struct IClass *cl,
                                       Object *obj,
                                       struct MUIP_AddChildWindowMessage *msg )
{
    ULONG result = 0;
    struct ParentWinData *pwd = INST_DATA(cl, obj);
    struct ChildWindowNode *cwn;

    if (cwn = tbAllocVecPooled(pwd->pwd_MemoryPool, sizeof(struct ChildWindowNode))) {
        APTR app;

        cwn->cwn_ChildWindow = msg->window;

        ObtainSemaphore(&pwd->pwd_ChildWindowListSema);

        pwd->pwd_ChildWindowCount++;
        CloseSuperflousChildWindows(cl, obj);
        AddTail((struct List *)&pwd->pwd_ChildWindowList, (struct Node *)cwn);

        ReleaseSemaphore(&pwd->pwd_ChildWindowListSema);

        get(obj, MUIA_ApplicationObject, &app);
        DoMethod(app, OM_ADDMEMBER, msg->window);

        result = 1;
    }

    return result;
}

static ULONG __saveds mRemChildWindow( struct IClass *cl,
                                       Object *obj,
                                       struct MUIP_RemChildWindowMessage *msg )
{
    ULONG result = 0;
    struct ParentWinData *pwd = INST_DATA(cl, obj);
    struct ChildWindowNode *cwn;

    ObtainSemaphore(&pwd->pwd_ChildWindowListSema);

    for (cwn = (struct ChildWindowNode *)pwd->pwd_ChildWindowList.mlh_Head; cwn->cwn_Node.mln_Succ; cwn = (struct ChildWindowNode *)cwn->cwn_Node.mln_Succ) {
        if (cwn->cwn_ChildWindow == msg->window) {
            APTR app;

            get(obj, MUIA_ApplicationObject, &app);
            DoMethod(app, OM_REMMEMBER, msg->window);

            Remove((struct Node *)cwn);
            CloseChildWindow(pwd, cwn);
            pwd->pwd_ChildWindowCount--;

            result = 1;

            break;
        }
    }

    ReleaseSemaphore(&pwd->pwd_ChildWindowListSema);

    return result;
}

static ULONG __saveds mCloseChildWindows( struct IClass *cl,
                                        Object *obj,
                                        Msg msg )
{
    struct ParentWinData *pwd = INST_DATA(cl, obj);

    CloseChildWindows(pwd);

    return 0;
}

ULONG __asm __saveds ParentWinDispatcher( register __a0 struct IClass *cl,
                                          register __a2 Object *obj,
                                          register __a1 Msg msg )
{
    switch (msg->MethodID) {
        case OM_NEW:                        return (mNew(cl, obj, (APTR)msg));
        case OM_SET:                        return (mSet(cl, obj, (APTR)msg));
        case OM_GET:                        return (mGet(cl, obj, (APTR)msg));
        case OM_DISPOSE:                    return (mDispose(cl, obj, (APTR)msg));
        case MUIM_Window_AddChildWindow:    return (mAddChildWindow(cl, obj, (APTR)msg));
        case MUIM_Window_RemChildWindow:    return (mRemChildWindow(cl, obj, (APTR)msg));
        case MUIM_Window_CloseChildWindows: return (mCloseChildWindows(cl, obj, (APTR)msg));
    }

    return (DoSuperMethodA(cl, obj, msg));
}

