#include <proto/alib.h>
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/icon.h>
#include <proto/intuition.h>
#include <proto/commodities.h>
#include <proto/diskfont.h>

//#include <dos/dos.h>
#include <exec/memory.h>
#include <graphics/text.h>
#include <intuition/intuitionbase.h>
#include <libraries/commodities.h>
#include <libraries/gadtools.h>

#define Y_DIST 16

struct winNode
{
 struct Node node;
 WORD   pad;
 struct Window *win;
};

long __oslibversion = 36;

struct Library *DiskfontBase = NULL;

struct NewBroker newbroker =
 {
  NB_VERSION,
  "Win-2-Front",
  "Win-2-Front 2.0 by Jacek Rzeuski",
  "Yet another ALT-TAB window popup",
  NBU_UNIQUE | NBU_NOTIFY,
  0, 0, 0, 0
 };
struct TextAttr  ta = { NULL, 0, 0, 0 };
struct IntuiText itext = { 1, 0, JAM1, 12, Y_DIST, NULL, NULL, NULL };

struct MsgPort  *broker_mp = NULL;
struct Screen   *scr = NULL;
struct Window   *my_wind = NULL, *wind = NULL;
struct NewGadget ng = { 0,0,0,0,NULL,NULL,100,0,NULL,NULL };
struct TextFont *tf = NULL;
CxObj *broker = NULL;

ULONG cxsig = 0, windsig = 0;
BOOL  active = FALSE;

/// "NextWindow()"
struct Window *NextWindow(struct Window *wind)
 {
  struct Window *start;

  if (wind == NULL) wind = scr->FirstWindow;
  start = wind;
  while ((wind->Flags & WFLG_BACKDROP) || (wind == my_wind))
   {
    wind = wind->NextWindow;
    if (wind == NULL) wind = scr->FirstWindow;
    if (wind == start) return NULL;
   }
  return wind;
 }
///
/// "DisplayName()"
void DisplayName(void)
 {
//  itext.FrontPen = 2;
  PrintIText(my_wind->RPort,&itext,1,0);
//  itext.FrontPen = 2;
  PrintIText(my_wind->RPort,&itext,0,0);
 }
///

/// "FilterFunc()"
#define MASK (~ ( IEQUALIFIER_CAPSLOCK | \
                  IEQUALIFIER_REPEAT | \
                  IEQUALIFIER_INTERRUPT | \
                  IEQUALIFIER_MULTIBROADCAST | \
                  IEQUALIFIER_RELATIVEMOUSE ))
void FilterFunc(register CxMsg *cxm, CxObj *co)
 {
  struct InputEvent *ie = (struct InputEvent * )CxMsgData(cxm);
  BOOL active = *((BOOL *)CxMsgID(cxm));

  if (ie->ie_Class == IECLASS_RAWKEY)
   {
    if ((ie->ie_Qualifier & MASK) == IEQUALIFIER_LALT)
     {
      if (ie->ie_Code == 0x42) DivertCxMsg(cxm,co,co);
     }
    else if (active) if (ie->ie_Code & IECODE_UP_PREFIX) DivertCxMsg(cxm,co,co);
    return;
   }
  if (ie->ie_Class == IECLASS_RAWMOUSE) if (active) DisposeCxMsg(cxm);
 }
///
/// "ProcessMsg()"
void ProcessMsg(void)
 {
  CxMsg *msg;
  struct MinList list;
  struct winNode *cur_winNode, *node;
            struct Screen *scr;
            struct Window *win;
            struct Gadget *gad, *glist = NULL;
  struct InputEvent ie;
  ULONG sigrcvd, msgid, msgtype;
  LONG returnvalue = 1L, selected;

  while (returnvalue)
   {
    sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsig | windsig);

    if (sigrcvd & cxsig) while (msg = (CxMsg *)GetMsg(broker_mp))
     {
      msgtype = CxMsgType(msg);
      msgid = CxMsgID(msg);
      ie = *(struct InputEvent *)CxMsgData(msg);
      ReplyMsg((struct Message *)msg);

      switch (msgtype)
       {
        case CXM_IEVENT:
         {
          if (active)
           {
                if (ie.ie_Qualifier & IEQUALIFIER_LALT)
             {
              if ( ! (ie.ie_Code & IECODE_UP_PREFIX))
               {
                cur_winNode = (struct winNode *)cur_winNode->node.ln_Succ;
                selected++;
                if (cur_winNode == (struct winNode *)&list.mlh_Tail)
                 {
                  cur_winNode = (struct winNode *)list.mlh_Head;
                  selected = 0;
                 }
                GT_SetGadgetAttrs(gad,my_wind,NULL,GTLV_MakeVisible, selected, GTLV_Selected, selected, TAG_END);
               }
             }
            else
             {
              if (my_wind)
               {
                CloseWindow(my_wind);
                my_wind = NULL;
                windsig = 0;
                WindowToFront(cur_winNode->win);
                ActivateWindow(cur_winNode->win);
                active = FALSE;

                FreeGadgets(glist);
                glist = NULL;
                FreeVisualInfo(ng.ng_VisualInfo);
                while(node = (struct winNode *)RemTail((struct List *)&list)) FreeMem(node, sizeof(struct winNode));
               }
             }
           }
          else
           {
            ULONG lock;
            WORD max_width = 0, width/*, height*/;

            int num_entries = 0;

            NewList((struct List *)&list);
            lock = LockIBase(0);
            scr = IntuitionBase->ActiveScreen;
            wind = IntuitionBase->ActiveWindow->NextWindow;

            for(win = scr->FirstWindow; win; win = win->NextWindow)
             {
              if ((win->Flags & WFLG_BACKDROP) == 0)     // skip backdrop windows
               {
                if (node = AllocMem(sizeof(struct winNode),MEMF_CLEAR))
                 {
                  if (win->Title) node->node.ln_Name = win->Title;
                  else node->node.ln_Name = "-- Unnamed --";
                  node->win = win;
                  AddTail((struct List *)&list,(struct Node *)node);
                 }
               }
             }
            UnlockIBase(lock);

            selected = 0;
            cur_winNode = (struct winNode *)list.mlh_Head;
            itext.ITextFont = (tf ? &ta : scr->Font);

            for(node = (struct winNode *)list.mlh_Head; node->node.ln_Succ; node = (struct winNode *)node->node.ln_Succ)
             {
              itext.IText = node->node.ln_Name;
//              printf("%s\n",itext.IText);
              width = IntuiTextLength(&itext);
              if (width > max_width) max_width = width;

              if (node->win == wind)
               {
                selected = num_entries;
                cur_winNode = node;
               }
              num_entries++;
             }
            if (gad = CreateContext(&glist))
             {
              ng.ng_LeftEdge = 0; //scr->WBorLeft;
              ng.ng_TopEdge  = 0; //scr->WBorTop;
              ng.ng_Width = width = max_width + 24;
              ng.ng_Height = itext.ITextFont->ta_YSize * num_entries + 8;
              if (ng.ng_VisualInfo = GetVisualInfoA(scr,NULL))
               {
                if (gad = CreateGadget(LISTVIEW_KIND, gad, &ng,
                                GTLV_Labels,          (Tag)&list,
                                GTLV_ShowSelected,    NULL,
                                GTLV_Top,             selected,
                                GTLV_Selected,        selected,
                                GTLV_ScrollWidth,     0,
                                TAG_END))
                 {
                  if (my_wind = OpenWindowTags(NULL,
                                    WA_Left,   (scr->Width - width) >> 1,
                                    WA_Top,    (scr->Height - gad->Height) >> 1,
                                    WA_Width,  width,
                                    WA_Height, gad->Height,
                                    WA_Gadgets, (Tag)glist,
                                    WA_CustomScreen, (Tag)scr,
                                    WA_IDCMP,  LISTVIEWIDCMP | IDCMP_REFRESHWINDOW,
                                    WA_Flags,  WFLG_SIMPLE_REFRESH | WFLG_ACTIVATE | WFLG_BORDERLESS,
                                    TAG_END))
                   {
                    GT_RefreshWindow(my_wind,NULL);
                    windsig = 1 << my_wind->UserPort->mp_SigBit;
                    active = TRUE;
                   }
                 }
               }
             }
           }
          break;
         }
        case CXM_COMMAND:
         {
          switch(msgid)
           {
            case CXCMD_DISABLE:
              ActivateCxObj(broker, FALSE);
              break;
            case CXCMD_ENABLE:
              ActivateCxObj(broker, TRUE);
              break;
            case CXCMD_KILL:
            case CXCMD_UNIQUE:
              returnvalue = 0L;
              break;
           }
          break;
         }
       }
     }
    if (windsig) if (sigrcvd & windsig)
     {
      struct IntuiMessage *imsg;
      ULONG  class;

      while(imsg = GT_GetIMsg(my_wind->UserPort))
       {
        class = imsg->Class;
        GT_ReplyIMsg(imsg);
        switch ( class)
         {
          case IDCMP_REFRESHWINDOW:
            GT_BeginRefresh(win);
            GT_EndRefresh(win,TRUE);
            break;
         }
       }
     }
    if (sigrcvd & SIGBREAKF_CTRL_C) returnvalue = 0L;
   }
 }
///

/// "main()"
int main(int argc, UBYTE **argv)
 {
  CxObj *custom, *sender, *translate;
  UBYTE **ttypes;
  CxMsg *msg;

  if (broker_mp = CreateMsgPort())
   {
    newbroker.nb_Port = broker_mp;
    cxsig = 1L << broker_mp->mp_SigBit;
    ttypes = ArgArrayInit(argc, argv);
    newbroker.nb_Pri = (BYTE)ArgInt(ttypes, "CX_PRIORITY", 0);
    if (ta.ta_Name = ArgString(ttypes,"FONTNAME",NULL))
     {
      if (ta.ta_YSize = ArgInt(ttypes,"FONTSIZE",0))
       {
        if (DiskfontBase = OpenLibrary("diskfont.library", 36))
         {
          tf = OpenDiskFont(&ta);
         }
       }
     }
    if (broker = CxBroker(&newbroker, NULL))
     {
      if (custom = CxCustom(FilterFunc, (ULONG)&active))
       {
        AttachCxObj(broker, custom);
        if (sender = CxSender(broker_mp, 0))
         {
          AttachCxObj(custom, sender);
          if (translate = CxTranslate(NULL))
           {
            AttachCxObj(custom, translate);
            ActivateCxObj(broker, TRUE);
            ProcessMsg();
           }
         }
       }
      DeleteCxObjAll(broker);
      while(msg = (CxMsg *)GetMsg(broker_mp)) ReplyMsg((struct Message *)msg);
     }
    if (tf) CloseFont(tf);
    CloseLibrary(DiskfontBase);
    ArgArrayDone();
    DeletePort(broker_mp);
    return 0;
   }
  return 5;
 }
///

