(********************************************************************)
(*                                                                  *)
(*  pixmapfont.s7i  Defines pixmapFontType and the font cache       *)
(*  Copyright (C) 2010 - 2013  Thomas Mertes                        *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


const type: pixmapHashType is hash [char] PRIMITIVE_WINDOW;

const type: activeFont is sub object interface;

const func font:              (in activeFont: actFont).baseFont         is DYNAMIC;
const varfunc font:           (inout activeFont: actFont).baseFont      is DYNAMIC;
const func pixmapHashType:    (in activeFont: actFont).pixmap           is DYNAMIC;
const varfunc pixmapHashType: (inout activeFont: actFont).pixmap        is DYNAMIC;
const func integer:           fontSize (in activeFont: actFont)         is DYNAMIC;
const func integer:           scale (in activeFont: actFont)            is DYNAMIC;
const func color:             foreground (in activeFont: actFont)       is DYNAMIC;
const func color:             background (in activeFont: actFont)       is DYNAMIC;
const func integer:           baseLineDelta (in activeFont: actFont)    is DYNAMIC;
const func integer:           line_delta (in activeFont: actFont)       is DYNAMIC;
const func integer:           column_delta (in activeFont: actFont)     is DYNAMIC;
const func integer:           characterSpacing (in activeFont: actFont) is DYNAMIC;

(**
 *  Type to describe a font, based on pixmap images.
 *  Pixmap images are rectangular off screen windows, which are
 *  supported by the graphic system of the operating system.
 *)
const type: pixmapFontType is new struct
    var pixmapHashType: pixmap is pixmapHashType.value;
    var font: baseFont is emptyFont.value;
    var integer: fontSize is 0;
    var integer: scale is 1;
    var color: foreground is white;
    var color: background is black;
    var integer: baseLineDelta is 0;
    var integer: line_delta is 0;
    var integer: column_delta is 0;
    var integer: characterSpacing is 0;
  end struct;

type_implements_interface(pixmapFontType, activeFont);

const activeFont: (attr activeFont).value is pixmapFontType.value;


const func pixmapFontType: genPixmapFont (in font: aFont,
    in integer: fontSize, in integer: scale,
    in color: foreground, in color: background)                     is DYNAMIC;
const func PRIMITIVE_WINDOW: getFontCharPixmap (in font: aFont,
    inout activeFont: actFont, in char: ch)                         is DYNAMIC;


const func integer: fontSize (in pixmapFontType: actFont)         is return actFont.fontSize;
const func integer: scale (in pixmapFontType: actFont)            is return actFont.scale;
const func color:   foreground (in pixmapFontType: actFont)       is return actFont.foreground;
const func color:   background (in pixmapFontType: actFont)       is return actFont.background;
const func integer: baseLineDelta (in pixmapFontType: actFont)    is return actFont.baseLineDelta ;
const func integer: line_delta (in pixmapFontType: actFont)       is return actFont.line_delta;
const func integer: column_delta (in pixmapFontType: actFont)     is return actFont.column_delta;
const func integer: characterSpacing (in pixmapFontType: actFont) is return actFont.characterSpacing;


#
# Font cache
#


(**
 *  Type used as key for the font cache.
 *)
const type: fontKeyType is new struct
    var font: baseFont is emptyFont.value;
    var integer: fontSize is 0;
    var integer: scale is 1;
    var color: foreground is white;
    var color: background is black;
  end struct;

const func integer: hashCode (in fontKeyType: fontKey) is
  return hashCode(fontKey.baseFont) + hashCode(fontKey.fontSize) + hashCode(fontKey.scale) +
      hashCode(fontKey.foreground) + hashCode(fontKey.background);

const func integer: compare (in fontKeyType: fontKey1, in fontKeyType: fontKey2) is func
  result
    var integer: signumValue is 0;
  begin
    signumValue := compare(fontKey1.baseFont, fontKey2.baseFont);
    if signumValue = 0 then
      signumValue := compare(fontKey1.fontSize, fontKey2.fontSize);
      if signumValue = 0 then
        signumValue := compare(fontKey1.scale, fontKey2.scale);
        if signumValue = 0 then
          signumValue := compare(fontKey1.foreground, fontKey2.foreground);
          if signumValue = 0 then
            signumValue := compare(fontKey1.background, fontKey2.background);
          end if;
        end if;
      end if;
    end if;
  end func;

(**
 *  Type of the font cache.
 *)
const type: fontCacheType is hash [fontKeyType] activeFont;

var fontCacheType: fontCache is fontCacheType.value;


const func pixmapFontType: genPixmapFont (in fontProperties: properties,
    in integer: fontSize, in integer: scale,
    in color: foreground, in color: background) is func
  result
    var pixmapFontType: pixmapFont is pixmapFontType.value;
  begin
    incl(pixmapFont.pixmap, ' ', PRIMITIVE_WINDOW.value);
    pixmapFont.baseFont := properties;
    pixmapFont.fontSize := fontSize;
    pixmapFont.scale := scale;
    pixmapFont.foreground := foreground;
    pixmapFont.background := background;
    pixmapFont.baseLineDelta := baseLineDelta(properties) * scale;
    pixmapFont.line_delta := lineHeight(properties) * scale;
    pixmapFont.column_delta := columnWidth(properties) * scale;
    pixmapFont.characterSpacing := characterSpacing(properties) * scale;
  end func;


(**
 *  Get the specified font, either from the font cache or create it.
 *)
const func activeFont: getFont (in font: baseFont, in integer: fontSize,
    in integer: scale, in color: foreground, in color: background) is func
  result
    var activeFont: selectedFont is activeFont.value;
  local
    var fontKeyType: fontKey is fontKeyType.value;
  begin
    # writeln("getFont(bmpFont, " <& fontSize <& ", " <& scale <& ")");
    fontKey.baseFont := baseFont;
    fontKey.fontSize := fontSize;
    fontKey.scale := scale;
    fontKey.foreground := foreground;
    fontKey.background := background;
    if fontKey in fontCache then
      selectedFont := fontCache[fontKey];
    else
      selectedFont := genPixmapFont(baseFont, fontSize, scale, foreground, background);
      fontCache @:= [fontKey] selectedFont;
    end if;
  end func;


const func PRIMITIVE_WINDOW: getFontCharPixmap (
    inout activeFont: actFont, in char: ch) is
  return getFontCharPixmap(actFont.baseFont, actFont, ch);