/* Pango
 * Copyright (C) 1999 Red Hat Software
 *
 * testfonts.c:
 * Copyright (C) 2001 Hans Breuer
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <glib.h>
#include <stdlib.h>
#include <gmodule.h>
#include "pango.h"
#include "pango-impl-utils.h"
#include "pangowin32.h"


#include <errno.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <locale.h>

#include <windows.h>

static HDC pre_render (int width, int height);
static void post_render (HDC hdc, const char* sFile);

static float
calc_duration (GTimeVal *tv1, GTimeVal *tv0)
{
  return (  ((float)tv1->tv_sec - tv0->tv_sec)
	  + (tv1->tv_usec - tv0->tv_usec) / 1000000.0);
}

static int
compare_font_family (PangoFontFamily** a,
		     PangoFontFamily** b)
{
  return strcmp (pango_font_family_get_name (*a), pango_font_family_get_name (*b));
}

int main (int argc, char **argv)
{
  PangoFontMap *fontmap = pango_win32_font_map_for_display();
  PangoContext *context;
  PangoCoverage * coverage = NULL;
  PangoFont* font = NULL;
  PangoFontFamily** families = NULL;
  PangoFontFace** faces = NULL;
  int nb, i;
  gchar* family_name = NULL;
  PangoLanguage *lang = pango_language_from_string (g_win32_getlocale ());
  HDC hdc = NULL;
  int line = 0;
  GTimeVal tv0, tv1;
  int my_font_size = 12;

  printf ("# Pango Font Test\n"
	  "# Language: %s\n"
	  "#\n", pango_language_to_string (lang));

  /* this wasn't necessary with previous version
   *
   * force initialization of built-in engines, otherwise
   * the rendering get's really fast - too fast to work :-(
   */
  context = pango_win32_get_context ();

  if (argc == 1)		/* No arguments given */
    {
      char *std_fonts[] = {"Sans 12", "Serif 12", "Monospace 12"};

      /* try to load some fonts often hardcoded */
      for (i = 0; i < G_N_ELEMENTS (std_fonts); i++)
	{
	  PangoFontDescription *desc = pango_font_description_from_string(std_fonts[i]);

	  /* spits warnings if font cannot be loaded */
	  font = pango_font_map_load_font (fontmap, context, desc);

	  g_object_unref (font);
	}
    }
  else
    {
      PangoFontDescription *desc = NULL;
      GString *s;

      s = g_string_new (argv[1]);
      for (i = 2; i < argc; i++)
	{
	  s = g_string_append_c (s, ' ');
	  s = g_string_append (s, argv[i]);

	  if (0 != atoi (argv[i]))
	    my_font_size = atoi (argv[i]);
	}

      desc = pango_font_description_from_string(s->str);
      family_name = g_strdup (pango_font_description_get_family (desc));

      font = pango_font_map_load_font (fontmap, context, desc);

      coverage = pango_font_get_coverage (font, lang);

      /* ... */

      pango_coverage_unref (coverage);
      pango_font_description_free (desc);
      g_object_unref (font);
    }

  pango_font_map_list_families (fontmap, &families, &nb);

  if (!family_name)
    {
      qsort (families, nb, sizeof (PangoFontFamily*), compare_font_family);
    }
  else
    {
      /* Get on the family faces. No simple way ? */
      for (i = 0; i < nb; i++)
	{
	  if (0 == g_ascii_strcasecmp (pango_font_family_get_name (families[i]), family_name))
	    {
	      pango_font_family_list_faces (families[i], &faces, &nb);
	      /* now nb is the number of faces */
	      break;
	    }
	}
      g_free (families);
      families = NULL;
      g_free (family_name);
      family_name = NULL;
    }

  hdc = pre_render(my_font_size * 64, 3 * my_font_size * nb / 2);

  for (i = 0; i < nb; i++)
    {
      PangoFontDescription *desc;
      const char *f_name;
      PangoWeight weight;
      PangoStyle  style;

      if (families)
	{
	  desc = pango_font_description_new ();

	  f_name =  pango_font_family_get_name (families[i]);
	  pango_font_description_set_family (desc, f_name);
	}
      else
	{
	  desc = pango_font_face_describe (faces[i]);
	  /* this is _not_ the family name from above */
	  f_name = pango_font_description_get_family (desc);
	}
      weight = pango_font_description_get_weight (desc);
      style  = pango_font_description_get_style  (desc);

      g_print ("%s; Style: %d; Weight: %d\n",
	       f_name, style, weight);

      /* give it an arbitray size to load it */
      pango_font_description_set_size (desc, my_font_size * PANGO_SCALE);

      g_get_current_time (&tv0);
      font = pango_font_map_load_font (fontmap, context, desc);
      g_get_current_time (&tv1);
      g_print ("\tpango_font_map_load_font took %.3f sec\n", calc_duration (&tv1, &tv0));

      if (font)
	{
	  PangoItem     *item;
	  PangoGlyphString * glyphs;
	  char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		     "abcdefghijklmnopqrstuvwxyz"
		     "1234567890 -+*/!\xc2\xa7$%&()[]{}<>|#=?@";

	  g_get_current_time (&tv0);
	  coverage = pango_font_get_coverage (font, lang);
	  g_get_current_time (&tv1);
	  g_print ("\tpango_font_get_coverage took %.3f sec\n", calc_duration (&tv1, &tv0));

	  /* ... */
	  pango_context_set_language (context, lang);
	  pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
	  pango_context_set_font_description (context, desc);

	  glyphs = pango_glyph_string_new ();
	  item = pango_item_new ();

	  item->analysis.shape_engine = pango_font_find_shaper (font, lang, s[0]);
	  item->analysis.font = g_object_ref (font);
	  pango_shape ( s, sizeof(s), &(item->analysis), glyphs);

	  if (hdc)
	    {
	      /* the positioning isn't correct */
	      char* name = g_strdup_printf ("%s (%s%s)",
					    f_name,
					    weight == PANGO_WEIGHT_NORMAL ? "n" :
					      (weight == PANGO_WEIGHT_HEAVY ? "h" :
					      (weight > PANGO_WEIGHT_NORMAL ? "b" : "l")),
					    style == PANGO_STYLE_OBLIQUE ? "o" :
					      (style == PANGO_STYLE_ITALIC ? "i" : "n"));

	      TextOut (hdc, 0, line, name, strlen(name));
	      g_get_current_time (&tv0);
	      pango_win32_render (hdc, font, glyphs, 200, line);
	      g_get_current_time (&tv1);
	      g_print ("\tpango_win32_render took %.3f sec\n",
		       calc_duration (&tv1, &tv0));
	      line += (3 * my_font_size / 2);
	      g_free(name);
	    }

	  /* free glyphs, ... */
	  pango_glyph_string_free (glyphs);
	  pango_item_free (item);

	  pango_coverage_unref (coverage);
	  g_object_unref (font);
	}
      pango_font_description_free (desc);
    }

  if (hdc)
    post_render (hdc, "pango-fonts.bmp");

  g_free (families);
  g_free (faces);

  return 0;
}

/*
 * Real Win32 specific render support
 */
static HBITMAP hbmpold = NULL;
static HWND hwndRender = NULL;

static BOOL
SaveBitmap (HBITMAP hBmp, const char* pszFile);

static HDC
pre_render (int width, int height)
{
  HDC hmemdc;
  HDC hdc;
  HBITMAP hbmp;
  RECT r;
  r.top = 0; r.left = 0;
  r.right  = width;
  r.bottom = height;

  hwndRender = CreateWindow ("EDIT",
			     "pango-render-window",
			     WS_DISABLED,
			     0, 0, width, height,
			     GetDesktopWindow(),
			     NULL,
			     GetModuleHandle(NULL),
			     NULL);

  if (hwndRender == NULL)
    fprintf (stderr, "Couldn't create window\n"), exit (1);

  hdc = GetDC(hwndRender);
  hmemdc = CreateCompatibleDC (hdc);
  if (hdc == NULL)
    fprintf (stderr, "CreateCompatibleDC failed\n"), exit (1);

  hbmp = CreateCompatibleBitmap (hdc, width, height);
  if (hbmp == NULL)
    fprintf (stderr, "CreateCompatibleBitmap failed\n"), exit (1);

  hbmpold = SelectObject(hmemdc, hbmp);

  FillRect (hmemdc, &r, GetStockObject(WHITE_BRUSH));
  SetTextColor (hmemdc, RGB (0,0,0));
  SetBkMode (hmemdc, TRANSPARENT);
  return hmemdc;
}

static void
post_render (HDC hdc, const char* sFile)
{
  HBITMAP hbmp = SelectObject(hdc, hbmpold);
  if (sFile)
    SaveBitmap (hbmp, sFile);
  DeleteObject (hbmp);
  DeleteDC (hdc);
  ReleaseDC(hwndRender, GetDC(hwndRender));
  DestroyWindow (hwndRender);
}

static BOOL
SaveBitmap (HBITMAP hBmp, const char* pszFile)
{
  BITMAP bmp;
  PBITMAPINFO pbmi;
  WORD    cClrBits;
  /* Retrieve the bitmap's color format, width, and height. */
  if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
    return FALSE;
  /* Convert the color format to a count of bits. */
  cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
  if (cClrBits == 1)
    cClrBits = 1;
  else if (cClrBits <= 4)
    cClrBits = 4;
  else if (cClrBits <= 8)
    cClrBits = 8;
  else if (cClrBits <= 16)
    cClrBits = 16;
  else if (cClrBits <= 24)
    cClrBits = 24;
  else
    cClrBits = 32;

  /*
   * Allocate memory for the BITMAPINFO structure. (This structure
   * contains a BITMAPINFOHEADER structure and an array of RGBQUAD data
   * structures.)      */
  if (cClrBits < 24)
    pbmi = (PBITMAPINFO) GlobalAlloc(LPTR,
				    sizeof(BITMAPINFOHEADER) +
				    sizeof(RGBQUAD) * (1 << cClrBits));
  /*
   * There is no RGBQUAD array for the 24-bit-per-pixel format.      */
  else
    pbmi = (PBITMAPINFO) GlobalAlloc(LPTR,
				    sizeof(BITMAPINFOHEADER));
  /* Initialize the fields in the BITMAPINFO structure. */
  pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  pbmi->bmiHeader.biWidth = bmp.bmWidth;
  pbmi->bmiHeader.biHeight = bmp.bmHeight;
  pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
  pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
  if (cClrBits < 24)
    pbmi->bmiHeader.biClrUsed = (1 << cClrBits);
  else
    pbmi->bmiHeader.biClrUsed = 0;
  /* If the bitmap is not compressed, set the BI_RGB flag. */
  pbmi->bmiHeader.biCompression = BI_RGB;
  /*
   * Compute the number of bytes in the array of color
   * indices and store the result in biSizeImage.
   */
  pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8
				  * pbmi->bmiHeader.biHeight
				  * cClrBits;
  /*
   * Set biClrImportant to 0, indicating that all of the
   * device colors are important.
   */
  pbmi->bmiHeader.biClrImportant = 0;

  { /* C sucks */
  HANDLE hf;                  /* file handle */
  BITMAPFILEHEADER hdr;       /* bitmap file-header */
  PBITMAPINFOHEADER pbih;     /* bitmap info-header */
  LPBYTE lpBits;              /* memory pointer */
  DWORD dwTotal;              /* total count of bytes */
  DWORD cb;                   /* incremental count of bytes */
  DWORD dwTmp;
  HDC hDC;

  pbih = (PBITMAPINFOHEADER) pbmi;
  lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
  if (!lpBits)
    return FALSE;
  /*
   * Retrieve the color table (RGBQUAD array) and the bits
   * (array of palette indices) from the DIB.
   */
  hDC = CreateCompatibleDC(NULL);
  if (!GetDIBits(hDC, hBmp, 0, (WORD) pbih->biHeight,
		 lpBits, pbmi, DIB_RGB_COLORS))
    return FALSE;
  /* Create the .BMP file. */
  hf = CreateFile (pszFile,
		   GENERIC_READ | GENERIC_WRITE,
		   (DWORD) 0,
		   (LPSECURITY_ATTRIBUTES) NULL,
		   CREATE_ALWAYS,
		   FILE_ATTRIBUTE_NORMAL,
		   (HANDLE) NULL);

  if (hf == INVALID_HANDLE_VALUE)
    return FALSE;
  hdr.bfType = 0x4d42;        /* 0x42 = "B" 0x4d = "M" */
  /* Compute the size of the entire file. */
  hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER)
			+ pbih->biSize + pbih->biClrUsed
			* sizeof(RGBQUAD) + pbih->biSizeImage);
  hdr.bfReserved1 = 0;
  hdr.bfReserved2 = 0;
  /* Compute the offset to the array of color indices. */
  hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER)
			  + pbih->biSize + pbih->biClrUsed
			  * sizeof (RGBQUAD);
  /* Copy the BITMAPFILEHEADER into the .BMP file. */
  if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
		 (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))
    return FALSE;
  /* Copy the BITMAPINFOHEADER and RGBQUAD array into the file. */
  if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
		     + pbih->biClrUsed * sizeof (RGBQUAD),
		     (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))
    return FALSE;
  /* Copy the array of color indices into the .BMP file. */
  dwTotal = cb = pbih->biSizeImage;

  if (!WriteFile(hf, (LPSTR) lpBits, (int) cb,
		 (LPDWORD) &dwTotal, (LPOVERLAPPED) NULL))
      return FALSE;

  /* Close the .BMP file. */
  if (!CloseHandle(hf))
    return FALSE;

  /* Free memory. */
  GlobalFree((HGLOBAL)lpBits);
  GlobalFree((HGLOBAL)pbmi);

  DeleteDC(hDC);
  } /* C sucks */
  return TRUE;
}
