/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1998
 * Netscape Communications Corporation.  All Rights Reserved.
 */

/* Image.cpp: Class for managing all imaging rendering issues
 * Created: Radha Kulkarni <radha@netscape.com> 21-Aug-1998
 */


#include "il_util.h"
#include "View.h"
#include "Frame.h"
#include "Image.h"

#ifdef DEBUG_radha
#define D(x) x
#else
#define D(x)
#endif


XFE_Image::XFE_Image(XFE_Component * frame, char * imageURL, fe_colormap * cmap, Widget  baseWidget) 
{

  // Initializations 
  m_urlString = strdup(imageURL);
  completelyLoaded = False;
  badImage = False;
  m_imageContext = NULL;
  fec = NULL;
  cxtInitSucceeded = False;
  m_image = NULL;
  m_mask = NULL;
  m_frame = frame;
  m_badImage = 0;


  // Create new context
  m_imageContext = XP_NewContext();
  fec = XP_NEW_ZAP(fe_ContextData);


  if (m_imageContext && fec) 
  {
    m_imageContext->type = MWContextIcon;
    CONTEXT_DATA(m_imageContext) = fec;

    // Set up the image library callbacks
    CONTEXT_DATA(m_imageContext)->DisplayPixmap = (DisplayPixmapPtr)fe_DisplayPixmap;
    CONTEXT_DATA(m_imageContext)->NewPixmap = (NewPixmapPtr)fe_NewPixmap;
    CONTEXT_DATA(m_imageContext)->ImageComplete = (ImageCompletePtr)fe_ImageComplete;

   
    /* 
     * Stolen from Frame.cpp. 
     */
    CONTEXT_DATA (m_imageContext)->colormap = cmap;
    CONTEXT_WIDGET (m_imageContext) = baseWidget; 
    CONTEXT_DATA(m_imageContext)->drawing_area = baseWidget;

    m_imageContext->funcs = fe_BuildDisplayFunctionTable();
    m_imageContext->convertPixX = m_imageContext->convertPixY = 1;
    m_imageContext->is_grid_cell = FALSE;
    m_imageContext->grid_parent = NULL;

    XP_AddContextToList(m_imageContext);
    fe_InitIconColors(m_imageContext);

    // Use colors from prefs

    LO_Color *color;

    color = &fe_globalPrefs.links_color;
    CONTEXT_DATA(m_imageContext)->link_pixel = fe_GetPixel(m_imageContext, 
							   color->red, 
							   color->green, 
							   color->blue);

    color = &fe_globalPrefs.vlinks_color;
    CONTEXT_DATA(m_imageContext)->vlink_pixel = fe_GetPixel(m_imageContext, 
							    color->red, 
							    color->green, 
							    color->blue);

    color = &fe_globalPrefs.text_color;
    CONTEXT_DATA(m_imageContext)->default_fg_pixel =fe_GetPixel(m_imageContext,
								color->red, 
								color->green, 
								color->blue);

    color = &fe_globalPrefs.background_color;
    CONTEXT_DATA(m_imageContext)->default_bg_pixel =fe_GetPixel(m_imageContext,
								color->red, 
								color->green, 
								color->blue);


    SHIST_InitSession (m_imageContext);
    fe_load_default_font(m_imageContext);
    {
       Pixel unused_select_pixel;
       XmGetColors (XtScreen (baseWidget),
		    fe_cmap(m_imageContext),
		    CONTEXT_DATA (m_imageContext)->default_bg_pixel,
		    &(CONTEXT_DATA (m_imageContext)->fg_pixel),
		    &(CONTEXT_DATA (m_imageContext)->top_shadow_pixel),
		    &(CONTEXT_DATA (m_imageContext)->bottom_shadow_pixel),
		    &unused_select_pixel);
    }


    /* Add mapping between MWContext and frame */
    ViewGlue_addMapping((XFE_Frame *)m_frame, m_imageContext);

    /* Initialize the Imagelib callbacks, Imagecontexts, 
     * imageobserver and group contexts
     */
    if (!fe_init_image_callbacks(m_imageContext))
    {
       cxtInitSucceeded = False;
       return;
    }

    fe_InitColormap (m_imageContext);
    cxtInitSucceeded = True;
  }
  else
  {
    cxtInitSucceeded = False;
    return;
  }

}


XFE_Image::~XFE_Image(void)
{

  free(m_urlString);

  // S'd I destroy the m_mask and m_image and the clientdata inside them?
  // Destroy the Image groupcontexts and observers
  if (m_imageContext)
  {
      PRBool observer_removed_p;

      if (m_imageContext->color_space) 
      {
         D(printf("Deleting image color spaces\n");)
	     IL_ReleaseColorSpace(m_imageContext->color_space);
          m_imageContext->color_space = NULL;
      }

      /* Destroy the image group context after removing the image group 
       * observer
       */
      if (m_imageContext->img_cx) {
           D(printf("Deleting the image group observer and context\n");)
            observer_removed_p =
                IL_RemoveGroupObserver(m_imageContext->img_cx,
                                       fe_ImageGroupObserver,
                                       (void *)m_imageContext);

			IL_DestroyGroupContext(m_imageContext->img_cx);
			m_imageContext->img_cx = NULL;
      }
  }  /* m_imageContext */
  free (CONTEXT_DATA (m_imageContext));
  free (m_imageContext);

}


/* returns if the image has been completely loaded */
Boolean 
XFE_Image::isImageLoaded(void)
{
     return (completelyLoaded);

}


/* return the image pixmap */
Pixmap
XFE_Image::getPixmap(void)
{
   if (m_image)
     return (Pixmap)(((fe_PixmapClientData *)(m_image->client_data))->pixmap);
   return (Pixmap)NULL;
}

/* Return the image mask */
Pixmap
XFE_Image::getMask(void)
{
    if (m_mask)
       return (Pixmap)(((fe_PixmapClientData *)(m_mask->client_data))->pixmap);
    return (Pixmap)NULL;

}

/* Get image width */
PRInt32
XFE_Image::getImageWidth(void)
{
  return(imageWidth);
}

/* Get image height */
PRInt32
XFE_Image::getImageHeight(void)
{
  return(imageHeight);
}


/* Call NET_GetURL() to fetch the image */
void 
XFE_Image::loadImage(void)
{

   if (cxtInitSucceeded)
     NET_GetURL(NET_CreateURLStruct(m_urlString, NET_DONT_RELOAD), FO_CACHE_AND_PRESENT,   m_imageContext, Image_GetUrlExitRoutine);

}

extern "C"
{
void
Image_GetUrlExitRoutine(URL_Struct *pUrl, int iStatus, MWContext *pContext)  
{
	//	Report any errors.
	if(iStatus < 0 && pUrl->error_msg != NULL)	
	{
         printf("Couldn't load image. Need to put the bad image up\n");
	}

    XP_FREE(pUrl);
}
};






///////////////////////////////////////////////////////////////////////////
//               ImageLibrary Callbacks                                  //
///////////////////////////////////////////////////////////////////////////

extern "C"
{

  /* The XFE handle to the image library callback -IMGCB_DisplayPixmap */
void 
fe_DisplayPixmap(MWContext * context, IL_Pixmap * image, IL_Pixmap * mask, PRInt32  x, PRInt32  y, PRInt32  x_offset,  PRInt32 y_offset, PRInt32  width, PRInt32 height) 
{

   XFE_Frame *    frameHandle=(XFE_Frame *)NULL;
   /* Get the handle to the frame from the context */
    
   if (context)
       frameHandle = (XFE_Frame *)ViewGlue_getFrame(context);

   /* Check the context type. If it is anything other than MWContextIcon
    * let it do the regular image processing. If it is MWContextIcon,
    * Look for a NavCenterVIew in th frame and let the view handle the
    * pixmap display
    */

        if (context->type != MWContextIcon)
        {
            /* Call the frame's displayImage method */
            fe_DisplayImage(context, image, mask, x, y, x_offset, y_offset,
                                width, height);
        }
        else
        {
            /* If the context type is MWContextIcon, get a handle to
             * NavCenterView and let it handle pixmap
             */
            XFE_View * navCenterView = XFE_View::getNavCenterView(frameHandle->getView());
            if (navCenterView)
            {
               Widget buttonWidget = CONTEXT_WIDGET(context);
               navCenterView->handleDisplayPixmap(buttonWidget, image, mask, width, height);
            }
            
        }

}  /* DisplayPixmap */



/* The XFE handle to the image library callback _IMGCB_NewPixmap */

void 
fe_NewPixmap(MWContext * context, IL_Pixmap * image, Boolean mask)
{

   XFE_Frame *    frameHandle=(XFE_Frame *)NULL;
   /* Get the handle to the frame from the context */
    
   if (context)
       frameHandle = ViewGlue_getFrame(context);

   /* Check the context type. If it is anything other than MWContextIcon
    * simply return, 'coz the frames don't have any processing for the
    * the NewPixmap callback.  If it is MWContextIcon,
    * Look for a NavCenterView in th frame and let the view handle it.
    */

   if (frameHandle)
   {
        if (context->type == MWContextIcon)
        {
            /* If the context type is MWContextIcon, get a handle to          
             * NavCenterView and let it handle pixmap
             */
            XFE_View * navCenterView = XFE_View::getNavCenterView(frameHandle->getView());
            if (navCenterView)
            {
                Widget buttonWidget = CONTEXT_WIDGET(context);
                navCenterView->handleNewPixmap(buttonWidget, image, mask);
            }
            

        }
   }
}   /* NewPixmap */


/* The XFE handle to the image library callback _IMGCB_ImageComplete */
void
fe_ImageComplete(MWContext * context, IL_Pixmap * image)
{
   XFE_Frame *    frameHandle=(XFE_Frame *)NULL;
   /* Get the handle to the frame from the context */
    
   if (context)
       frameHandle = (XFE_Frame *)ViewGlue_getFrame(context);

   /* Check the context type. If it is anything other than MWContextIcon
    * simply return, 'coz the frames don't have any processing for the
    * the NewPixmap callback.  If it is MWContextIcon,
    * Look for a NavCenterView in th frame and let the view handle it.
    */

   if (frameHandle)
   {
        if (context->type == MWContextIcon)
        {
            /* If the context type is MWContextIcon, let the NavCenterView
             * handle it
             */
            XFE_View * navCenterView = XFE_View::getNavCenterView(frameHandle->getView());
            if (navCenterView)
            {
                Widget buttonWidget = CONTEXT_WIDGET(context);
                navCenterView->handleImageComplete(buttonWidget, image);
            }
          }
   }

}  /* ImageComplete  */


/* The actual XFE function that renders image on a HTML area */
void
fe_DisplayImage(MWContext * context, IL_Pixmap * image, 
                        IL_Pixmap * mask, PRInt32 x, PRInt32 y,
                        PRInt32 x_offset, PRInt32 y_offset, PRInt32 width,PRInt32  height)
{

    int32 img_x_offset, img_y_offset;   /* Offset of image in drawable. */
    int32 rect_x_offset, rect_y_offset; /* Offset of update rect in
                                           drawable. */
    NI_PixmapHeader *img_header = &image->header;
    uint32 img_width = img_header->width;     /* Image width. */
    uint32 img_height = img_header->height;   /* Image height. */

    Widget widget = CONTEXT_WIDGET(context);
    fe_Drawable *fe_drawable = CONTEXT_DATA(context)->drawable;
    Drawable drawable = fe_drawable->xdrawable;
    Display *dpy = XtDisplay(widget);
    Pixmap img_x_pixmap, mask_x_pixmap;
    fe_PixmapClientData *img_client_data, *mask_client_data;
    GC gc;
    XGCValues gcv;
    unsigned long flags;
    XP_Bool tiling_required = FALSE;
    
    /* Check for zero display area. */
    if (width == 0 || height == 0)
        return;

    /* Retrieve the server pixmaps. */
    img_client_data = (fe_PixmapClientData *)image->client_data;
    if (!img_client_data)
        return;
    img_x_pixmap = img_client_data->pixmap;
    if (!img_x_pixmap)
        return;
    if (mask) {
        mask_client_data = (fe_PixmapClientData *)mask->client_data;
        mask_x_pixmap = mask_client_data->pixmap;
    }

    /* Determine whether tiling is required. */
    if ((x_offset + width > img_width) || (y_offset + height > img_height))
        tiling_required = TRUE;

    /* Compute the offset into the drawable of the image origin. */
    img_x_offset = x - CONTEXT_DATA(context)->document_x +
        fe_drawable->x_origin;
    img_y_offset = y - CONTEXT_DATA(context)->document_y +
        fe_drawable->y_origin;

    /* Compute the offset into the drawable for the area to be drawn. */
    rect_x_offset = img_x_offset + x_offset;
    rect_y_offset = img_y_offset + y_offset;

    /* Do the actual drawing.  There are several cases to be dealt with:
       transparent vs non-transparent, tiled vs non-tiled and clipped by
       compositor's clip region vs not clipped. */
    memset(&gcv, ~0, sizeof (XGCValues));
    if (mask) {                 /* Image is transparent. */
        if (tiling_required) {
            /* Offsets are measured wrt the origin of the tiled mask to
               be generated. */
            int x_tile_offset = img_x_offset - rect_x_offset;
            int y_tile_offset = img_y_offset - rect_y_offset;
            Pixmap tmp_pixmap = 0;

            /* Create the mask by tiling the mask_x_pixmap and computing
               the intersection with the compositor's clip region. */
            tmp_pixmap =
                fe_TiledMaskWithClipRegion(dpy, drawable, mask_x_pixmap,
                                           width, height, x_tile_offset,
                                           y_tile_offset, -rect_x_offset,
                                           -rect_y_offset,
                                           (Region)fe_drawable->clip_region);
            
            /* Create the GC.  Don't attempt to get a GC from the GC cache
               because we are using a temporary mask pixmap. */
            gcv.fill_style = FillTiled;
            gcv.tile = img_x_pixmap;
            gcv.ts_x_origin = img_x_offset;
            gcv.ts_y_origin = img_y_offset;
            gcv.clip_mask = tmp_pixmap;
            gcv.clip_x_origin = rect_x_offset;
            gcv.clip_y_origin = rect_y_offset;
            flags = GCFillStyle | GCTile | GCTileStipXOrigin |
                GCTileStipYOrigin | GCClipMask | GCClipXOrigin | GCClipYOrigin;
            gc = XCreateGC(dpy, drawable, flags, &gcv);

            /* Draw the image (transparent and tiled.) */
            XFillRectangle (dpy, drawable, gc, rect_x_offset, rect_y_offset,
                            width, height);

            /* Clean up. */
            XFreeGC(dpy, gc);
            XFreePixmap(dpy, tmp_pixmap);
        }
        else {                  /* Tiling not required. */
            if (fe_drawable->clip_region) {
                
                /* Draw the image (transparent, non-tiled and with
                   clip_region.)  x_offset and y_offset are wrt the image
                   origin, while rect_x_offset and rect_y_offset are wrt the
                   drawable origin. */
                fe_DrawMaskedImageWithClipRegion(dpy, drawable, img_x_pixmap,
                                                 mask_x_pixmap, width, height,
                                                 img_x_offset, img_y_offset,
                                                 x_offset, y_offset,
                                                 (Region)fe_drawable->clip_region);
            }
            else {              /* No clip region. */
                /* XXX transparent, non-tiled and no clip_region. */
            }
        }
    }
    else {                      /* Image is not transparent. */
        if (tiling_required) {
            /* Get the GC from the GC cache.  If the compositor has given us
               a clip region, then the GC must have a matching clip mask. */
            gcv.fill_style = FillTiled;
            gcv.tile = img_x_pixmap;
            gcv.ts_x_origin = img_x_offset;
            gcv.ts_y_origin = img_y_offset;
            flags = GCFillStyle | GCTile | GCTileStipXOrigin |
                GCTileStipYOrigin;
            if (fe_drawable->clip_region)
                gc = fe_GetGCfromDW(dpy, drawable, flags, &gcv,
                                    (Region)fe_drawable->clip_region);
            else
                gc = fe_GetGCfromDW(dpy, drawable, flags, &gcv, NULL);

            /* Draw the image (opaque and tiled.) */
            XFillRectangle (dpy, drawable, gc, rect_x_offset, rect_y_offset,
                            width, height);  
        }
        else {                  /* Tiling not required. */
            /* Get the GC from the GC cache.  If the compositor has given us
               a clip region, then the GC must have a matching clip mask. */
            gcv.function = GXcopy;
            if (fe_drawable->clip_region)
                gc = fe_GetGCfromDW(dpy, drawable, GCFunction,
                                    &gcv, (Region)fe_drawable->clip_region);
            else
                gc = fe_GetGCfromDW(dpy, drawable, GCFunction, &gcv, NULL);
 
            /* Draw the image (opaque and non-tiled.) */
            XCopyArea (dpy, img_x_pixmap, drawable, gc, x_offset,
                       y_offset, width, height, rect_x_offset, rect_y_offset);
        }
    }


}  /* displayImage */
};   /* extern "C"  */

