"""
StegaVorto
(C) Copyright 2008 James Paige & Hamster Republic Productions

StegaVorto 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.

StegaVorto is distributed in the hope that it will be fun,
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 StegaVorto in the file LICENSE.txt ;
if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

This is a game that was given to James by an apparition in a dream.
The application of intellectual property law to supernatural entities
who reside in the dream word is uncertain. if in doubt consult a
combination Lawyer-Shaman.
"""

import os
from copy import copy
from random import choice
from random import randint
import pygame
from pygame.locals import *
import pygame.mixer
import urllib
import re
from StringIO import StringIO
import tempfile
import bz2
from threading import Thread
from ConfigParser import RawConfigParser

#-----------------------------------------------------------------------

class StegaVortoURLopener(urllib.FancyURLopener):
  # Spoof a plausible User-Agent
  version = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4"
urllib._urlopener = StegaVortoURLopener()

#-----------------------------------------------------------------------

class LetterSprite(pygame.sprite.Sprite):

  def __init__(self, groups, text, pos, font, color=[0,0,0], bgcolor=[245,245,245]):
    pygame.sprite.Sprite.__init__(self, groups)
    self.font = font
    self.text = text
    self.color = color
    self.bgcolor = bgcolor
    self.focus_color = [200,200,250]
    self.focus = False
    self.render()
    self.rect.topleft = pos
    self.enabled = True
    self.found = False
    self.prev = None
    self.next = None

  def render(self):
    txt_image = self.font.render(self.text, True, self.color)
    r = txt_image.get_rect()
    self.image = pygame.Surface(r.size)
    bgcolor = self.bgcolor
    if self.focus:
      bgcolor = self.focus_color
    self.image.fill(bgcolor)
    self.image.blit(txt_image, [0,0])
    try:
      self.rect.size = self.image.get_rect().size
    except AttributeError:
      self.rect = self.image.get_rect()

  def update(self):
    pos = pygame.mouse.get_pos()
    r = self.rect
    if self.enabled and r.collidepoint(pos):
      # Mouse is touching this letter
      self.focus = True
      self.render()
    else:
      # Mouse is not touching this letter
      if self.focus == True:
        self.focus = False
        self.render()
    if not self.enabled:
      if randint(0, 9) >= 7:
        self.rect.x += randint(-1,1)
      if randint(0, 9) >= 7:
        self.rect.y += randint(-1,1)

  def draw(self, surface):
    if self.refresh:
      self.refresh = False
      self.render()
    pygame.sprite.Sprite.draw(self, surface)

  def disable(self, letter, first=False):
    if not self.enabled:
      return
    self.enabled = False
    if first:
      self.found = True
      self.bgcolor = [240,240,100]
    elif self.text.lower() == letter:
      self.bgcolor = [240,200,200]
      self.color = [200,0,0]
    else:
      self.bgcolor = [240,240,240]
      self.color = [190,190,190]
    self.render()
    if self.prev != None:
      self.prev.disable(letter)

  def click_seek(self, word):
    letter = word.seeking()
    if self.text.lower() == letter:
      self.disable(letter, first=True)
      word.advance()
      if word.index > 0:
        if self.next != None:
          self.next.click_seek(word)

  def count_back(self, letter, n=1, first=True):
    if self.prev == None:
      return n * 2
    if not first:
      if self.found == True:
        return n
      if self.text.lower() == letter:
        n += 5.0
    result = self.prev.count_back(letter, n+0.01, False)
    if first:
      result /= self.count_ahead()
    return result

  def count_ahead(self, n=1.0, first=True):
    if self.next == None:
      return n
    if not first:
      if self.found:
        return n + 5
    return self.next.count_ahead(n, False)

#-----------------------------------------------------------------------

def random_filename(dir, pattern):
  regex = re.compile(pattern, re.I)
  list = []
  for filename in os.listdir(dir):
    if regex.search(filename) != None:
      list.append(filename)
  return os.path.join(dir, choice(list))

#-----------------------------------------------------------------------

class RandomWords(object):

  def __init__(self, wordfile):
    f = open(wordfile)
    self.all = f.readlines()
    f.close()

  def get(self):
    return choice(self.all).strip()

#-----------------------------------------------------------------------

class SequenceWords(object):

  def __init__(self, wordfile):
    self.repeat = 0
    if re.search("\.bz2$", wordfile):
      self.f = bz2.BZ2File(wordfile, "r")
    else:
      self.f = open(wordfile)

  def get(self):
    s = ""
    f = self.f
    while True:
      char = f.read(1)
      if char == "":
        self.repeat += 1
        f.seek(0)
        break
      if char == " ":
        break
      if char == "\n":
        break
      s += char
    return s.strip()
      

  def __del__(self):
    self.f.close()

#-----------------------------------------------------------------------

class SequenceLines(object):

  def __init__(self, linefile):
    self.f = open(linefile)

  def get(self):
    f = self.f
    line = f.readline()
    if line == "":
      f.seek(0)
    return line.strip()

  def __del__(self):
    self.f.close()

#-----------------------------------------------------------------------

class NullWordlist(object):

  def __init__(self):
    self.text = "Error:no_wordlist_loaded"

  def get(self):
    return self.text

#-----------------------------------------------------------------------

class ImageSearchError(Exception):

  pass

#-----------------------------------------------------------------------

class DownloadThread(Thread):

  # regex1 confirms that a results page was returned, and not a failure page
  __regex1 = re.compile(r'of [0-9,]+ results\. Search for ".*?" took [0-9.]+ seconds\. Click on thumbnail to view image details')
  # regex2 extracts the url of the first search result thumbnail
  __regex2 = re.compile(r'src="(?P<url>http://media\d+\.picsearch\.com/is?.*?)" alt="Click to show')

  def __init__(self, word):
    Thread.__init__(self)
    self.word = word
    self.done = False

  def run(self):
    try:
      if self.word.reward_fail >= 3:
        raise ImageSearchError("gave up on downloads, resort to kittens")
      self.word.reward_sprites.empty()
      self.word.reward_sprite = None
      params = urllib.urlencode({"q":self.word.text})
      url = "http://www.picsearch.com/index.cgi?" + params
      html = self.url_as_string(url)
      if not html:
        raise ImageSearchError("unable to fetch " + url)
      match = self.__regex1.search(html)
      if match == None:
        raise ImageSearchError("picture search unavailable")
      match = self.__regex2.search(html)
      if match == None:
        raise ImageSearchError("no img tag matches")
      url = match.group("url")
      img_file = self.url_to_file(url, self.word.temp)
      if img_file == False:
        raise ImageSearchError("img download failed " + url)
      try:
        img = pygame.image.load(img_file)
      except:
        raise ImageSearchError("downloaded file unopenable " + self.word.temp)
      self.word.reward_fail = 0
    except ImageSearchError, e:
      print e
      self.word.reward_fail += 1
      img_file = random_filename("data", "^yay_kitten_")
      img = pygame.image.load(img_file)
    spr = pygame.sprite.Sprite(self.word.reward_sprites)
    newsize = (int(self.word.screen_rect.width * .85), int(self.word.screen_rect.height * .85))
    spr.image = safescale(img, newsize)
    spr.rect = spr.image.get_rect()
    spr.rect.center = self.word.screen_rect.center
    self.word.reward_sprite = spr
    self.done = True

  def url_to_file(self, url, destfile):
    try:
      urllib.urlretrieve(url, destfile)
    except IOError:
      # If the urlopen fails, just give up
      return False
    return destfile

  def url_as_string(self, url):
    try:
      f = urllib.urlopen(url)
    except IOError:
      # If the urlopen fails, just give up
      return False
    html = f.read()
    f.close()
    return html

#-----------------------------------------------------------------------

def make_bordered_text_sprite(text, font, color, bgcolor, expand, group):
  img = font.render(text, True, color)
  r = img.get_rect()
  bg = pygame.surface.Surface([int(r.width * expand), int(r.height * expand)]).convert_alpha()
  bg.fill(bgcolor)
  alpha_corners(bg)
  r.center = bg.get_rect().center
  bg.blit(img, r)
  r = bg.get_rect()
  spr = pygame.sprite.Sprite(group)
  spr.image = bg
  spr.rect = r
  return spr

def alpha_corners(img):
  dest_r = img.get_rect()
  corner = pygame.image.load(os.path.join("data", "alpha_corner.png")).convert_alpha()
  r = corner.get_rect()
  try:
    img.blit(corner, r, None, BLEND_RGBA_MULT)
    corner = pygame.transform.rotate(corner, 90)
    r.bottomleft = dest_r.bottomleft
    img.blit(corner, r, None, BLEND_RGBA_MULT)
    corner = pygame.transform.rotate(corner, 90)
    r.bottomright = dest_r.bottomright
    img.blit(corner, r, None, BLEND_RGBA_MULT)
    corner = pygame.transform.rotate(corner, 90)
    r.topright = dest_r.topright
    img.blit(corner, r, None, BLEND_RGBA_MULT)
  except NameError:
    # Fail Gracefully on older versions of pygame that don't have BLEND_RGBA_MULT
    pass

def safescale(img, newsize):
  try:
    return pygame.transform.smoothscale(img, newsize)
  except AttributeError:
    return pygame.transform.scale(img, newsize)

def time_text(seconds):
  if seconds < 60:
    return "%d sec" % (seconds)
  if seconds < 3600:
    remainder = seconds % 60
    return "%d min %s" % (seconds / 60, time_text(remainder))
  remainder = seconds % 3600
  return "%d hr %s" % (seconds / 3600, time_text(remainder))

#-----------------------------------------------------------------------

class SeekWord(object):

  def __init__(self, font, small_font, reward_font, screen_rect):
    self.wordlist = NullWordlist()
    self.dl = None
    self.reward_sprites = pygame.sprite.Group()
    self.reward_captions = pygame.sprite.Group()
    temp = tempfile.mkstemp(".jpg")
    os.close(temp[0])
    self.temp = temp[1]
    self.font = font
    self.small_font = small_font
    self.reward_font = reward_font
    self.screen_rect = screen_rect
    self.text = ""
    self.notice = ""
    self.sprite = None
    self.notice_sprite = None
    self.letter_sprite = None
    self.reward_sprite = None
    self.reward_text_sprite = None
    self.rewarding = False
    self.reward_fail = 0
    self.new_word()

  def __del__(self):
    os.remove(self.temp)

  def new_wordlist(self, wordlist):
    self.wordlist = wordlist
    self.new_word()

  def new_word(self):
    self.rewarding = False
    self.text = self.wordlist.get().lower()
    self.index = 0
    self.render()
    self.image_search()

  def image_search(self):
    # render the text for this sprite
    if re.search("^Error:", self.text, re.I):
      return
    self.reward_captions.empty()
    self.reward_sprites.remove(self.reward_text_sprite)
    self.make_reward_text_sprite()
    self.dl = DownloadThread(self)
    self.dl.start()

  def make_reward_text_sprite(self):
    img = self.reward_font.render(self.text + "!", True, [100,250,130])
    r = img.get_rect()
    bg = pygame.surface.Surface(r.size).convert_alpha()
    bg.fill([0,80,0,190])
    alpha_corners(bg)
    bg.blit(img, r)
    spr = pygame.sprite.Sprite(self.reward_captions)
    spr.image = bg
    spr.rect = r
    self.reward_text_sprite = spr
    self.reward_text_sprite.rect.midbottom = self.screen_rect.midbottom

  def seeking(self):
    if self.index >= len(self.text):
      return ""
    return self.text[self.index]

  def render(self):
    #render the notice
    txt_image = self.small_font.render(self.notice, True, [255,0,0])
    spr = pygame.sprite.Sprite()
    spr.image = txt_image
    spr.rect = txt_image.get_rect()
    spr.rect.bottomright = self.screen_rect.bottomright
    self.notice_sprite = spr
    #render the string
    txt_image = self.font.render(self.text, True, [200,50,50])
    spr = pygame.sprite.Sprite()
    spr.image = txt_image
    spr.rect = txt_image.get_rect()
    spr.rect.bottomright = self.notice_sprite.rect.topright
    self.sprite = spr
    #render the current letter
    txt_image = self.font.render(self.seeking(), True, [255,0,0])
    spr = pygame.sprite.Sprite()
    spr.image = txt_image
    spr.rect = txt_image.get_rect()
    self.letter_sprite = spr

  def draw(self, surface):
    if not self.rewarding:
      if self.sprite != None:
        surface.blit(self.sprite.image, self.sprite.rect)
      if self.letter_sprite != None:
        r = copy(self.sprite.rect)
        r.x += self.letter_sprite.rect.width * self.index + randint(-1,1)
        r.y += randint(-2, 2)
        surface.blit(self.letter_sprite.image, r)
      if self.notice_sprite != None:
        surface.blit(self.notice_sprite.image, self.notice_sprite.rect)
    else:
      self.reward_sprites.draw(surface)
      self.reward_captions.draw(surface)

  def advance(self):
    self.index += 1
    if self.index >= len(self.text):
      self.rewarding = True
    else:
      self.render()

#-----------------------------------------------------------------------

class Interval(object):
  
  def __init__(self, how_long):
    self.change(how_long)
    self.temp = []

  def reset(self):
    self.ticks = pygame.time.get_ticks()

  def change(self, how_long):
    self.reset()
    self.how_long = how_long

  def update(self):
    ticks = pygame.time.get_ticks()
    if self.ticks + self.how_long < ticks:
      self.ticks = ticks
      return True
    return False

  def temporary(self, how_long):
    self.temp.append(self.how_long)
    self.change(how_long)

  def restore(self):
    if len(self.temp) > 0:
      how_long = self.temp[-1]
      self.temp = self.temp[:-1]
      self.change(how_long)
      

#-----------------------------------------------------------------------

class SoundEffects(object):

  def __init__(self):
    self.select = pygame.mixer.Sound(os.path.join("data", "sfx_select.ogg"))
    self.cheer = pygame.mixer.Sound(os.path.join("data", "sfx_cheer.ogg"))

#-----------------------------------------------------------------------

class Levels(object):

  def __init__(self, screen_rect, font, small_font):
    self.list = {}
    self.screen_rect = screen_rect
    self.group = pygame.sprite.Group()
    self.highlight_group = pygame.sprite.Group()
    self.text_group = pygame.sprite.Group()
    self.padding = 10
    self.font = font
    self.small_font = small_font

  def __getitem__(self, key):
    try:
      return self.list[key]
    except KeyError:
      for lev in self:
        if lev.name == key:
          return lev

  def add(self, level, order):
    self.list[order] = level

  def render(self):
    r = Rect(0,0,0,0)
    maxwidth = 0
    for lev in self:
      lev.render(self.group)
      r.height = max(r.height, lev.sprite.rect.height)
      if r.width > 0:
        r.width += self.padding
      r.width += lev.sprite.rect.width
      maxwidth = max(maxwidth, lev.sprite.rect.width)
    r.center = self.screen_rect.center
    for i in self.list:
      lev = self.list[i]
      lev.sprite.rect.top = r.top
      lev.sprite.rect.left = r.left + (maxwidth + self.padding) * i
    spr = make_bordered_text_sprite("press ESC to quit", self.small_font, [255,255,255], [0,0,0,64], 1.15, self.text_group)
    spr.rect.bottomright = self.screen_rect.bottomright

  def __iter__(self):
    self.index = 0
    return self

  def next(self):
    try:
      level = self.list[self.index]
    except KeyError:
      raise StopIteration
    self.index += 1
    return level

  def update(self):
    self.highlight_group.empty()
    pos = pygame.mouse.get_pos()
    for lev in self:
      lev.highlighted = False
      if lev.sprite.rect.collidepoint(pos):
        lev.highlighted = True
        spr = pygame.sprite.Sprite(self.highlight_group)
        r = Rect(lev.sprite.rect)
        r.width += 12
        r.height += 12
        img = pygame.surface.Surface(r.size).convert_alpha()
        img.fill([64, 128, 255, 96])
        alpha_corners(img)
        r.center = lev.sprite.rect.center
        spr.image = img
        spr.rect = r

  def draw(self, screen):
    self.group.draw(screen)
    self.highlight_group.draw(screen)
    self.text_group.draw(screen)

#-----------------------------------------------------------------------

class Level(object):

  def __init__(self, name, font):
    self.name = name
    self.font = font
    self.highlighted = None

  def render(self, group):
    spr = pygame.sprite.Sprite(group)
    filename = os.path.join("data", "author_" + self.author + ".jpg")
    img = pygame.image.load(filename).convert_alpha()
    alpha_corners(img)
    r = img.get_rect()
    newsize = [int(r.width * 1.5), int(r.height * 1.5)]
    img = safescale(img, newsize)
    r.size = img.get_size()
    txt = self.font.render(self.name, True, [255,255,255])
    txt_r = txt.get_rect()
    txt_r.midbottom = r.midbottom
    box = pygame.surface.Surface(txt_r.size).convert_alpha()
    box.fill([0, 0, 0, 150])
    img.blit(box, txt_r)
    img.blit(txt, txt_r)
    spr.image = img
    spr.rect = r
    self.sprite = spr

#-----------------------------------------------------------------------

class GameTimer(object):

  def __init__(self):
    self.reset()

  def reset(self):
    self.stored_time = 0
    self.start_ticks = pygame.time.get_ticks()
    self.paused = False

  def get_ticks(self):
    if self.paused:
      return self.stored_time
    now = pygame.time.get_ticks()
    return self.stored_time + (now - self.start_ticks)

  def get(self):
    return self.get_ticks() / 1000.0

  def pause(self):
    if not self.paused:
      self.stored_time = self.get_ticks()
      self.paused = True

  def unpause(self):
    if self.paused:
      self.start_ticks = pygame.time.get_ticks()
      self.paused = False

#-----------------------------------------------------------------------

class StegaVorto(object):

  def __init__(self):
    self.sound = True
    if self.sound:
      pygame.mixer.init()
      try:
        pygame.mixer.music.load(os.path.join("data", "song_my_life_changed.ogg"))
        pygame.mixer.music.play(-1)
      except AttributeError, e:
        print "pygame.mixer.music module failed. Why?", e
    self.fullscreen = FULLSCREEN
    self.reset_screen()
    pygame.display.set_caption('StegaVorto')
    
    self.time = GameTimer()
    self.clock = pygame.time.Clock()
    self.clock.tick()
    self.wait = {}
    self.wait["scroll"] = Interval(45)
    self.wait["add"] = Interval(100)
    self.wait["hurry"] = Interval(2000)
    self.wait["victory"] = Interval(100)
    self.hurrytext = SequenceLines(os.path.join("data", "impatience.txt"))
    
    self.font = pygame.font.Font(os.path.join("data", "FreeMonoBold.ttf"), 32)
    self.big_font = pygame.font.Font(os.path.join("data", "FreeMonoBold.ttf"), 50)
    self.small_font = pygame.font.Font(os.path.join("data", "FreeMonoBold.ttf"), 18)
    self.giant_font = pygame.font.Font(os.path.join("data", "FreeMonoBold.ttf"), 75)
    self.sprites = pygame.sprite.Group()
    self.score_layer = pygame.sprite.Group()
    self.score_floaters = pygame.sprite.Group()
    self.completion_layer = pygame.sprite.Group()
    self.debug_layer = pygame.sprite.Group()
    self.last_char = None
    self.start_intro()
    self.prompting = False
    self.completion = False
    self.word = SeekWord(self.big_font, self.small_font, self.giant_font, self.screen_rect)
    self.load_levels()
    self.full = False
    self.possible = True
    self.reset_score()
    self.sounds = SoundEffects()
    self.victory_sprite = None

  def load_levels(self):
    parser = RawConfigParser()
    parser.read(os.path.join("data", "levels.txt"))
    self.levels = Levels(self.screen_rect, self.font, self.small_font)
    for section_name in parser.sections():
      lev = Level(section_name, self.small_font)
      lev.author = parser.get(section_name, "author")
      lev.story = parser.get(section_name, "story")
      lev.wordlist = parser.get(section_name, "wordlist")
      lev.wordcount = parser.getint(section_name, "wordcount")
      lev.scrolldelay = parser.getint(section_name, "scrolldelay")
      order = parser.getint(section_name, "order")
      self.levels.add(lev, order)
    self.levels.render()

  def start_intro(self):
    self.story = SequenceWords(os.path.join("data", "intro.txt"))
    self.wait["add"].temporary(400)
    self.intro_active = True

  def end_intro(self):
    self.intro_active = False
    self.wait["add"].restore()
    self.prompt_for_level()

  def show_completion_screen(self, caption="VICTORY", victory=True):
    self.completion = True
    if self.last_char:
      self.last_char.disable("")
    grp = self.completion_layer
    grp.empty()
    headercol = [190, 0, 130]
    msgcol = [0, 0, 0]
    bgcol = [0, 0, 0, 85]
    spr = make_bordered_text_sprite(caption, self.giant_font, headercol, bgcol, 1.3, grp)
    spr.rect.midtop = self.screen_rect.midtop
    spr.rect.y += 20
    text = []
    text.append("Score: %d" % (self.score))
    text.append("Playtime: %s" % (time_text(self.time.get())))
    text.append("Points-Per-Minute: %0.1f PPM" % (self.score / (self.time.get() / 60)))
    for t in text:
      lastspr = spr
      spr = make_bordered_text_sprite(t, self.font, msgcol, bgcol, 1.2, grp)
      spr.rect.midtop = lastspr.rect.midbottom
      spr.rect.y += 8
    if victory:
      img = pygame.image.load(os.path.join("data", "victory_hibiscus.png"))
      r = img.get_rect()
      img = safescale(img, [r.width*4, r.height*4])
      spr = pygame.sprite.Sprite()
      spr.image = img
      spr.rect = img.get_rect()
      spr.rect.topright = self.screen_rect.topright
      grp.add(spr)
      self.victory_sprite = spr

  def prompt_for_level(self):
    self.completion = False
    self.prompting = True
    if self.last_char:
      self.last_char.disable("")

  def change_level(self):
    for lev in self.levels:
      if lev.highlighted:
        self.prompting = False
        self.level = lev
        self.wait["scroll"].change(lev.scrolldelay)
        self.new_story()
        return

  def new_story(self):
    file = os.path.join("data", self.level.wordlist + ".txt")
    self.word.new_wordlist(RandomWords(file))
    file = os.path.join("data", "story_" + self.level.story + ".txt.bz2")
    self.story = SequenceWords(file)
    self.reset_score()

  def reset_score(self):
    self.score = 0.0
    self.score_divider = 999
    self.wordcount = 0
    self.render_score()
    self.time.reset()

  def reset_screen(self):
    self.screen = pygame.display.set_mode([640,480], self.fullscreen)
    self.screen_rect = self.screen.get_rect()
    icon = pygame.image.load(os.path.join("data","icon.png"))
    pygame.display.set_icon(icon)

  def add_string(self, s):
    s = self.magic_strings(s)
    for char in s:
      self.add_char(char)

  def add_char(self, char):
    if self.last_char == None:
      pos = [0,int(self.screen_rect.height / 3)]
    else:
      rect = copy(self.last_char.rect)
      pos = [rect.x, rect.y]
      pos[0] += rect.width + 1
      if pos[0] + rect.width > self.screen_rect.width:
        pos[0] = 0
        pos[1] += rect.height + 4
        if pos[1] + rect.height > self.screen_rect.height:
          self.full = True
    spr = LetterSprite(self.sprites, char, pos, self.font)
    if self.last_char != None:
      spr.prev = self.last_char
      spr.prev.next = spr
    self.last_char = spr

  def play(self):
    self.playing = True
    while self.playing:
      self.clock.tick(50)
      self.input()
      self.update()
      self.draw()

  def update(self):
    scroll = 0
    if self.intro_active:
      self.possible = True
    if self.wait["scroll"].update() or not self.possible:
      scroll = 1
      if not self.possible:
        scroll = 5
    if self.word.rewarding or self.prompting or self.completion:
      scroll = choice([0,0,0,0,1])
    self.full = False
    self.possible = False
    seek = self.word.seeking()
    for spr in self.sprites:
      r = spr.rect
      r.y -= scroll
      if r.y + r.height > self.screen_rect.height:
        self.full = True
      if r.y + r.height * 2 < self.screen_rect.top:
        spr.prev = None
        spr.next = None
        self.sprites.remove(spr)
        del(spr)
        continue
      spr.update()
      if spr.enabled:
        if spr.text.lower() == seek:
          self.possible = True 
    if self.intro_active:
      self.possible = True
    if self.prompting:
      self.levels.update()
    if not self.prompting and not self.completion:
      if self.wait["add"].update() or not self.possible:
        if not self.full:
          self.add_string(self.story.get() + " ")
    if self.wait["hurry"].update():
      if self.possible:
        self.word.notice = self.magic_strings(self.hurrytext.get())
      else:
        self.word.notice = "Oh, those pesky letter " + self.word.seeking().upper() + "'s are so tricky sometimes..."
      self.word.render()
    for spr in self.score_floaters:
      r = spr.rect
      r.y += 1
      spr.age += 1
      if spr.age >= 40:
        spr.kill()
    if self.wait["victory"].update():
      if self.victory_sprite:
        r = self.victory_sprite.rect
        r.x += choice([-1, 0 ,1])
        r.y += choice([-1, 0, 1])
    self.update_debug()

  def update_debug(self):
    return # comment this out to enable
    self.debug_layer.empty()
    spr = pygame.sprite.Sprite()
    #text = "scroll delay %d" % (self.wait["scroll"].how_long)
    text = "play time %s" % (time_text(self.time.get()))
    img = self.small_font.render(text, True, [0,0,0])
    spr.image = img
    spr.rect = img.get_rect()
    spr.rect.midbottom = self.screen_rect.midbottom
    spr.rect.y -= 50
    self.debug_layer.add(spr)

  def magic_strings(self, s):
    #-- Insert the letter you are seeking
    s = re.sub("\$\{L\}", self.word.seeking(), s)
    #-- Insert "first" or "next" depending on which letter you seek
    fnext = "next"
    if self.word.index == 0:
      fnext = "first"
    s = re.sub("\$\{FNEXT\}", fnext, s)
    #-- Support the ENDINTRO tag
    if re.search("\$\{ENDINTRO\}", s):
      s = re.sub("\$\{ENDINTRO\}", "", s)
      self.end_intro()
    return s

  def draw(self):
    self.screen.fill([255,255,255])
    #for char in self.string:
    self.sprites.draw(self.screen)
    if not self.intro_active and not self.prompting and not self.completion:
      self.word.draw(self.screen)
      self.score_layer.draw(self.screen)
      self.score_floaters.draw(self.screen)
      self.debug_layer.draw(self.screen)
    if self.prompting:
      self.levels.draw(self.screen)
    if self.completion:
      self.completion_layer.draw(self.screen)
    pygame.display.update()

  def update_score(self):
    amount = (4 + len(self.word.text) * .25) * 50
    amount = amount / self.score_divider
    amount = max(amount, 1)
    self.add_to_score(amount)

  def add_to_score(self, amount):
    self.score += amount
    self.render_score()
    self.create_score_floater(amount)

  def render_score(self):
    self.score_layer.empty()
    # Update score
    spr = make_bordered_text_sprite("%0d" % self.score, self.font, [0,0,100], [100, 100, 250, 180], 1.4, self.score_layer)
    spr.rect.topleft = self.screen_rect.topleft
    # Update word count
    try:
      spr2 = make_bordered_text_sprite("%0d/%0d words" % (self.wordcount, self.level.wordcount), self.small_font, [70, 35, 0], [255, 200, 100, 180], 1.4, self.score_layer)
      spr2.rect.topleft = spr.rect.topright
    except AttributeError:
      #this fails if level is not set, but that is okay, just ignore it
      pass

  def create_score_floater(self, amount):
    spr = make_bordered_text_sprite("%d" % amount, self.small_font, [0, 0, 100], [100, 100, 250, 200], 2.0, self.score_floaters)
    r = spr.rect
    pos = pygame.mouse.get_pos()
    r.midtop = pos
    r.right = min(r.right, self.screen_rect.right)
    r.left = max(r.left, self.screen_rect.left)
    spr.age = 0

  def normal_game_play(self):
    return self.intro_active == False and self.prompting == False and self.completion == False

  def input(self):
    for event in pygame.event.get():
      if event.type == QUIT:
        self.playing = False
      elif event.type == KEYUP:
        if event.key == K_ESCAPE:
          if self.normal_game_play():
            self.time.pause()
            self.show_completion_screen("GAME OVER", False)
          else:
            self.playing = False
        if event.mod & KMOD_CTRL:
          if event.key == K_z:
            for spr in self.sprites:
              spr.rect.y += 30
          if event.key == K_a:
            self.word.advance()
          if event.key == K_q:
            self.new_story()
          if event.key == K_w:
            self.wait["scroll"].how_long -= 1
          if event.key == K_e:
            self.wait["scroll"].how_long += 1
        if event.key == K_RETURN:
          if event.mod & KMOD_ALT:
            self.fullscreen = self.fullscreen ^ FULLSCREEN
            self.reset_screen()
          else:
            self.click()
      elif event.type == MOUSEBUTTONDOWN:
        if event.button == 1:
          self.click()

  def click(self):
    if self.completion:
      self.prompt_for_level()
      return
    if self.prompting:
      self.change_level()
      return
    if self.intro_active:
      self.end_intro()
      return
    if self.word.rewarding:
      if self.wordcount >= self.level.wordcount:
        self.show_completion_screen()
        return
      self.word.new_word()
      self.time.unpause()
    else:
      pos = pygame.mouse.get_pos()
      for spr in self.sprites:
        r = spr.rect
        if spr.enabled and r.collidepoint(pos):
          letter = self.word.seeking()
          spr.click_seek(self.word)
          if spr.found:
            if self.sound:
              self.sounds.select.play()
            self.score_divider = spr.count_back(letter)
            if self.word.rewarding:
              self.time.pause()
              if self.sound:
                self.sounds.cheer.play()
              self.wordcount += 1
            self.update_score()
          break

#-----------------------------------------------------------------------

if __name__ == "__main__":
  pygame.init()
  game = StegaVorto()
  game.play()
  pygame.quit()

