#####################################################################
# -*- coding: iso-8859-1 -*-                                        #
#                                                                   #
# Frets on Fire                                                     #
# Copyright (C) 2006 Sami Kyöstilä                                  #
#               2008 myfingershurt                                  #
#               2008 evilynux <evilynux@gmail.com>                  #
#                                                                   #
# 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 os

from View import Layer
from Input import KeyListener
import Data
import Theme
import Dialogs
import Player

import Log

class Choice:
  def __init__(self, text, callback, name = None, values = None, valueIndex = 0, append_submenu_char = True, tipText = None):
    #Log.debug("Choice class init (Menu.py)...")
    self.text       = unicode(text)
    self.callback   = callback
    self.name       = name
    self.values     = values
    self.valueIndex = valueIndex
    self.append_submenu_char = append_submenu_char
    self.tipText    = tipText

    if self.text.endswith(" >"):
      self.text = text[:-2]
      self.isSubMenu = True
    else:
      self.isSubMenu = isinstance(self.callback, Menu) or isinstance(self.callback, list)
    
  #MFH - add support for passing position values to the callback "next menu"
  def trigger(self, engine = None):
    if engine and isinstance(self.callback, list):
      #MFH 
      if self.values:
        nextMenu = Menu(engine, self.callback, name = self.name, pos = self.values, selectedIndex = self.valueIndex )
      else:
        nextMenu = Menu(engine, self.callback, name = self.name)
    elif engine and isinstance(self.callback, Menu):
      nextMenu = self.callback
    elif self.values:
      nextMenu = self.callback(self.values[self.valueIndex])
    else:
      nextMenu = self.callback()
    if isinstance(nextMenu, Menu):
      engine.view.pushLayer(nextMenu)
      
  def selectNextValue(self):
    if self.values:
      self.valueIndex = (self.valueIndex + 1) % len(self.values)
      self.trigger()

  def selectPreviousValue(self):
    if self.values:
      self.valueIndex = (self.valueIndex - 1) % len(self.values)
      self.trigger()
      
  def getText(self, selected):
    if not self.values:
      if self.isSubMenu and self.append_submenu_char:
        return "%s >" % self.text
      return self.text
    if selected:
      return "%s: %s%s%s" % (self.text, Data.LEFT, self.values[self.valueIndex], Data.RIGHT)
    else:
      return "%s: %s" % (self.text, self.values[self.valueIndex])
          
class Menu(Layer, KeyListener):
  def __init__(self, engine, choices, name = None, onClose = None, onCancel = None, pos = (.2, .66 - .35), viewSize = 6, fadeScreen = False, font = "font", mainMenu = None, textColor = None, selectedColor = None, append_submenu_char = True, selectedIndex = None, showTips = True, selectedBox = False):
    self.engine       = engine

    self.logClassInits = self.engine.config.get("game", "log_class_inits")
    if self.logClassInits == 1:
      Log.debug("Menu class init (Menu.py)...")

    #Get theme
    self.themename = self.engine.data.themeLabel
    self.theme = self.engine.data.theme
    
    self.choices      = []
    self.currentIndex = 0
    #MFH
    if selectedIndex:
      self.currentIndex = selectedIndex
    self.time         = 0
    self.onClose      = onClose
    self.onCancel     = onCancel
    self.viewOffset   = 0
    self.name     = name # akedrou - for graphical support
    self.mainMenu = False
    self.graphicMenu = False
    self.useSelectedBox = selectedBox
    self.useGraphics = self.engine.config.get("game", "use_graphical_submenu")
    self.gfxText = None
    
    self.scrolling = 0
    self.delay     = 0
    self.rate      = 0
    self.scroller  = [0, self.scrollUp, self.scrollDown, self.scrollLeft, self.scrollRight]

    self.textColor = textColor
    self.selectedColor = selectedColor
    self.tipColor = Theme.menuTipTextColor

    #self.sfxVolume    = self.engine.config.get("audio", "SFX_volume")
    self.drumNav = self.engine.config.get("game", "drum_navigation")  #MFH
    
    if self.name and self.useGraphics > 0:
      try:
        try:
          self.engine.loadImgDrawing(self, "menuBackground", os.path.join("themes",self.themename,"menu","%s.png" % self.name))
          if self.menuBackground.height1() == 1:
            self.menuBackground = None
        except IOError:
          self.menuBackground = None
        self.gfxText = "%stext%d" % (self.name, len(choices))
        self.engine.loadImgDrawing(self, "menuText", os.path.join("themes",self.themename,"menu","%s.png" % self.gfxText))
        self.graphicMenu = True
        self.menux = Theme.submenuX[self.gfxText]
        self.menuy = Theme.submenuY[self.gfxText]
        self.menuScale = Theme.submenuScale[self.gfxText]
        self.vSpace = Theme.submenuVSpace[self.gfxText]
        if str(self.menux) != "None" and str(self.menuy) != "None":
          self.menux = float(self.menux)
          self.menuy = float(self.menuy)
        else:
          self.menux = .4
          self.menuy = .4
        if str(self.menuScale) != "None":
          self.menuScale = float(self.menuScale)
        else:
          self.menuScale = .5
        if str(self.vSpace) != "None":
          self.vSpace = float(self.vSpace)
        else:
          self.vSpace = .08
        Log.debug("Graphic menu enabled for submenu: %s" % self.name)
      except IOError:
        self.menuBackground = None
        self.menuText = None
      except KeyError:
        Log.warn("Your theme does not appear to properly support the %s graphical submenu. Check to be sure you have the latest version of your theme." % self.name)
        self.menuBackground = None
        self.menuText = None


    if pos == (.2, .66 - .35):  #MFH - default position, not called with a special one - this is a submenu:
      self.sub_menu_x = Theme.sub_menu_xVar
      self.sub_menu_y = Theme.sub_menu_yVar
  
      if engine.data.theme == 0:
        if self.sub_menu_x == None:
          self.sub_menu_x = .44
        if self.sub_menu_y == None:
          self.sub_menu_y = .14
      elif engine.data.theme == 1:
        if self.sub_menu_x == None:
          self.sub_menu_x = .38
        if self.sub_menu_y == None:
          self.sub_menu_y = .15
      elif engine.data.theme == 2:
        if self.sub_menu_x == None:
          self.sub_menu_x = .25
        if self.sub_menu_y == None:
          self.sub_menu_y = .14

      pos = (self.sub_menu_x, self.sub_menu_y)

    if viewSize == 6:   #MFH - default viewsize
      if self.theme == 0 or self.theme == 1 or self.theme == 2:#8bit
        viewSize = 10
    
    self.pos          = pos
    self.viewSize     = viewSize
    self.fadeScreen   = fadeScreen
    self.font         = font
    if self.font == "font":
      self.font = self.engine.data.font
    self.tipFont = Theme.menuTipTextFont
    if self.tipFont == "None":
      self.tipFont = self.font
    else:
      self.tipFont = self.engine.data.fontDict[self.tipFont]
    self.active = False
    self.mainMenu = mainMenu
    
    self.showTips = showTips
    if self.showTips:
      self.showTips = Theme.menuTipTextDisplay
    self.tipDelay = 700
    self.tipTimerEnabled = False
    self.tipScroll = 0
    self.tipScrollB = None
    self.tipScrollSpace = Theme.menuTipTextScrollSpace
    self.tipScale = Theme.menuTipTextScale
    self.tipDir = 0
    self.tipSize = 0
    self.tipY = Theme.menuTipTextY
    self.tipScrollMode = Theme.menuTipTextScrollMode # - 0 for constant scroll; 1 for back and forth
    
    for c in choices:
      try:
        text, callback = c
        if isinstance(text, tuple):
          if len(text) == 2: # a submenu's name
            c = Choice(text[0], callback, name = text[1], append_submenu_char = append_submenu_char)
          else: # Dialogs menus - FileChooser, NeckChooser, ItemChooser - this last to be changed soon
            c = Choice(text[0], callback, values = text[2], valueIndex = text[1], append_submenu_char = append_submenu_char)
        else:
          c = Choice(text, callback, append_submenu_char = append_submenu_char)
      except ValueError:
        text, callback, tipText = c
        if isinstance(text, tuple):
          if len(text) == 2: # a submenu's name
            c = Choice(text[0], callback, name = text[1], append_submenu_char = append_submenu_char, tipText = tipText)
          else: # Dialogs menus - FileChooser, NeckChooser, ItemChooser - this last to be changed soon
            c = Choice(text[0], callback, values = text[2], valueIndex = text[1], append_submenu_char = append_submenu_char, tipText = tipText)
        else:
          c = Choice(text, callback, append_submenu_char = append_submenu_char, tipText = tipText)
      except TypeError:
        pass
      self.choices.append(c)
    
    self.setTipScroll()
      
  def selectItem(self, index):
    self.currentIndex = index
    
  def shown(self):
    self.engine.input.addKeyListener(self)
    
  def hidden(self):
    self.engine.input.removeKeyListener(self)
    if self.onClose:
      self.onClose()

  def updateSelection(self):
    self.setTipScroll()
    if self.currentIndex > self.viewOffset + self.viewSize - 1:
      self.viewOffset = self.currentIndex - self.viewSize + 1
    if self.currentIndex < self.viewOffset:
      self.viewOffset = self.currentIndex
    
  #MFH added drum navigation conditional here to prevent drum nav if disabled
  #MFH updated SFX play logic to just play the new sound instead of setting volume
  def keyPressed(self, key, unicode): #racer: drum nav.
    choice = self.choices[self.currentIndex]
    c = self.engine.input.controls.getMapping(key)
    if c in Player.menuYes or key == pygame.K_RETURN:
      self.scrolling = 0
      choice.trigger(self.engine)
      #self.engine.data.acceptSound.setVolume(self.sfxVolume)  #MFH
      self.engine.data.acceptSound.play()
    elif c in Player.menuNo or key == pygame.K_ESCAPE:
      if self.onCancel:
        self.onCancel()
      self.engine.view.popLayer(self)
      self.engine.input.removeKeyListener(self)
      #self.engine.data.cancelSound.setVolume(self.sfxVolume)  #MFH
      self.engine.data.cancelSound.play()
    elif c in Player.menuDown or key == pygame.K_DOWN:
      self.scrolling = 2
      self.delay = self.engine.scrollDelay
      self.scrollDown()
    elif c in Player.menuUp or key == pygame.K_UP:
      self.scrolling = 1
      self.delay = self.engine.scrollDelay
      self.scrollUp()
    elif c in Player.menuNext or key == pygame.K_RIGHT:
      self.scrolling = 4
      self.delay = self.engine.scrollDelay
      self.scrollRight()
    elif c in Player.menuPrev or key == pygame.K_LEFT:
      self.scrolling = 3
      self.delay = self.engine.scrollDelay
      self.scrollLeft()
    return True
  
  def scrollDown(self):
    self.engine.data.selectSound.play()
    self.currentIndex = (self.currentIndex + 1) % len(self.choices)
    self.updateSelection()
  
  def scrollUp(self):
    self.engine.data.selectSound.play()
    self.currentIndex = (self.currentIndex - 1) % len(self.choices)
    self.updateSelection()
  
  def scrollLeft(self):
    self.choices[self.currentIndex].selectPreviousValue()
    
  def scrollRight(self):
    self.choices[self.currentIndex].selectNextValue()
  
  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
      if self.delay <= 0 and self.rate >= self.engine.scrollRate:
        self.rate = 0
        self.scroller[self.scrolling]()
    if self.tipTimerEnabled:
      if self.tipDelay > 0:
        self.tipDelay -= ticks
        if self.tipDelay <= 0:
          self.tipDelay = 0
          self.tipDir = (self.tipDir+1)&1
      elif self.tipDir == 1 and self.tipScrollMode == 1:
        self.tipScroll -= ticks/8000.0
        if self.tipScroll < -(self.tipSize-.98):
          self.tipScroll = -(self.tipSize-.98)
          self.tipDelay = 900
      elif self.tipDir == 0 and self.tipScrollMode == 1:
        self.tipScroll += ticks/8000.0
        if self.tipScroll > .02:
          self.tipScroll = .02
          self.tipDelay = 900
      elif self.tipScrollMode == 0:
        self.tipScroll  -= ticks/8000.0
        self.tipScrollB -= ticks/8000.0
        if self.tipScroll < -(self.tipSize):
          self.tipScroll = self.tipScrollB + self.tipSize + self.tipScrollSpace
        if self.tipScrollB < -(self.tipSize):
          self.tipScrollB = self.tipScroll + self.tipSize + self.tipScrollSpace

  def renderTriangle(self, up = (0, 1), s = .2):
    left = (-up[1], up[0])
    glBegin(GL_TRIANGLES)
    glVertex2f( up[0] * s,  up[1] * s)
    glVertex2f((-up[0] + left[0]) * s, (-up[1] + left[1]) * s)
    glVertex2f((-up[0] - left[0]) * s, (-up[1] - left[1]) * s)
    glEnd()
  
  def setTipScroll(self):
    if self.choices[self.currentIndex].tipText is None:
      return
    tipW, tipH = self.tipFont.getStringSize(self.choices[self.currentIndex].tipText, self.tipScale)
    if tipW > .99:
      self.tipSize = tipW
      self.tipDelay = 1000
      self.tipTimerEnabled = True
      self.tipScroll = 0.02
      self.tipScrollB = 0.02 + self.tipSize + self.tipScrollSpace
      self.tipWait = False
      self.tipDir = 0
    else:
      self.tipScroll = .5 - tipW/2
      self.tipScrollB = None
      self.tipTimerEnabled = False
      self.tipDir = 0
      self.tipSize = tipW
  
  def render(self, visibility, topMost):
    #MFH - display version in any menu:

    if not visibility:
      self.active = False
      return

    self.active = True
    if self.graphicMenu and self.menuBackground:
      self.engine.graphicMenuShown = True
    else:
      self.engine.graphicMenuShown = False
    
    self.engine.view.setOrthogonalProjection(normalize = True)
    try:
      v = (1 - visibility) ** 2
      # Default to this font if none was specified

      font = self.font
      tipFont = self.tipFont

      if self.fadeScreen:
        Dialogs.fadeScreen(v)
        
      wS, hS = self.engine.view.geometry[2:4]
        
      if self.graphicMenu and self.menuBackground:
        #volshebnyi - better menu scaling
        self.engine.drawImage(self.menuBackground, scale = (1.0,-1.0), coord = (wS/2,hS/2), stretched = 3)
      else:
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glEnable(GL_COLOR_MATERIAL)

      n = len(self.choices)
      x, y = self.pos

      for i, choice in enumerate(self.choices[self.viewOffset:self.viewOffset + self.viewSize]):
        if self.graphicMenu:
          if self.currentIndex == i:
            xpos = (.5,1)
          else:
            xpos = (0,.5)
          ypos = float(i+self.viewOffset)
          self.menuText.transform.reset()
          self.menuText.transform.scale(.5*self.menuScale,(-1.0/n*self.menuScale))
          self.menuText.transform.translate(wS*self.menux,(hS*self.menuy)-(hS*self.vSpace)*i)
          self.menuText.draw(rect = (xpos[0],xpos[1],ypos/n,(ypos+1.0)/n))
          #self.engine.drawImage(self.menuText, scale = (self.menuScale,-self.menuScale*2/n), coord = (wS*self.menux,hS*(self.menuy-self.vSpace*i)), rect = (xpos[0],xpos[1],ypos/n,(ypos+1.0)/n), stretched = 11)
        else:
          text = choice.getText(i + self.viewOffset == self.currentIndex)
          glPushMatrix()
          glRotate(v * 45, 0, 0, 1)

          scale = 0.002
          if self.mainMenu and self.theme < 2 and i % 2 == 1:#8bit
              scale = 0.0016

          w, h = font.getStringSize(" ", scale = scale)

          # Draw arrows if scrolling is needed to see all items
          if i == 0 and self.viewOffset > 0:
            Theme.setBaseColor((1 - v) * max(.1, 1 - (1.0 / self.viewOffset) / 3))
            glPushMatrix()
            glTranslatef(x - v / 4 - w * 2, y + h / 2, 0)
            self.renderTriangle(up = (0, -1), s = .015)
            glPopMatrix()
          elif i == self.viewSize - 1 and self.viewOffset + self.viewSize < n:
            Theme.setBaseColor((1 - v) * max(.1, 1 - (1.0 / (n - self.viewOffset - self.viewSize)) / 3))
            glPushMatrix()
            glTranslatef(x - v / 4 - w * 2, y + h / 2, 0)
            self.renderTriangle(up = (0, 1), s = .015)
            glPopMatrix()

          if i + self.viewOffset == self.currentIndex:
            if choice.tipText and self.showTips:
              if self.tipColor:
                c1, c2, c3 = self.tipColor
                glColor3f(c1,c2,c3)
              elif self.textColor:
                c1, c2, c3 = self.textColor
                glColor3f(c1,c2,c3)
              else:
                Theme.setBaseColor(1-v)
              tipScale = self.tipScale
              if self.tipScroll > -(self.tipSize) and self.tipScroll < 1:
                tipFont.render(choice.tipText, (self.tipScroll, self.tipY), scale = tipScale)
              if self.tipScrollMode == 0:
                if self.tipScrollB > -(self.tipSize) and self.tipScrollB < 1:
                  tipFont.render(choice.tipText, (self.tipScrollB, self.tipY), scale = tipScale)
            a = (math.sin(self.time) * .15 + .75) * (1 - v * 2)
            Theme.setSelectedColor(a)
            a *= -.005
            glTranslatef(a, a, a)
          else:
            Theme.setBaseColor(1 - v)      
        
          #MFH - settable color through Menu constructor
          if i + self.viewOffset == self.currentIndex and self.selectedColor:
            c1,c2,c3 = self.selectedColor
            glColor3f(c1,c2,c3)
          elif self.textColor:
            c1,c2,c3 = self.textColor
            glColor3f(c1,c2,c3)
        
          #MFH - now to catch " >" main menu options and blank them:
          if text == " >":
            text = ""
            
          if self.engine.data.submenuSelectFound and len(text) > 0 and not self.mainMenu and self.useSelectedBox:
            Tw, Th = font.getStringSize(text,scale)
            lineSpacing = font.getLineSpacing(scale)
            frameWidth = Tw*1.10
            #frameHeight = (Th+Th2)*1.10
            frameHeight = Th + lineSpacing
            boxXOffset = (x + (Tw/2))*wS
            boxYOffset = (1.0 - (y*4.0/3.0) - (Th*1.2/2))*hS
            subSelectHYFactor = 640.000/self.engine.view.aspectRatio
            subSelectHFactor = subSelectHYFactor/self.engine.data.subSelectImgH
            self.engine.data.submenuSelect.transform.reset()
            tempWScale = frameWidth*self.engine.data.subSelectWFactor
            tempHScale = -(frameHeight)*subSelectHFactor
            self.engine.data.submenuSelect.transform.scale(tempWScale,tempHScale)
            self.engine.data.submenuSelect.transform.translate(boxXOffset,boxYOffset)
            self.engine.data.submenuSelect.draw()
          
          font.render(text, (x - v / 4, y), scale = scale)
        
        
          v *= 2
          if self.theme == 1 and self.font == self.engine.data.pauseFont: # evilynux - Ugly workaround for Gh3
            y += h*.70      #Worldrave - Changed Pause menu spacing back to .70 from .65 for now.
          else:
            y += h
          glPopMatrix()
    
    
    finally:
      self.engine.view.resetProjection()
