#include "phluid.h"
#include "debug.h"

static void render_name(Client *);

void
make_client(Window * w)
{
  Client *c;
  XWindowAttributes attr;
  XSetWindowAttributes pattr;

  XGetWindowAttributes(disp, *w, &attr);
  /* popup window, don't make a client */
  if (attr.override_redirect)
    return;

  c = malloc(sizeof *c);

  XFetchName(disp, *w, &c->name);
  c->shaded = 0;
  c->window = *w;
  c->x = attr.x;
  c->y = attr.y;
  c->width = attr.width;
  c->height = attr.height;
  c->ignore_unmap = 0;
  c->next = client_list_head;
  client_list_head = c;
  c->decals = NULL;
  c->decals_to_render = NULL;

/* XGrabServer(disp); *//* is this necessary? */

  /* set attributes for window decoration window */
  pattr.override_redirect = True;
  pattr.background_pixel = bg.pixel;
  pattr.border_pixel = bd.pixel;
  pattr.event_mask =
      StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask |
      ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask |
      EnterWindowMask;

  /* make window decoration window */
  c->frame =
      XCreateWindow(disp, root, c->x, c->y, c->width,
                    c->height + TITLEBAR_HEIGHT, 1, DefaultDepth(disp,
                                                                 screen),
                    CopyFromParent, DefaultVisual(disp, screen),
                    CWOverrideRedirect | CWBackPixel | CWBorderPixel |
                    CWEventMask, &pattr);

  XAddToSaveSet(disp, c->window);
  XSelectInput(disp, c->window, PropertyChangeMask);
  XSetWindowBorderWidth(disp, c->window, 0);
  XMoveWindow(disp, c->window, c->x, c->y + TITLEBAR_HEIGHT);

  /* going to get an UnmapNotify from XReparentWindow() */
  if (attr.map_state == IsViewable)
    c->ignore_unmap++;
  XReparentWindow(disp, c->window, c->frame, 0, TITLEBAR_HEIGHT);

  if (attr.map_state == IsViewable) {
    XWMHints *wm_hints;
    wm_hints = XAllocWMHints();
    wm_hints = XGetWMHints(disp, c->window);
    if (wm_hints->initial_state == IconicState) {
      c->visible = 0;
    } else {
      CARD32 data[2];
      data[0] = NormalState;
      data[1] = None;
      XMapWindow(disp, c->frame);
      c->visible = 1;
      XChangeProperty(disp, c->window, XInternAtom(disp, "WM_STATE", False),
                      XInternAtom(disp, "WM_STATE", False), 32, PropModeReplace,
                      (unsigned char *) data, 2);
    }

    XFree(wm_hints);
  }

  /* XUngrabServer(disp); */

  /* start adding nift with Imlib2 */

  /* make background image */
  c->bg =
      imlib_load_image(PACKAGE_DATA_DIR "/themes/default/images/title_bg.png");

  /* add decals */
  add_new_decal_to_client(c,
                          PACKAGE_DATA_DIR
                          "/themes/default/images/close_normal.png", 1, 1,
                          DecalFnDelete, DecalFnNone, DecalFnDestroy);

  c->updates = imlib_updates_init();
  render_title_bar(c);

  DEBUG_CLIENT_LIST();
}

void
delete_client(Client * c)
{
  Client *tmp;
  if (!c)
    return;

  XGrabServer(disp);
  /* The upcoming XReparentWindow and XMoveWindow calls might generate
     BadWindow errors if the window already got destroyed.  I don't know
     how to avoid this so I'll set a handler to take care of it(*cough* 
     ignore it *cough*) gracefully. */
  XSetErrorHandler(handle_xerror);

  XReparentWindow(disp, c->window, root, c->x, c->y);
  XRemoveFromSaveSet(disp, c->window);
  XDestroyWindow(disp, c->frame);
  XMoveWindow(disp, c->window, c->x, c->y);
  XSync(disp, False);

  /* set back to the default error handler */
  XSetErrorHandler(NULL);
  XUngrabServer(disp);

  if (client_list_head == c)
    client_list_head = c->next;
  else
    for (tmp = client_list_head; tmp && tmp->next; tmp = tmp->next)
      if (tmp->next == c)
        tmp->next = c->next;

  /* free up memory */
  if (c->name)
    XFree(c->name);
  imlib_context_set_image(c->title_bar);
  imlib_free_image();
  imlib_context_set_image(c->bg);
  imlib_free_image();
  free(c);

  DEBUG_CLIENT_LIST();
}

Client *
find_client_by_frame(Window w)
{
  Client *c;
  for (c = client_list_head; c; c = c->next)
    if (c->frame == w)
      return c;
  return NULL;
}

Client *
find_client_by_child(Window w)
{
  Client *c;
  for (c = client_list_head; c; c = c->next)
    if (c->window == w)
      return c;
  return NULL;
}

void
render_title_bar(Client * c)
{
  int i_w, i_h;
  imlib_context_set_image(c->bg);
  i_w = imlib_image_get_width();
  i_h = imlib_image_get_height();
  c->title_bar = imlib_create_image(c->width, TITLEBAR_HEIGHT);
  if (c->bg) {
    imlib_context_set_image(c->title_bar);
    imlib_blend_image_onto_image(c->bg, 0, 0, 0, i_w, i_h, 0, 0,
                                 c->width, TITLEBAR_HEIGHT);
  }

  c->decals_to_render = c->decals;
  render_decals(c);

  imlib_context_set_image(c->title_bar);
  imlib_context_set_blend(0);
  imlib_context_set_drawable(c->frame);
  imlib_render_image_on_drawable(0, 0);
  imlib_context_set_blend(1);

  /* render text */
  c->name_change = 1;
  c->name_width = 0;
  c->name_height = 0;
  redraw_client(c);
}

static void
render_name(Client * c)
{
  Imlib_Font font;
  int text_w, text_h;

  DEBUG_PRINT("in render_name\n");
  text_w = c->name_width;
  text_h = c->name_height;
  imlib_add_path_to_font_path(PACKAGE_DATA_DIR "/ttfonts");
  font = imlib_load_font("tahomabd/10");
  if (font) {
    int i_w, i_h;

    /* cover up old text */
    imlib_context_set_image(c->bg);
    i_w = imlib_image_get_width();
    i_h = imlib_image_get_height();
    imlib_context_set_image(c->title_bar);
    imlib_blend_image_onto_image(c->bg, 0, 0, 0, i_w, i_h, TEXT_X, 0,
                                 c->name_width, TITLEBAR_HEIGHT);

    /* render new text */
    imlib_context_set_font(font);
    imlib_context_set_color(60, 60, 60, 200);
    imlib_get_text_size(c->name, &c->name_width, &c->name_height);
    imlib_context_set_blend(1); /* be sure of blending */
    imlib_text_draw(TEXT_X, TEXT_Y, c->name);
    imlib_free_font();
  }
  /* update changed portion (the union of the old and new text areas) */
  if (c->name_width > text_w)
    text_w = c->name_width;
  if (c->name_height > text_h)
    text_h = c->name_height;
  c->updates =
      imlib_update_append_rect(c->updates, TEXT_X, TEXT_Y, text_w, text_h);
}

void
redraw_client(Client * c)
{
  Imlib_Image buffer;
  int i_w, i_h;
  Imlib_Updates current_update;
  int up_x, up_y, up_w, up_h;   /* geometry of updated portions */

  if (c->name_change > 0) {
    render_name(c);             /* update the name text */
    c->name_change--;
  }

  if (!c->updates)
    return;

  c->updates =
      imlib_updates_merge_for_rendering(c->updates, c->width, TITLEBAR_HEIGHT);

  for (current_update = c->updates; current_update;
       current_update = imlib_updates_get_next(current_update)) {
    imlib_updates_get_coordinates(current_update, &up_x, &up_y, &up_w, &up_h);

    imlib_context_set_image(c->title_bar);
    i_w = imlib_image_get_width();
    i_h = imlib_image_get_height();

    buffer = imlib_create_image(up_w, up_h);
    imlib_context_set_image(buffer);

    /* blend image onto buffer */
    imlib_blend_image_onto_image(c->title_bar, 0, 0, 0, i_w, i_h, -up_x,
                                 -up_y, c->width, TITLEBAR_HEIGHT);

    imlib_context_set_image(buffer);
    imlib_context_set_blend(0);
    imlib_context_set_drawable(c->frame);
    imlib_render_image_on_drawable(up_x, up_y);
    imlib_free_image();
    imlib_context_set_blend(1);
  }
  /* clean up old updates and init new */
  if (c->updates)
    imlib_updates_free(c->updates);
  c->updates = imlib_updates_init();
  XFlush(disp);
}


syntax highlighted by Code2HTML, v. 0.9.1