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

    This file is part of Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Hector Peraza.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

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

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
#include <X11/Xlib.h>
#include <X11/xpm.h>

#include <xclass/utils.h>
#include <xclass/OXWindow.h>
#include <xclass/OPicture.h>

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

OPicturePool::OPicturePool(const OXClient *client, const char *path) {
  _client = client;
  if (path) {
    _path = new char[strlen(path)+1];
    strcpy(_path, path);
  } else {
    _path = NULL;
  }
  _plist  = NULL;
}

OPicturePool::~OPicturePool() {
  SListPixmapElt *ptr, *next;

  ptr = _plist;
  while (ptr) {
    next = ptr->next;
    XFreePixmap(_client->GetDisplay(), ptr->picture->_pic);
    if (ptr->picture->_mask != None)
      XFreePixmap(_client->GetDisplay(), ptr->picture->_mask);
    XpmFreeAttributes(&(ptr->picture->_attributes));
    delete ptr->picture;
    delete ptr;
    ptr = next;
  }
  if (_path) delete[] _path;
}

void OPicturePool::FreePicture(const OPicture *pic) {
  SListPixmapElt *ptr, *ptr2;

  // remove the picture if nobody is using it anymore

  if (_plist == NULL) return;

  if (_plist->picture == pic) {
    ptr = _plist;
    ptr->count--;
    if (ptr->count > 0) return;
    _plist = _plist->next;
    XFreePixmap(_client->GetDisplay(), ptr->picture->_pic);
    if (ptr->picture->_mask != None)
      XFreePixmap(_client->GetDisplay(), ptr->picture->_mask);
    XpmFreeAttributes(&(ptr->picture->_attributes));
    delete ptr->picture;
    delete ptr;
  } else {
    for (ptr = _plist; (ptr->next != NULL) && (ptr->next->picture != pic); ptr = ptr->next);
    if (ptr->next == NULL) return; // not found!
    ptr2 = ptr->next;
    ptr2->count--;
    if (ptr2->count > 0) return;
    ptr->next = ptr2->next;
    XFreePixmap(_client->GetDisplay(), ptr2->picture->_pic);
    if (ptr2->picture->_mask != None)
      XFreePixmap(_client->GetDisplay(), ptr2->picture->_mask);
    XpmFreeAttributes(&(ptr2->picture->_attributes));
    delete ptr2->picture;
    delete ptr2;
  }

  return;
}

const OPicture *OPicturePool::GetPicture(const char *name, char **data) {
  SListPixmapElt *ptr, *nw;

  for (ptr = _plist; ptr != NULL; ptr = ptr->next)
    if ((strcmp(name, (ptr->picture)->_name) == 0) && 
         !ptr->picture->_scaled) {
      ptr->count++;
      return ptr->picture;
    }

  OPicture *pic = new OPicture(name);
  pic->_attributes.colormap  = _defaultColormap;
  pic->_attributes.closeness = 40000; // Allow for "similar" colors
  pic->_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap |
                               XpmCloseness;
  
  if (XpmCreatePixmapFromData(_client->GetDisplay(),
                          (_client->GetRoot())->GetId(), data,
                          &(pic->_pic), &(pic->_mask),
                          &(pic->_attributes)) == XpmSuccess) { 
    nw = new SListPixmapElt;
    nw->next    = NULL;
    nw->picture = pic;
    nw->count   = 1;
    if (_plist == NULL) {
      _plist = nw;
    } else {
      for (ptr = _plist; ptr->next != NULL; ptr = ptr->next);
      ptr->next = nw;
    }
    return pic;
  } else {
    delete pic;
    return NULL;
  }
}

const OPicture *OPicturePool::GetPicture(const char *name) {
  char tmp[MAXPATHLEN];
  SListPixmapElt *ptr, *nw;
  
  for (ptr = _plist; ptr != NULL; ptr = ptr->next)
    if ((strcmp(name, (ptr->picture)->_name) == 0) && 
         !ptr->picture->_scaled) {
      ptr->count++;
      return ptr->picture;
    }

  OPicture *pic = new OPicture(name);
  pic->_attributes.colormap  = _defaultColormap;
  pic->_attributes.closeness = 40000; // Allow for "similar" colors
  pic->_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap |
                               XpmCloseness;
  
  if (!LocatePicture((char *) name, _path, tmp)) {
    // unable to locate picture
    delete pic;
    return NULL;
  }

  if (XpmReadFileToPixmap(_client->GetDisplay(),
                          (_client->GetRoot())->GetId(), tmp,
			  &(pic->_pic), &(pic->_mask),
                          &(pic->_attributes)) == XpmSuccess) { 
    nw = new SListPixmapElt;
    nw->next    = NULL;
    nw->picture = pic;
    nw->count   = 1;
    if (_plist == NULL) _plist = nw;
    else {
      for (ptr = _plist; ptr->next != NULL; ptr = ptr->next);
      ptr->next = nw;
    }
    return pic;
  } else {
    delete pic;
    return NULL;
  }
}

const OPicture *OPicturePool::GetPicture(const char *name, char **data,
                                         int new_width, int new_height) {
  SListPixmapElt *ptr, *nw;
  char **smalldata;
  double x, y, xscale, yscale;
  int    x1, y1;
  int    retc, i, height, width, colors, chars;
  int    totalheight;
  int    headersize;
  int    pixels;

  for (ptr = _plist; ptr != NULL; ptr = ptr->next)
    if ((strcmp(name, (ptr->picture)->_name) == 0) && 
        (ptr->picture->GetWidth() == new_width) &&
        (ptr->picture->GetHeight() == new_height)) {
      ptr->count++;
      return ptr->picture;
    }

  OPicture *pic = new OPicture(name, True);
  pic->_attributes.colormap  = _defaultColormap;
  pic->_attributes.closeness = 40000; // Allow for "similar" colors
  pic->_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap |
                               XpmCloseness;


  sscanf(data[0], "%d %d %d %d", &width, &height, &colors, &chars);
  headersize = colors + 1;
  yscale = (double) new_height / (double) height;
  xscale = (double) new_width / (double) width;
  totalheight = (colors + new_height + 1);

  if ((width != new_width) || (height != new_height)) {

    smalldata = new char* [totalheight + 1];
    if (!smalldata) FatalError("Not enough memory.");

    smalldata[0] = new char[30];
    for (i = 1; i < headersize; i++){
      smalldata[i] = new char [strlen(data[i]) + 1];
      if (!smalldata[i])
        FatalError("Not enough memory.");
    }
    for (i = headersize; i < totalheight + 1; i++) {
      smalldata[i] = new char[(new_width * chars) + 1];
      if (!smalldata[i])
        FatalError("Not enough memory.");
    }

    sprintf(smalldata[0], "%d %d %d %d", new_width, new_height, colors, chars);

    for (i = 1; i < headersize; i++) strcpy(smalldata[i], data[i]);

    y = headersize;
    for (y1 = headersize; y1 < new_height + headersize; y1++) {
      x = 0;
      for (x1 = 0; x1 < new_width; x1++) {
        for (pixels = 0; pixels < chars; pixels++)
          smalldata[y1][x1+pixels] = data[(int)y][(int)x + pixels];
        x += 1.0 / xscale;
      }
      smalldata[y1][x1] = '\0';
      y += 1.0 / yscale;
    }

    retc = XpmCreatePixmapFromData(_client->GetDisplay(),
                            (_client->GetRoot())->GetId(), smalldata,
                           &(pic->_pic), &(pic->_mask),
                           &(pic->_attributes));

    for (i = 0; i < totalheight + 1; i++) delete[] smalldata[i];
    delete[] smalldata;

  } else {

    retc = XpmCreatePixmapFromData(_client->GetDisplay(),
                            (_client->GetRoot())->GetId(), data,
                           &(pic->_pic), &(pic->_mask),
                           &(pic->_attributes));
  }

  XpmFree(data);

  if (retc != XpmSuccess) {
    delete pic;
    return NULL;
  }

  nw = new SListPixmapElt;
  nw->next    = NULL;
  nw->picture = pic;
  nw->count   = 1;
  if (_plist == NULL) 
    _plist = nw;
  else {
    for (ptr = _plist; ptr->next != NULL; ptr = ptr->next);
    ptr->next = nw;
  }

  return pic;
}

const OPicture *OPicturePool::GetPicture(const char *name,
                                         int new_width, int new_height) {
  char tmp[MAXPATHLEN];
  SListPixmapElt *ptr, *nw;
  char **data;
  char **smalldata;
  double x, y, xscale, yscale;
  int    x1, y1;
  int    retc, i, height, width, colors, chars;
  int    totalheight;
  int    headersize;
  int    pixels;

  for (ptr = _plist; ptr != NULL; ptr = ptr->next)
    if ((strcmp(name, (ptr->picture)->_name) == 0) && 
        (ptr->picture->GetWidth() == new_width) &&
        (ptr->picture->GetHeight() == new_height)) {
      ptr->count++;
      return ptr->picture;
    }

  OPicture *pic = new OPicture(name, True);
  pic->_attributes.colormap  = _defaultColormap;
  pic->_attributes.closeness = 40000; // Allow for "similar" colors
  pic->_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap |
                               XpmCloseness;

  if (!LocatePicture((char *) name, _path, tmp)) {
    // unable to locate picture   
    delete pic;
    return NULL;
  } 

  if (XpmReadFileToData(tmp, &data) != XpmSuccess) {
    delete pic;
    return NULL;
  }

  sscanf(data[0], "%d %d %d %d", &width, &height, &colors, &chars);
  headersize = colors + 1;
  yscale = (double) new_height / (double) height;
  xscale = (double) new_width / (double) width;
  totalheight = (colors + new_height + 1);

  if ((width != new_width) || (height != new_height)) {

    smalldata = new char* [totalheight + 1];
    if (!smalldata) FatalError("Not enough memory.");

    smalldata[0] = new char[30];
    for (i = 1; i < headersize; i++){
      smalldata[i] = new char [strlen(data[i]) + 1];
      if (!smalldata[i])
        FatalError("Not enough memory.");
    }
    for (i = headersize; i < totalheight + 1; i++) {
      smalldata[i] = new char[(new_width * chars) + 1];
      if (!smalldata[i])
        FatalError("Not enough memory.");
    }

    sprintf(smalldata[0], "%d %d %d %d", new_width, new_height, colors, chars);

    for (i = 1; i < headersize; i++) strcpy(smalldata[i], data[i]);

    y = headersize;
    for (y1 = headersize; y1 < new_height + headersize; y1++) {
      x = 0;
      for (x1 = 0; x1 < new_width; x1++) {
        for (pixels = 0; pixels < chars; pixels++)
          smalldata[y1][x1+pixels] = data[(int)y][(int)x + pixels];
        x += 1.0 / xscale;
      }
      smalldata[y1][x1] = '\0';
      y += 1.0 / yscale;
    }

    retc = XpmCreatePixmapFromData(_client->GetDisplay(),
                            (_client->GetRoot())->GetId(), smalldata,
                           &(pic->_pic), &(pic->_mask),
                           &(pic->_attributes));

    for (i = 0; i < totalheight + 1; i++) delete[] smalldata[i];
    delete[] smalldata;

  } else {

    retc = XpmCreatePixmapFromData(_client->GetDisplay(),
                            (_client->GetRoot())->GetId(), data,
                           &(pic->_pic), &(pic->_mask),
                           &(pic->_attributes));
  }

  XpmFree(data);

  if (retc != XpmSuccess) {
    delete pic;
    return NULL;
  }

  nw = new SListPixmapElt;
  nw->next    = NULL;
  nw->picture = pic;
  nw->count   = 1;
  if (_plist == NULL) 
    _plist = nw;
  else {
    for (ptr = _plist; ptr->next != NULL; ptr = ptr->next);
    ptr->next = nw;
  }

  return pic;
}

int OPicturePool::LocatePicture(char *name, char *path, char *bfr) {
  char *colon_pos;

  // if name contains path
  if (strchr(name, '/' )) {
    // consider as file referece
    strcpy(bfr, name);
    if (access(bfr, R_OK) == 0) {
      // file found
      return 1;
    }
  }

  // if no path given, use default
  if (!path) {
    if ((path = getenv("OX_POOL")) == NULL) {
      path = OX_DEFAULT_POOL;
    }
  }

  while ((colon_pos = strchr(path, ':'))) {
    // colon found
    if (colon_pos != path) {
      memcpy(bfr, path, colon_pos-path);
      sprintf(&bfr[colon_pos-path], "/%s", name);
      if (access(bfr, R_OK) == 0) {
        // file found
        return 1;
      }
      sprintf(&bfr[colon_pos-path], "/%s.gz", name);
      if (access(bfr, R_OK) == 0 ) {
        // file found
        return 1;
      } 
    } // else empty path
    path = colon_pos+1;
  }

  if (path[0]) {
    // non empty rest
    sprintf(bfr, "%s/%s", path, name);
    if (access(bfr, R_OK) == 0) {
      // file found
      return 1;
    } 
    sprintf(bfr, "%s/%s.gz", path, name);
    if (access(bfr, R_OK ) == 0) {
      // file found
      return 1;
    } 
  }

  bfr[0] = '\0';
  return 0;
}

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

void OPicture::Draw(Display *dpy, XID id, GC gc, int x, int y) const {
  XGCValues gcv;  

  unsigned long gcm = GCClipMask | GCClipXOrigin | GCClipYOrigin;
  gcv.clip_mask = _mask;
  gcv.clip_x_origin = x;
  gcv.clip_y_origin = y;
  XChangeGC(dpy, gc, gcm, &gcv);
  XCopyArea(dpy, _pic, id, gc, 0, 0,
                 _attributes.width, _attributes.height, x, y);
  gcm = GCClipMask;
  gcv.clip_mask = None;
  XChangeGC(dpy, gc, gcm, &gcv);
}
