/*
*  XmNap  A Motif napster client
*
*  Copyright (C) 2000 Mats Peterson
*
*  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
*  (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 program; see the file COPYING.  If not, write to
*  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
*  Boston, MA 02111-1307, USA.
*
*  Please send any comments/bug reports to
*  [email protected]  (Mats Peterson)
*/

#include <Xm/Xm.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <regex.h>
#include <errno.h>

#include "chat.h"
#include "main.h"
#include "msgbox.h"


char* ReadErr(int r)
{
   if (r == 0)
       return "EOF";
   else
       return strerror(errno);
}


int ReadChars(int fd, void *ptr, int toRead)
{
   char *p = (char*)ptr;
   int copiedNow, copied;

   for (copied = 0; (unsigned)toRead > 0; ) {
       copiedNow = read(fd, p, toRead);
       if (copiedNow <= 0) {
           if (copiedNow == -1) {
               if (errno != EAGAIN)
                   return copiedNow;
               copiedNow = 0;
           } else
               return copiedNow;
       }
       copied += copiedNow;
       toRead -= copiedNow;
       p += copiedNow;
       XmUpdateDisplay(topLevel);
       if ((unsigned)toRead > 0)
           usleep(1000);
   }
   return copied;
}


int WriteChars(int fd, void *ptr, int toWrite)
{
   char *p = (char*)ptr;
   int copiedNow, copied;

   for (copied = 0; (unsigned)toWrite > 0; ) {
       copiedNow = write(fd, p, toWrite);
       if (copiedNow < 0) {
           if (errno != EAGAIN)
               return copiedNow;
           copiedNow = 0;
       }
       copied += copiedNow;
       toWrite -= copiedNow;
       p += copiedNow;
       XmUpdateDisplay(topLevel);
       if ((unsigned)toWrite > 0)
           usleep(1000);
   }
   return copied;
}


PRIV* FindPrivByNick(String nick)
{
   PRIV *p;

   for (p = privs; p; p = p->next) {
       if (! strcmp(p->nick, nick))
           break;
   }
   return p;
}


PRIV* FindPrivByWin(Widget w)
{
   PRIV *p;

   for (p = privs; p; p = p->next) {
       if (p->w == w)
           break;
   }
   return p;
}


CHAN* FindChanByName(String channel)
{
   CHAN *c;

   for (c = channels; c; c = c->next) {
       if (! strcmp(c->name, channel))
           break;
   }
   return c;
}


CHAN* FindChanByWin(Widget w)
{
   CHAN *c;

   for (c = channels; c; c = c->next) {
       if (c->w == w)
           break;
   }
   return c;
}


int CmdMatch(String s, String cmd, int len)
{
   if (strlen(s) < len)
       return 0;
   return !strncmp(s, cmd, strlen(s));
}


int SplitServer(String s, String *name, String *port, String *meta)
{
   String p = s;

   while (isspace(*p))
       p++;
   if (! *p)
       return 0;
   *name = p;
   if (! (p = strchr(*name, ':')))
       return 0;
   *p++ = 0;
   *port = p;
   if (! (p = strchr(*port, ':')))
       return 0;
   *p++ = 0;
   *meta = p;
   return 1;
}


String GetFileTail(String fileName)
{
   String p, p2, tail;

   if ((p = strrchr(fileName, '\\'))) {
       tail = p;
       *p = '\0';
       if ((p2 = strrchr(fileName, '\\')))
           tail = p2;
       *p = '\\';
   } else if ((p = strrchr(fileName, '/'))) {
       tail = p;
       *p = '\0';
       if ((p2 = strrchr(fileName, '/')))
           tail = p2;
       *p = '/';
   } else
       tail = fileName;

   if (tail != fileName)
       tail++;

   return XtNewString(tail);
}


String GetDir(String fileName)
{
   String tail, dir;
   int dirLen;

   tail = strrchr(fileName, '/');
   dirLen = tail - fileName;
   if (dirLen) {
       dir = XtCalloc(dirLen + 1, 1);
       strncpy(dir, fileName, dirLen);
   } else
       dir = XtNewString("/");

   return dir;
}


int RegEx(String s, String regExpr, int nmatch, regmatch_t pmatch[])
{
   regex_t reg;
   char rbuf[256];
   int r;

   if((r = regcomp(&reg, regExpr, REG_EXTENDED))) {
       regerror(r, &reg, rbuf, 256);
       ErrMsg(rbuf);
       regfree(&reg);
       return -1;
   }

   r = regexec(&reg, s, nmatch, pmatch, 0);
   regfree(&reg);
   return r ? 0 : 1;
}


void CenterDialog(Widget d)
{
   Dimension width, height, scrWidth, scrHeight;
   Widget topLevel = XtParent(d);

   XtVaSetValues(topLevel, XmNmappedWhenManaged, False, NULL);
   XtManageChild(d);

   XtVaGetValues(d, XmNwidth, &width, XmNheight, &height, NULL);
   scrWidth = WidthOfScreen(XtScreen(d));
   scrHeight = HeightOfScreen(XtScreen(d));
   XtVaSetValues(d, XmNx, (scrWidth / 2) - (width / 2),
           XmNy, (scrHeight / 2) - (height / 2), NULL);

   XtVaSetValues(topLevel, XmNmappedWhenManaged, True, NULL);
}


/*
* This procedure will ensure that, if a dialog window is being mapped,
* its contents become visible before returning.  It is intended to be
* used just before a bout of computing that doesn't service the display.
* You should still call XmUpdateDisplay() at intervals during this
* computing if possible.
*
* The monitoring of window states is necessary because attempts to map
* the dialog are redirected to the window manager (if there is one) and
* this introduces a significant delay before the window is actually mapped
* and exposed.  This code works under mwm, twm, uwm, and no-wm.  It
* doesn't work (but doesn't hang) with olwm if the mainwindow is iconified.
*
* The argument to ForceDialog is any widget in the dialog (often it
* will be the BulletinBoard child of a DialogShell).
*/

void ForceDialog(Widget w)
{
   Widget diashell, topshell;
   Window diawindow, topwindow;
   Display *dpy;
   XWindowAttributes xwa;
   XEvent event;
   XtAppContext cxt;

/* Locate the shell we are interested in.  In a particular instance, you
* may know these shells already.
*/

   for (diashell = w; !XtIsShell(diashell);
        diashell = XtParent(diashell));

/* Locate its primary window's shell (which may be the same) */

   for (topshell = diashell; !XtIsTopLevelShell(topshell);
        topshell = XtParent(topshell));

   if (XtIsRealized(diashell) && XtIsRealized(topshell)) {
       dpy = XtDisplay(topshell);
       diawindow = XtWindow(diashell);
       topwindow = XtWindow(topshell);
       cxt = XtWidgetToApplicationContext(diashell);

/* Wait for the dialog to be mapped.  It's guaranteed to become so unless... */

       while (XGetWindowAttributes(dpy, diawindow, &xwa),
               xwa.map_state != IsViewable) {

/* ...if the primary is (or becomes) unviewable or unmapped, it's
probably iconified, and nothing will happen. */

           if (XGetWindowAttributes(dpy, topwindow, &xwa),
                   xwa.map_state != IsViewable)
               break;

/* At this stage, we are guaranteed there will be an event of some kind.
Beware; we are presumably in a callback, so this can recurse. */

           XtAppNextEvent(cxt, &event);
           XtDispatchEvent(&event);
       }

#if 0
/* Wait for focus if explicit focus policy */

       if (focusPolicy == XmEXPLICIT) {
           while(! XmGetFocusWidget(diashell)) {
               XtAppNextEvent(cxt, &event);
               XtDispatchEvent(&event);
           }
       }
#endif

   }

   XmUpdateDisplay(topshell);
}


void ForceWindow(Widget w)
{
   Window win;
   Display *dpy;
   XWindowAttributes xwa;
   XEvent event;
   XtAppContext cxt;

   if (XtIsRealized(w)) {
       dpy = XtDisplay(w);
       win = XtWindow(w);
       cxt = XtWidgetToApplicationContext(w);

/* Wait for the window to be mapped. */

       while (XGetWindowAttributes(dpy, win, &xwa),
               xwa.map_state != IsViewable) {
           XtAppNextEvent(cxt, &event);
           XtDispatchEvent(&event);
       }

#if 0
/* Wait for focus if explicit focus policy */

       if (focusPolicy == XmEXPLICIT) {
           while(! XmGetFocusWidget(w)) {
               XtAppNextEvent(cxt, &event);
               XtDispatchEvent(&event);
           }
       }
#endif

   }

   XmUpdateDisplay(w);
}