#####################################################################
# -*- coding: iso-8859-1 -*-                                        #
#                                                                   #
# Frets on Fire                                                     #
# Copyright (C) 2006 Sami Kyöstilä                                  #
#                                                                   #
# This program is free software; you can redistribute it and/or     #
# modify it under the terms of the GNU General Public License       #
# as published by the Free Software Foundation; either version 2    #
# of the License, or (at your option) any later version.            #
#                                                                   #
# This program 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 General Public License for more details.                      #
#                                                                   #
# You should have received a copy of the GNU 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.                                              #
#####################################################################

import pygame
from OpenGL.GL import *
import sys

from Texture import Texture

class Font:
  """A texture-mapped font."""
  def __init__(self, fileName, size, bold = False, italic = False, underline = False, outline = True,
               scale = 1.0, reversed = False, systemFont = False):
    pygame.font.init()
    self.size           = size
    self.scale          = scale
    self.glyphCache     = {}
    self.glyphSizeCache = {}
    self.outline        = outline
    self.reversed       = reversed
    # Try loading a system font first if one was requested
    self.font           = None
    if systemFont and sys.platform != "win32":
      try:
        self.font       = pygame.font.SysFont(None, size)
      except:
        pass
    if not self.font:
      self.font         = pygame.font.Font(fileName, size)
    self.font.set_bold(bold)
    self.font.set_italic(italic)
    self.font.set_underline(underline)

  def getStringSize(self, s, scale = 0.002):
    """
    Get the dimensions of a string when rendered with this font.

    @param s:       String
    @param scale:   Scale factor
    @return:        (width, height) tuple
    """
    w = 0
    h = 0
    scale *= self.scale
    for ch in s:
      try:
        s = self.glyphSizeCache[ch]
      except:
        s = self.glyphSizeCache[ch] = self.font.size(ch)
      w += s[0]
      h = max(s[1], h)
    return (w * scale, h * scale)

  def getHeight(self):
    """@return: The height of this font"""
    return self.font.get_height() * self.scale

  def getLineSpacing(self):
    """@return: The line spacing of this font"""
    return self.font.get_linesize() * self.scale
    
  def setCustomGlyph(self, character, texture):
    """
    Replace a character with a texture.

    @param character:   Character to replace
    @param texture:     L{Texture} instance
    """
    texture.setFilter(GL_LINEAR, GL_LINEAR)
    texture.setRepeat(GL_CLAMP, GL_CLAMP)
    self.glyphCache[character]     = texture
    s = .75 * self.getHeight() / float(texture.pixelSize[0])
    self.glyphSizeCache[character] = (texture.pixelSize[0] * s, texture.pixelSize[1] * s)

  def render(self, text, pos = (0, 0), direction = (1, 0, 0), scale = 0.002):
    """
    Draw some text.

    @param text:      Text to draw
    @param pos:       Text coordinate tuple (x, y)
    @param direction: Text direction vector (x, y, z)
    @param scale:     Scale factor
    """
    glEnable(GL_TEXTURE_2D)
    glEnableClientState(GL_VERTEX_ARRAY)
    glEnableClientState(GL_TEXTURE_COORD_ARRAY)

    scale *= self.scale
    glPushMatrix()
    glTranslatef(pos[0], pos[1], 0)
    
    if self.reversed:
      text = "".join(reversed(text))

    if self.outline:
      glPushAttrib(GL_CURRENT_BIT)
      glPushMatrix()
      glColor4f(0, 0, 0, .25 * glGetDoublev(GL_CURRENT_COLOR)[3])
      for ch in text:
        g = self.getGlyph(ch)
        w, h = self.getStringSize(ch, scale = scale)
        tw, th = g.size
  
        glVertexPointerf([(0.0, 0.0, 0.0), (w, 0.0, 0.0), (0.0, h, 0.0), (w, h, 0.0)])
        glTexCoordPointerf([(0.0, th), (tw, th), (0.0, 0.0), (tw, 0.0)])
  
        g.bind()
  
        blur = 2 * 0.002
        for offset in [(-.7, -.7), (0, -1), (.7, -.7), (-1, 0), (1, 0), (-.7, .7), (0, 1), (.7, .7)]:
          glPushMatrix()
          glTranslatef( blur * offset[0], blur * offset[1], 0)
          glDrawElementsui(GL_TRIANGLE_STRIP, [0, 1, 2, 3])
          glPopMatrix()

        glTranslatef(w * direction[0],
                     w * direction[1],
                     w * direction[2])

      glPopAttrib()
      glPopMatrix()

    for ch in text:
      g = self.getGlyph(ch)
      w, h = self.getStringSize(ch, scale = scale)
      tw, th = g.size

      glVertexPointerf([(0.0, 0.0, 0.0), (w, 0.0, 0.0), (0.0, h, 0.0), (w, h, 0.0)])
      glTexCoordPointerf([(0.0, th), (tw, th), (0.0, 0.0), (tw, 0.0)])

      g.bind()
      glDrawElementsui(GL_TRIANGLE_STRIP, [0, 1, 2, 3])

      glTranslatef(w * direction[0],
                   w * direction[1],
                   w * direction[2])

    glPopMatrix()

    glDisableClientState(GL_VERTEX_ARRAY)
    glDisableClientState(GL_TEXTURE_COORD_ARRAY)
    glDisable(GL_TEXTURE_2D)

  def getGlyph(self, ch):
    """
    Get the L{Texture} for a given character.

    @param ch:    Character
    @return:      L{Texture} instance
    """
    try:
      return self.glyphCache[ch]
    except KeyError:
      s = self.font.render(ch, True, (255, 255, 255))

      """
      # Draw outlines
      import Image, ImageFilter
      srcImg = Image.fromstring("RGBA", s.get_size(), pygame.image.tostring(s, "RGBA"))
      img    = Image.fromstring("RGBA", s.get_size(), pygame.image.tostring(s, "RGBA"))
      for y in xrange(img.size[1]):
        for x in xrange(img.size[0]):
          a = 0
          ns = 3
          n = 0
          for ny in range(max(0, y - ns), min(img.size[1], y + ns)):
            for nx in range(max(0, x - ns), min(img.size[0], x + ns)):
              a += srcImg.getpixel((nx, ny))[3]
              n += 1
          if a and srcImg.getpixel((x, y))[3] == 0:
            img.putpixel((x, y), (0, 0, 0, a / n))
      s = pygame.image.fromstring(img.tostring(), s.get_size(), "RGBA")
      """
      
      t = Texture()
      t.setFilter(GL_LINEAR, GL_LINEAR)
      t.setRepeat(GL_CLAMP, GL_CLAMP)
      t.loadSurface(s, alphaChannel = True)
      del s
      self.glyphCache[ch] = t
      return t
