#####################################################################
# -*- coding: iso-8859-1 -*-                                        #
#                                                                   #
# Frets on Fire                                                     #
# Copyright (C) 2006 Sami Kyöstilä                                  #
#               2008 rchiav                                         #
#                                                                   #
# 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 math
import colorsys
import os
import shutil

from View import Layer
from Input import KeyListener
from GameTask import GameTask
from Session import MessageHandler
from Language import _
import Dialogs
import Player
import Menu
import Song
import Theme
import Config

class Lobby(Layer, KeyListener, MessageHandler):
  def __init__(self, engine, session, singlePlayer = False, tutorial = False):
    self.engine       = engine
    self.session      = session
    self.isRunning    = False
    self.time         = 0.0
    self.scrolling    = 0
    self.rate         = 0
    self.delay        = 0
    self.scroller     = [0, self.scrollUp, self.scrollDown]
    self.gameStarted  = False
    self.done         = True
    self.active       = False
    self.neckB        = 5.0/6.0
    self.neckT        = 1
    self.blockedItems  = [1]
    self.selectedItems = []
    self.singlePlayer = singlePlayer
    self.tutorial     = tutorial
    self.players      = Config.get("game", "players")
    self.mode1p       = Config.get("game","game_mode")
    self.mode2p       = Config.get("game","multiplayer_mode")
    self.lobbyMode    = Theme.lobbyMode
    sfxVolume = self.engine.config.get("audio", "SFX_volume")
    self.engine.data.selectSound.setVolume(sfxVolume)
    self.engine.data.acceptSound.setVolume(sfxVolume)  #MFH
    self.engine.data.cancelSound.setVolume(sfxVolume)  #MFH
    self.fullView     = self.engine.view.geometry[2:4]
    self.session.broker.addMessageHandler(self)
    self.music        = True
    self.creator      = CreateCharacter(self.engine)
    
    if self.singlePlayer:
      themename  = self.engine.data.themeLabel
      self.theme = self.engine.data.theme
      try:
        self.engine.loadImgDrawing(self, "background", os.path.join("themes", themename, "lobby", "lobby.png"))
      except IOError:
        self.engine.loadImgDrawing(self, "background", os.path.join("themes", themename, "menu", "optionsbg.png"))
      try:
        self.engine.loadImgDrawing(self, "backgroundTop", os.path.join("themes", themename, "lobby", "lobby_top.png"))
      except IOError:
        self.backgroundTop = None
      
      try:
        self.engine.loadImgDrawing(self, "itemSelect", os.path.join("themes", themename, "lobby", "select.png"))
      except IOError:
        self.itemSelect = None
      
      try:
        self.engine.loadImgDrawing(self, "infoImg", os.path.join("themes", themename, "lobby", "information.png"))
      except IOError:
        self.infoImg = None
      
      try:
        self.engine.loadImgDrawing(self, "chooseCharImg", os.path.join("themes", themename, "lobby", "choosechar.png"))
      except IOError:
        self.chooseCharImg = None
      
      if os.path.exists(self.engine.resource.fileName(os.path.join("themes",themename,"avatars","default.png"))):
        self.engine.loadImgDrawing(self, "defaultAvatar", os.path.join("themes", themename, "avatars", "default.png"))
      else:
        self.engine.loadImgDrawing(self, "defaultAvatar", os.path.join("users", "players", "default.png"))
      self.engine.loadImgDrawing(self, "defaultNeck",   os.path.join("necks", self.engine.mainMenu.chosenNeck + ".png"))
      self.engine.loadImgDrawing(self, "randomNeck",    os.path.join("necks", "randomneck.png"))
      self.engine.loadImgDrawing(self, "buttons", os.path.join("themes", themename, "notes.png"))
      
      imgheight = self.buttons.height1()
      imgwidth  = self.buttons.width1()
      hFactor = (70.00/imgheight)*6
      wFactor = 200.00/imgwidth
      self.buttonScale = min(hFactor, wFactor)
      imgheight = self.defaultAvatar.height1()
      imgwidth  = self.defaultAvatar.width1()
      hFactor = 110.00/imgheight
      wFactor = 200.00/imgwidth
      self.defAvScale = min(hFactor, wFactor)
      imgheight = self.defaultNeck.height1()
      imgwidth  = self.defaultNeck.width1()
      hFactor = (68.00/imgheight)*6
      wFactor = 200.00/imgwidth
      self.defNeckScale = min(hFactor, wFactor)
      imgheight = self.randomNeck.height1()
      imgwidth  = self.randomNeck.width1()
      hFactor = (68.00/imgheight)*6
      wFactor = 200.00/imgwidth
      self.randNeckScale = min(hFactor, wFactor)
      
      self.tsChooseChar = _("Choose Your Character")
      self.tsPlayerStr  = _("Player %d")
      self.playerNum  = 0
      self.tsDrumFlip = (_("Regular Drums"), _("Flipped Drums"))
      self.tsAutoKick = (_("Pedal-Using"), _("Footless"))
      self.tsAssist   = (_("Assist Mode Off"), _("Easy Assist"), _("Medium Assist"))
      self.tsTwoChord = (_("Chordmaster"), _("Two Notes Max"))
      self.tsInfo     = _("Information:")
      self.tsList     = [("0","1"), self.tsDrumFlip, self.tsAutoKick, self.tsAssist, self.tsTwoChord]
      
      self.controlDict = Player.controlDict
      self.selected = 0
      self.screenOptions = Theme.lobbySelectLength
      self.pos = (0, self.screenOptions)
      self.getPlayers()
      
    
  def getPlayers(self):
    if self.creator.updatedName:
      default = self.creator.updatedName
      self.creator.updatedName = None
    else:
      default = self.engine.config.get("game","player%d" % self.playerNum)
    self.playerNames = Player.playername
    self.playerPrefs = Player.playerpref
    self.options = [_("Create New Player"), _("Saved Characters")]
    self.options.extend(self.playerNames)
    if self.selected >= len(self.options):
      self.selected = len(self.options) - 1
    self.blockedItems = [1]
    for i in self.selectedItems:
      self.blockedItems.append(self.options.index(i))
    self.avatars = [None for i in self.options]
    self.avatarScale = [None for i in self.options]
    self.necks   = [None for i in self.options]
    self.neckScale = [None for i in self.options]
    self.getStartingSelected(default)

  def getStartingSelected(self, default):
    if self.engine.input.controls.type[self.engine.input.activeGameControls[self.playerNum]] > 1:
      self.yes  = [Player.playerkeys[self.playerNum][Player.DRUM5], Player.playerkeys[self.playerNum][Player.DRUM5A]]
      self.no   = [Player.playerkeys[self.playerNum][Player.DRUM1], Player.playerkeys[self.playerNum][Player.DRUM1A]]
      self.up   = [Player.playerkeys[self.playerNum][Player.DRUM2], Player.playerkeys[self.playerNum][Player.DRUM2A]]
      self.down = [Player.playerkeys[self.playerNum][Player.DRUM3], Player.playerkeys[self.playerNum][Player.DRUM3A]]
      self.conf = [Player.playerkeys[self.playerNum][Player.DRUMBASS], Player.playerkeys[self.playerNum][Player.DRUMBASSA], Player.playerkeys[self.playerNum][Player.START]]
    else:
      self.yes  = [Player.playerkeys[self.playerNum][Player.KEY1], Player.playerkeys[self.playerNum][Player.KEY1A]]
      self.no   = [Player.playerkeys[self.playerNum][Player.KEY2], Player.playerkeys[self.playerNum][Player.KEY2A]]
      self.up   = [Player.playerkeys[self.playerNum][Player.ACTION1]]
      self.down = [Player.playerkeys[self.playerNum][Player.ACTION2]]
      self.conf = [Player.playerkeys[self.playerNum][Player.KEY3], Player.playerkeys[self.playerNum][Player.KEY3A], Player.playerkeys[self.playerNum][Player.START]]
    if len(self.options) > 2:
      for i, option in enumerate(self.options):
        if i < 2: #Just in case someone names their character "Create New Character" or something...
          continue
        if option == default:
          self.selected = i
          break
      else:
        self.selected = 2
    else:
      self.selected = 0
    if self.selected > self.pos[1]:
      self.pos = (self.selected-self.screenOptions, self.selected)
    elif self.selected < self.pos[0]:
      self.pos = (self.selected, self.selected+self.screenOptions)
    self.loadAvatar()
    self.loadNeck()
    if self.selected in self.blockedItems:
      self.scrollDown()
  
  def loadAvatar(self):
    if self.avatars[self.selected] is not None:
      return
    if self.selected > 1:
      try:
        self.engine.loadImgDrawing(self, "avatar", os.path.join("users", "players", self.options[self.selected] + ".png"))
      except IOError:
        self.avatars[self.selected] = "Empty"
        return
    else:
      self.avatars[self.selected] = "Empty"
      return
    self.avatars[self.selected] = self.avatar
    imgheight = self.avatar.height1()
    imgwidth  = self.avatar.width1()
    hFactor = 110.00/imgheight
    wFactor = 200.00/imgwidth
    self.avatarScale[self.selected] = min(hFactor, wFactor)
    self.avatar = None
    
  def loadNeck(self):
    if self.necks[self.selected] is not None:
      return
    if self.selected > 1:
      chosenNeck = str(Player.playerpref[self.selected-2][6])
      if chosenNeck == "randomneck" or chosenNeck == "0" or chosenNeck.lower() == "neck_0":
        self.necks[self.selected] = "random"
        return
      elif chosenNeck == "" or chosenNeck.lower() == "default":
        self.necks[self.selected] = "Default"
        return
      try:
        self.engine.loadImgDrawing(self, "neck", os.path.join("necks",str(chosenNeck+".png")))
      except IOError:
        self.necks[self.selected] = "Default"
        return
    else:
      self.necks[self.selected] = "Default"
      return
    self.necks[self.selected] = self.neck
    imgheight = self.neck.height1()
    imgwidth  = self.neck.width1()
    hFactor = (68.00/imgheight)*6
    wFactor = 200.00/imgwidth
    self.neckScale[self.selected] = min(hFactor, wFactor)
    self.neck = None
    
  def shown(self):
    self.engine.input.addKeyListener(self)
    self.isRunning = True
    if not self.singlePlayer:
      n = self.session.id or 1
      name = Dialogs.getText(self.engine, _("Enter your name:"), _("Player #%d") % n)
      if name:
        self.session.world.createPlayer(name)
      else:
        self.engine.view.popLayer(self)

  def hidden(self):
    self.engine.input.removeKeyListener(self)
    self.session.broker.removeMessageHandler(self)
    if not self.gameStarted:
      self.session.close()
      self.engine.view.pushLayer(self.engine.mainMenu)    #rchiav: use already-existing MainMenu instance

  def handleGameStarted(self, sender):
    self.gameStarted = True
    self.engine.addTask(GameTask(self.engine, self.session), synchronized = False)
    self.engine.view.popLayer(self)

  def keyPressedSP(self, key, unicode):
    c = self.engine.input.controls.getMapping(key)
    i = self.playerNum
    if self.gameStarted:
      return True
    if c in Player.cancels + self.no or key == pygame.K_ESCAPE:
#      if self.playerNum == 0: #akedrou - needs fixing.
      self.engine.data.cancelSound.play()
      self.engine.view.popLayer(self)
#      else:
#        self.playerNum -= 1
#        self.session.world.deletePlayer(self.session.world.players[self.playerNum])
#        self.blockedItems.remove(self.selectedItems.pop())
#        self.engine.data.cancelSound.play()
#        default = self.engine.config.get("game","player%d" % self.playerNum)
#        self.getStartingSelected(default)
      return True
    elif c in self.yes or key == pygame.K_RETURN:
      self.engine.data.acceptSound.play()
      self.scrolling = 0
      if self.selected == 0:
        self.done = False
        self.creator.loadPlayer()
        self.engine.view.pushLayer(self.creator)
      elif self.selected > 1:
        self.session.world.createPlayer(self.options[self.selected])
        self.engine.config.set("game", "player%d" % self.playerNum, self.options[self.selected])
        self.playerNum += 1
        self.blockedItems.append(self.selected)
        self.blockedItems.sort()
        self.selectedItems.append(self.options[self.selected])
        if self.playerNum >= self.players:
          self.gameStarted = True
          self.engine.menuMusic = False
          self.music = False
          self.engine.mainMenu.cutMusic()
          if self.tutorial:
            self.engine.config.set("game", "selected_library", "songs")
            self.session.world.startGame(libraryName = Song.DEFAULT_LIBRARY, songName = "tutorial")
          else:
            self.session.world.startGame()
        else:
          default = self.engine.config.get("game","player%d" % self.playerNum)
          self.getStartingSelected(default)
      return True
    elif (c in self.conf or key in [pygame.K_LCTRL, pygame.K_RCTRL]):
      self.engine.data.acceptSound.play()
      self.creator.loadPlayer(self.options[self.selected])
      self.done = False
      self.engine.view.pushLayer(self.creator)
      return True
    if self.playerNum >= self.players:
      return True
    if c in self.up + [Player.playerkeys[self.playerNum][Player.UP]] or key == pygame.K_UP:
      self.scrolling = 1
      self.scrollUp()
      self.delay = self.engine.scrollDelay
    elif c in self.down + [Player.playerkeys[self.playerNum][Player.DOWN]] or key == pygame.K_DOWN:
      self.scrolling = 2
      self.scrollDown()
      self.delay = self.engine.scrollDelay
    if self.selected > self.pos[1]:
      self.pos = (self.selected - self.screenOptions, self.selected)
    elif self.selected < self.pos[0]:
        self.pos = (self.selected, self.selected+self.screenOptions)
    return True
  
  def scrollUp(self):
    self.engine.data.selectSound.play()
    self.selected -= 1
    if self.selected < 0:
      self.selected = len(self.options) - 1
    while self.selected in self.blockedItems:
      self.selected -= 1
    self.loadAvatar()
    #self.loadNeck()
    if self.selected > self.pos[1]:
      self.pos = (self.selected - self.screenOptions, self.selected)
    elif self.selected < self.pos[0]:
        self.pos = (self.selected, self.selected+self.screenOptions)
    
  def scrollDown(self):
    self.engine.data.selectSound.play()
    self.selected += 1
    while self.selected in self.blockedItems:
      self.selected += 1
    if self.selected >= len(self.options):
      self.selected = 0
    self.loadAvatar()
    #self.loadNeck()
    if self.selected > self.pos[1]:
      self.pos = (self.selected - self.screenOptions, self.selected)
    elif self.selected < self.pos[0]:
        self.pos = (self.selected, self.selected+self.screenOptions)
  
  def keyPressed(self, key, unicode):
    if not self.active:
      return
    if self.singlePlayer:
      return self.keyPressedSP(key, unicode)
    
    c = self.engine.input.controls.getMapping(key)
    if c in Player.cancels + Player.key2s or key == pygame.K_ESCAPE:
      self.engine.view.popLayer(self)
    elif (c in [Player.key1s] or key == pygame.K_RETURN) and self.canStartGame():
      self.gameStarted = True
      self.session.world.startGame()      
    return True

  def keyReleased(self, key):
    self.scrolling = 0

  def run(self, ticks):
    self.time += ticks / 50.0
    self.neckT -= ticks/2000.0
    if self.neckT < 0.0:
      self.neckT = 1
    self.neckB = self.neckT - (1.0/6.0)
    if self.scrolling > 0:
      self.delay -= ticks
      self.rate += ticks
      if self.delay <= 0 and self.rate >= self.engine.scrollRate:
        self.rate = 0
        self.scroller[self.scrolling]()
    if self.music:
      self.engine.mainMenu.runMusic()

  def canStartGame(self):
    return len(self.session.world.players) >= self.players and self.session.isPrimary() and not self.gameStarted
  
  def drawNeck(self, w, h, i):
    twoNeck = False
    if self.neckB < 0:
      neckB = 1 - (self.neckB * -1)
      neckT = 1
      self.neckB = 0
      twoNeck = True
    if self.necks[i] == "Default" or self.necks[i] == None:
      neck = self.defaultNeck
      scale = self.defNeckScale
    elif self.necks[i] == "random":
      neck = self.randomNeck
      scale = self.randNeckScale
    else:
      neck = self.necks[i]
      scale = self.neckScale[i]
    hn = neck.height1()*scale*(self.neckT-self.neckB)
    self.engine.drawImage(neck, scale = (scale, scale*(self.neckT-self.neckB)), coord = (w*.7, h*.6-(hn/2)), rect = (0, 1, self.neckB, self.neckT))
    if twoNeck:
      h2 = neck.height1()*scale*(neckT-neckB)
      self.engine.drawImage(neck, scale = (scale, scale*(neckT-neckB)), coord = (w*.7, (h*.6)-hn-(h2/2)), rect = (0, 1, neckB, neckT))
  
  def renderLocalLobby(self, visibility, topMost):
    if self.playerNum >= self.players:
      return
    self.engine.view.setOrthogonalProjection(normalize = True)
    try:
      font = self.engine.data.fontDict[Theme.lobbySelectFont]
      titleFont = self.engine.data.fontDict[Theme.lobbyTitleFont]
    except KeyError:
      font = self.engine.data.font
      titleFont = self.engine.data.loadingFont
    v = ((1 - visibility) **2)
    w, h = self.fullView
    try:
      if self.background:
        wFactor = 640.000/self.background.width1()
        self.engine.drawImage(self.background, scale = (wFactor,-wFactor), coord = (w/2,h/2))
      r, g, b = Theme.lobbyTitleColor
      glColor3f(r, g, b)
      if self.chooseCharImg:
        self.engine.drawImage(self.chooseCharImg, scale = (Theme.lobbyTitleScale,-Theme.lobbyTitleScale), coord = (w*Theme.lobbyTitleX,h*Theme.lobbyTitleY))
      else:
        wText, hText = font.getStringSize(self.tsChooseChar, scale = Theme.lobbyTitleScale)
        titleFont.render(self.tsChooseChar, (Theme.lobbyTitleX-(wText),Theme.lobbyTitleY), scale = Theme.lobbyTitleScale)
      r, g, b = Theme.lobbyPlayerColor
      glColor3f(r, g, b)
      wText, hText = titleFont.getStringSize(self.tsPlayerStr % (self.playerNum+1), scale = Theme.lobbyTitleScale)
      titleFont.render(self.tsPlayerStr % (self.playerNum+1), (Theme.lobbyTitleCharacterX-wText/2, Theme.lobbyTitleCharacterY), scale = Theme.lobbyTitleScale)
      for i, name in enumerate(self.options):
        if i < self.pos[0] or i > self.pos[1]:
          continue
        if i == self.selected:
          if i > 1:
            #self.drawNeck(w, h, i)
            j = i-2 #player corresponding
            lefty = 1
            if self.playerPrefs[j][0] == 1:
              lefty = -1
            self.engine.drawImage(self.buttons, scale = (self.buttonScale*lefty, -self.buttonScale*(1.0/6.0)), coord = (w*Theme.lobbyPreviewX,h*(Theme.lobbyPreviewY+.45)), rect = (0, 1, 0, (1.0/6.0)))
            if self.lobbyMode == 1:
              avatarCoord = (w*Theme.lobbyAvatarX,h*Theme.lobbyAvatarY)
              avatarScale = Theme.lobbyAvatarScale
            else:
              avatarCoord = (w*Theme.lobbyPreviewX,h*(Theme.lobbyPreviewY+.75))
              avatarScale = 1
            if self.avatars[i] == "Empty" or self.avatars[i] == None:
              self.engine.drawImage(self.defaultAvatar, scale = (self.defAvScale*avatarScale,-self.defAvScale*avatarScale), coord = avatarCoord)
            else:
              self.engine.drawImage(self.avatars[i], scale = (self.avatarScale[i]*avatarScale,-self.avatarScale[i]*avatarScale), coord = avatarCoord)
            if self.infoImg:
              self.engine.drawImage(self.infoImg, scale = (.5,-.5), coord = (w*Theme.lobbyPreviewX,h*(Theme.lobbyPreviewY+.55)))
            else:
              wText, hText = titleFont.getStringSize(self.tsInfo, scale = .0025)
              titleFont.render(self.tsInfo, (Theme.lobbyPreviewX-wText/2, ((.45-Theme.lobbyPreviewY)*self.engine.data.fontScreenBottom)-hText/2), scale = .0025)
            r, g, b = Theme.lobbyInfoColor
            glColor3f(r, g, b)
            for k in range(1,5):
              text = self.tsList[k][self.playerPrefs[j][k]]
              wText, hText = font.getStringSize(text, scale = .0018)
              font.render(text, (Theme.lobbyPreviewX-wText/2,.4-(Theme.lobbyPreviewY*self.engine.data.fontScreenBottom)+(Theme.lobbyPreviewSpacing*k)), scale = .0018)
          if self.itemSelect:
            self.engine.drawImage(self.itemSelect, scale = (.5,-.5), coord = (w*Theme.lobbySelectImageX,h*(1-(Theme.lobbySelectImageY+Theme.lobbySelectSpace*(i-self.pos[0]))/self.engine.data.fontScreenBottom)))
          else:
            r, g, b = Theme.lobbySelectColor
            glColor3f(r, g, b)
        else:
          if i in self.blockedItems and i != 1:
            r, g, b = Theme.lobbyDisableColor
            glColor3f(r, g, b)
          else:
            r, g, b = Theme.lobbyFontColor
            glColor3f(r, g, b)
        if i == 1:
          wText, hText = titleFont.getStringSize(name, scale = Theme.lobbySelectScale)
          titleFont.render(name, (Theme.lobbySelectX-wText, Theme.lobbySelectY + (Theme.lobbySelectSpace*(i-self.pos[0]))), scale = Theme.lobbySelectScale)
        else:
          wText, hText = font.getStringSize(name, scale = Theme.lobbySelectScale)
          font.render(name, (Theme.lobbySelectX-wText, Theme.lobbySelectY + (Theme.lobbySelectSpace*(i-self.pos[0]))), scale = Theme.lobbySelectScale)
      if self.backgroundTop:
        wFactor = 640.000/self.backgroundTop.width1()
        self.engine.drawImage(self.backgroundTop, scale = (wFactor,-wFactor), coord = (w/2,h/2))
    finally:
      self.engine.view.resetProjection()
  
  def render(self, visibility, topMost):
    if not visibility:
      self.active = False
      return
    if not self.active:
      self.getPlayers()
    self.active = True
    self.done   = True
    if self.singlePlayer:
      self.renderLocalLobby(visibility, topMost)
      return
    
    self.engine.view.setOrthogonalProjection(normalize = True)
    font = self.engine.data.font

    try:
      v = 1.0 - ((1 - visibility) ** 2)
      
      glEnable(GL_BLEND)
      glBlendFunc(GL_SRC_ALPHA, GL_ONE)
      glEnable(GL_COLOR_MATERIAL)

      text = _("Lobby (%d players)") % len(self.session.world.players)
      w, h = font.getStringSize(text)

      x = .5 - w / 2
      d = 0.0
      c = 1 - .25 * v

      y = .1 - (1.0 - v) * .2
        
      for i, ch in enumerate(text):
        w, h = font.getStringSize(ch)
        c = i * .05
        glColor3f(*colorsys.hsv_to_rgb(.75, c, 1))
        glPushMatrix()
        s = .25 * (math.sin(i / 2 + self.time / 4) + 2)
        glTranslate(-s * w / 2, -s * h / 2, 0)
        font.render(ch, (x, y), scale = 0.002 * s)
        glPopMatrix()
        x += w

      x = .1
      y = .2 + (1 - v) / 4
      glColor4f(1, 1, 1, v)
      
      for player in self.session.world.players:
        font.render(player.name, (x, y))
        y += .08

      if self.canStartGame():
        s = _("Press Enter to Start Game")
        sz = 0.0013
        w, h = font.getStringSize(s, scale = sz)
        font.render(s, (.5 - w / 2, .65), scale = sz)
        
    finally:
      self.engine.view.resetProjection()

class CreateCharacter(Layer, KeyListener):
  def __init__(self, engine):
    self.engine    = engine
    self.time      = 0.0
    self.blink     = 0
    self.cursor    = ""
    self.name      = ""
    self.active    = False
    self.oldValue  = None
    self.oldName   = None
    self._cache    = None
    self.selected  = 0
    self.choice    = 0
    self.scrolling = 0
    self.scrollRate  = self.engine.scrollRate
    self.scrollDelay = self.engine.scrollDelay
    self.delay       = 0
    self.rate        = 0
    sfxVolume = self.engine.config.get("audio", "SFX_volume")
    self.engine.data.selectSound.setVolume(sfxVolume)
    self.engine.data.acceptSound.setVolume(sfxVolume)  #MFH
    self.engine.data.cancelSound.setVolume(sfxVolume)  #MFH
    self.fullView  = self.engine.view.geometry[2:4]
    self.invalidNames = []
    for i in Player.playername:
      self.invalidNames.append(i.lower())
    self.newChar   = True
    self.choices   = []
    self.avatar    = None
    self.player    = None
    self.neck      = None
    self.updatedName = None
    self.loadPlayer()
    self.dictEnDisable = {0: _("Disabled"), 1: _("Enabled")}
    self.lefty     = {0: 1, 1: -1}
    neckDict       = {0: _("Default Neck"), 1: _("Theme Neck"), 2: _("Select a Neck")}
    self.values    = (self.dictEnDisable, self.dictEnDisable, self.dictEnDisable, {0: _("Disabled"), 1: _("Easy Assist"), 2: _("Medium Assist")}, self.dictEnDisable, neckDict)
    self.options   = [(_("Name"),             _("Name your character!")), \
                      (_("Lefty Mode"),       _("Flip the guitar frets for left-handed playing!")), \
                      (_("Drum Flip"),        _("Flip the drum sounds - red hits become green, and so on")), \
                      (_("Auto-Kick Bass"),   _("Feet uncooperative? Broke your pedal? Not to worry!")), \
                      (_("Assist Mode"),      _("Play hard and expert, even when you're not that good!")), \
                      (_("Two-Chord Max"),    _("Cut those jumbo chords down to a more manageable size!")), \
                      (_("Neck"),             _("Set a custom neck image just for you!")), \
                      (_("Upload Name"),      _("'Harry Potter' is all well and good, but...")), \
                      (_("Choose Avatar"),    _("Sometimes I wonder what you look like.")), \
                      (_("Delete Character"), _("Come on... I didn't mean it. Really, I promise!")), \
                      (_("Done"),             _("All finished? Let's do this thing!"))]
    themename = self.engine.data.themeLabel
    try:
      self.engine.loadImgDrawing(self, "background", os.path.join("themes", themename, "lobby", "creator.png"))
    except IOError:
      self.background = None
    try:
      self.engine.loadImgDrawing(self, "backgroundTop", os.path.join("themes", themename, "lobby", "creator_top.png"))
    except IOError:
      self.backgroundTop = None
  def loadPlayer(self, player = None):
    self.choices = []
    if player is not None:
      try:
        pref = self.cache.execute('SELECT * FROM `players` WHERE `name` = ?', [player]).fetchone()
        pref = [pref[0], pref[1], pref[2], pref[3], pref[4], pref[5], pref[6], pref[10]]
        self.neck = pref[7]
        self.newChar = False
        self.player = player
        self.oldName = pref[0]
      except: #not found
        pref = ['', 0, 0, 0, 0, 0, 0, '']
        self.neck = ''
        self.newChar = True
        self.player = None
    else:
      pref = ['', 0, 0, 0, 0, 0, 0, '']
      self.neck = ''
      self.newChar = True
      self.player = None
    for i in pref:
      self.choices.append(i)
    self.choices.extend(["", "", ""])
  def deleteCharacter(self):
    tsYes = _("Yes")
    q = Dialogs.chooseItem(self.engine, [tsYes, _("No")], _("Are you sure you want to delete this player?"))
    if q == tsYes:
      if self.player:
        Player.deletePlayer(self.player)
      self.engine.view.popLayer(self)
      self.engine.input.removeKeyListener(self)
  def saveCharacter(self):
    pref = self.choices[0:8]
    pref.insert(7, self.neck)
    if len(self.choices[0]) > 0:
      if self.choices[0].lower() == "default":
        Dialogs.showMessage(self.engine, _("That is a terrible name. Choose something not 'default'"))
      elif self.choices[0].lower() not in self.invalidNames or self.choices[0] == self.player:
        Player.updatePlayer(self.player, pref)
        self.updatedName  = self.choices[0]
        if self.avatar is not None:
          shutil.copy(self.engine.resource.fileName(self.avatar),os.path.join(self.engine.data.path,"users","players",self.choices[0]+".png"))
        if self.oldName:
          if os.path.exists(self.engine.resource.fileName(os.path.join("users","players",self.oldName+".png"))) and self.oldName != self.choices[0]:
            if self.avatar is None:
              os.rename(self.engine.resource.fileName(os.path.join("users","players",self.oldName+".png")), os.path.join(self.engine.data.path,"users","players",self.choices[0]+".png"))
            else:
              os.remove(self.engine.resource.fileName(os.path.join("users","players",self.oldName+".png")))
        self.engine.view.popLayer(self)
        self.engine.input.removeKeyListener(self)
      else:
        Dialogs.showMessage(self.engine, _("That name already exists!"))
    else:
      Dialogs.showMessage(self.engine, _("Please enter a name!"))
  def getCache(self):
    if not self._cache:
      self._cache = Player.playerCacheManager.getCache()
    return self._cache
  cache = property(getCache) #to allow deleting.
  def shown(self):
    self.engine.input.addKeyListener(self)
  def hidden(self):
    self.engine.input.removeKeyListener(self)
  def keyPressed(self, key, unicode):
    c = self.engine.input.controls.getMapping(key)
    if key == pygame.K_BACKSPACE and self.active:
      self.choices[self.selected] = self.choices[self.selected][:-1]
    elif unicode and ord(unicode) > 31 and self.active:
      if self.selected == 0 or self.selected == 7:
        if self.selected == 0 and (ord(unicode) in (34, 42, 47, 58, 60, 62, 63, 92, 124) or ord(unicode) > 126): #ascii only
          self.engine.data.cancelSound.play()
          return True
        if len(self.choices[self.selected]) > 24:
          self.choices[self.selected] = self.choices[self.selected][:-1]
        self.choices[self.selected] += unicode
        return
    if c in Player.key1s or key == pygame.K_RETURN:
      self.scrolling = 0
      self.engine.data.acceptSound.play()
      if self.selected in (0, 7):
        if self.active:
          self.active = False
        else:
          self.blink  = 0
          self.active = True
          self.oldValue = self.choices[self.selected]
      elif self.selected == 6:
        if self.choices[6] == 2:
          self.engine.view.pushLayer(Dialogs.NeckChooser(self.engine, player = self.player, owner = self))
          self.keyActive = False
      elif self.selected == 8:
        self.avatar = Dialogs.chooseAvatar(self.engine)
      elif self.selected == 9:
        self.deleteCharacter()
      elif self.selected == 10:
        self.saveCharacter()
    elif c in Player.key2s + Player.cancels or key == pygame.K_ESCAPE:
      self.engine.data.cancelSound.play()
      if not self.active:
        if self.player:
          self.updatedName  = self.oldName
        self.engine.view.popLayer(self)
        self.engine.input.removeKeyListener(self)
      else:
        self.choices[self.selected] = self.oldValue
        self.active = False
    elif (c in Player.action1s + Player.ups or key == pygame.K_UP):
      self.scrolling = 1
      self.delay = self.scrollDelay
      self.scrollUp()
    elif (c in Player.action2s + Player.downs or key == pygame.K_DOWN):
      self.scrolling = 2
      self.delay = self.scrollDelay
      self.scrollDown()
    elif (c in Player.key3s + Player.rights or key == pygame.K_RIGHT) and self.active:
      if len(self.choices[self.selected]) > 0:
        self.choices[self.selected] += self.choices[self.selected][len(self.choices[self.selected]) - 1]
        if c in Player.key3s:
          self.engine.data.acceptSound.play()
    elif (c in Player.key4s + Player.lefts or key == pygame.K_LEFT) and self.active:
      self.choices[self.selected] = self.choices[self.selected][:-1]
      if c in Player.key4s:
        self.engine.data.cancelSound.play()
    elif c in Player.rights or key == pygame.K_RIGHT:
      if self.selected in (0, 7, 8, 9, 10):
        pass
      elif self.selected in [4, 6]:
        self.choices[self.selected]+=1
        if self.choices[self.selected] > 2:
          self.choices[self.selected] = 0
      else:
        self.choices[self.selected] = 1 and (self.choices[self.selected] == 0) or 0
    elif c in Player.lefts or key == pygame.K_LEFT:
      if self.selected in (0, 7, 8, 9, 10):
        pass
      elif self.selected in [4, 6]:
        self.choices[self.selected]-=1
        if self.choices[self.selected] < 0:
          self.choices[self.selected] = 2
      else:
        self.choices[self.selected] = 1 and (self.choices[self.selected] == 0) or 0
  def scrollUp(self):
    if self.active:
      if len(self.choices[self.selected]) == 0:
        self.choices[self.selected] = "A"
        return True
      letter = self.choices[self.selected][len(self.choices[self.selected])-1]
      letterNum = ord(letter)
      if letterNum == ord('A'):
        letterNum = ord(' ')
      elif letterNum == ord(' '):
        letterNum = ord('_')
      elif letterNum == ord('_'):
        letterNum = ord('-')
      elif letterNum == ord('-'):
        letterNum = ord('9')
      elif letterNum == ord('0'):
        letterNum = ord('z')
      elif letterNum == ord('a'):
        letterNum = ord('Z')        
      else:
        letterNum -= 1
      self.choices[self.selected] = self.choices[self.selected][:-1] + chr(letterNum)
      self.engine.data.selectSound.play()
    else:
      self.engine.data.selectSound.play()
      self.selected -= 1
      if self.selected < 0:
        self.selected = len(self.options) - 1
  def scrollDown(self):
    if self.active:
      if len(self.choices[self.selected]) == 0:
        self.choices[self.selected] = "A"
        return True
      letter = self.choices[self.selected][len(self.choices[self.selected])-1]
      letterNum = ord(letter)
      if letterNum == ord('Z'):
        letterNum = ord('a')
      elif letterNum == ord('z'):
        letterNum = ord('0')
      elif letterNum == ord('9'):
        letterNum = ord('-')
      elif letterNum == ord('-'):
        letterNum = ord('_')
      elif letterNum == ord('_'):
        letterNum = ord(' ')
      elif letterNum == ord(' '):
        letterNum = ord('A')
      else:
        letterNum += 1
      self.choices[self.selected] = self.choices[self.selected][:-1] + chr(letterNum)
      self.engine.data.selectSound.play()
    else:
      self.engine.data.selectSound.play()
      self.selected += 1
      if self.selected >= len(self.options):
        self.selected = 0
  def keyReleased(self, key):
    self.scrolling = 0
  def run(self, ticks):
    self.time += ticks/50.0
    if self.scrolling > 0:
      self.delay -= ticks
      self.rate += ticks
    self.blink = self.time%20
    if self.active and self.blink > 10:
      self.cursor = "|"
    else:
      self.cursor = ""
    if self.scrolling == 1 and self.delay <= 0 and self.rate >= self.scrollRate:
      self.rate = 0
      self.scrollUp()
    elif self.scrolling == 2 and self.delay <= 0 and self.rate >= self.scrollRate:
      self.rate = 0
      self.scrollDown()
  def render(self, visibility, topMost):
    try:
      font = self.engine.data.fontDict[Theme.characterCreateOptionFont]
      helpFont = self.engine.data.fontDict[Theme.characterCreateHelpFont]
    except KeyError:
      font = self.engine.data.font
      helpFont = self.engine.data.loadingFont
    self.engine.view.setOrthogonalProjection(normalize = True)
    v = ((1 - visibility) **2)
    w, h = self.fullView
    try:
      if self.background:
        wFactor = 640.000/self.background.width1()
        self.engine.drawImage(self.background, scale = (wFactor,-wFactor), coord = (w/2,h/2))
      for i, option in enumerate(self.options):
        r, g, b = Theme.characterCreateHelpColor
        glColor3f(r, g, b)
        cursor = ""
        if self.selected == i:
          wText, hText = helpFont.getStringSize(option[1], scale = Theme.characterCreateScale)
          helpFont.render(option[1], (Theme.characterCreateHelpX-(wText/2), Theme.characterCreateHelpY-hText), scale = Theme.characterCreateHelpScale)
          r, g, b = Theme.characterCreateSelectColor
          glColor3f(r, g, b)
          cursor = self.cursor
        else:
          r, g, b = Theme.characterCreateFontColor
          glColor3f(r, g, b)
        wText, hText = font.getStringSize(option[0], scale = Theme.characterCreateScale)
        font.render(option[0], (Theme.characterCreateX, Theme.characterCreateY+Theme.characterCreateSpace*i), scale = Theme.characterCreateScale)
        if self.active and self.selected == i:
          Theme.setSelectedColor(1-v)
        if i == 0 or i > 6:
          wText, hText = font.getStringSize(self.choices[i], scale = Theme.characterCreateScale)
          font.render(self.choices[i]+cursor, (Theme.characterCreateOptionX-wText, Theme.characterCreateY+Theme.characterCreateSpace*i), scale = Theme.characterCreateScale)
        else:
          if i == self.selected:
            str = "< %s >" % self.values[i-1][self.choices[i]]
          else:
            str = self.values[i-1][self.choices[i]]
          wText, hText = font.getStringSize(str, scale = Theme.characterCreateScale)
          font.render(str, (Theme.characterCreateOptionX-wText, Theme.characterCreateY+Theme.characterCreateSpace*i), scale = Theme.characterCreateScale)
      if self.backgroundTop:
        wFactor = 640.000/self.backgroundTop.width1()
        self.engine.drawImage(self.backgroundTop, scale = (wFactor,-wFactor), coord = (w/2,h/2))
    finally:
      self.engine.view.resetProjection()
