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

from ConfigParser import ConfigParser
import Log
import Resource
import os

encoding  = "iso-8859-1"
config    = None
prototype = {}

logIniReads = 0   #MFH - INI reads disabled by default during the startup period
logUndefinedGets = 0 

class MyConfigParser(ConfigParser):
  def write(self, fp, type = 0):
      if type == 1:
        return self.writeController(fp)
      elif type == 2:
        return self.writePlayer(fp)
      if self._defaults:
        fp.write("[%s]\n" % DEFAULTSECT)
        for (key, value) in self._defaults.items():
          fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
      sections = sorted(self._sections)
      for section in sections:
        if section == "theme":
          continue
        elif section == "controller":
          continue
        elif section == "player":
          continue
        fp.write("[%s]\n" % section)
        sectList = self._sections[section].items()
        sectList.sort()
        for key, value in sectList:
          if key != "__name__":
            fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
        
  def writeTheme(self, fp):
      if self._defaults:
        fp.write("[%s]\n" % DEFAULTSECT)
        for (key, value) in self._defaults.items():
          fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
      sections = sorted(self._sections)
      for section in sections:
        if section != "theme":
          continue
        fp.write("[%s]\n" % section)
        sectList = self._sections[section].items()
        sectList.sort()
        for key, value in sectList:
          if key != "__name__":
            fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
  
  def writeController(self, fp):
      if self._defaults:
        fp.write("[%s]\n" % DEFAULTSECT)
        for (key, value) in self._defaults.items():
          fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
      sections = sorted(self._sections)
      for section in sections:
        if section != "controller":
          continue
        fp.write("[%s]\n" % section)
        sectList = self._sections[section].items()
        sectList.sort()
        for key, value in sectList:
          if key != "__name__":
            fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
  
  def writePlayer(self, fp):
      if self._defaults:
        fp.write("[%s]\n" % DEFAULTSECT)
        for (key, value) in self._defaults.items():
          fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
      sections = sorted(self._sections)
      for section in sections:
        if section != "player":
          continue
        fp.write("[%s]\n" % section)
        sectList = self._sections[section].items()
        sectList.sort()
        for key, value in sectList:
          if key != "__name__":
            fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
        fp.write("\n")
        
class Option:
  """A prototype configuration key."""
  def __init__(self, **args):
    for key, value in args.items():
      setattr(self, key, value)
      
def define(section, option, type, default = None, text = None, options = None, prototype = prototype, tipText = None):
  """
  Define a configuration key.
  
  @param section:    Section name
  @param option:     Option name
  @param type:       Key type (e.g. str, int, ...)
  @param default:    Default value for the key
  @param text:       Text description for the key
  @param options:    Either a mapping of values to text descriptions
                    (e.g. {True: 'Yes', False: 'No'}) or a list of possible values
  @param prototype:  Configuration prototype mapping
  @param tipText:    Helpful tip text to display in option menus.
  """
  if not section in prototype:
    prototype[section] = {}
    
  if type == bool and not options:
    options = [True, False]
    
  prototype[section][option] = Option(type = type, default = default, text = text, options = options, tipText = tipText)

def load(fileName = None, setAsDefault = False, type = 0):
  """Load a configuration with the default prototype"""
  global config, logIniReads, logUndefinedGets
  c = Config(prototype, fileName, type)
  if setAsDefault and not config:
    config = c
  logIniReads = c.get("game", "log_ini_reads")
  logUndefinedGets = c.get("game", "log_undefined_gets")
  return c

#def unLoad():   #MFH - to unload the global config object for later reload
#  global config
#  config = None

class Config:
  """A configuration registry."""
  def __init__(self, prototype, fileName = None, type = 0):
    """
    @param prototype:  The configuration protype mapping
    @param fileName:   The file that holds this configuration registry
    """
    self.prototype = prototype

    # read configuration
    self.config = MyConfigParser()

    if fileName:
      if not os.path.isfile(fileName):
        path = Resource.getWritableResourcePath()
        fileName = os.path.join(path, fileName)
      self.config.read(fileName)
  
    self.fileName  = fileName
    self.type = type
  
    # fix the defaults and non-existing keys
    for section, options in prototype.items():
      if not self.config.has_section(section):
        self.config.add_section(section)
      for option in options.keys():
        type    = options[option].type
        default = options[option].default
        if not self.config.has_option(section, option):
          self.config.set(section, option, str(default))
    
  def get(self, section, option):
    """
    Read a configuration key.
    
    @param section:   Section name
    @param option:    Option name
    @return:          Key value
    """
    
    global logIniReads, logUndefinedGets

    try:
      type    = self.prototype[section][option].type
      default = self.prototype[section][option].default
    except KeyError:
      if logUndefinedGets == 1:
        Log.warn("Config key %s.%s not defined while reading %s." % (section, option, self.fileName))
      type, default = str, None
  
    value = self.config.has_option(section, option) and self.config.get(section, option) or default
    if type == bool:
      value = str(value).lower()
      if value in ("1", "true", "yes", "on"):
        value = True
      elif value in ("none", ""):
        value = default #allows None-default bools to return None
      else:
        value = False
    else:
      try:
        value = type(value)
      except:
        value = None
      
    #myfingershurt: verbose log output
    if logIniReads == 1:
      Log.debug("Config.get: %s.%s = %s" % (section, option, value))
    return value
  
  def getOptions(self, section, option):
    """
    Read the preset options of a configuration key.
    
    @param section:   Section name
    @param option:    Option name
    @return:          Tuple of Key list and Values list
    """
    global logIniReads, logUndefinedGets


    try:
      options = self.prototype[section][option].options.values()
      keys    = self.prototype[section][option].options.keys()
      type    = self.prototype[section][option].type
    except KeyError:
      if logUndefinedGets == 1:
        Log.warn("Config key %s.%s not defined while reading %s." % (section, option, self.fileName))
      type = None
      
    optionList = []
  
    if type != None:
      for i in range(len(options)):
        value = keys[i]
        if type == bool:
          value = str(value).lower()
          if value in ("1", "true", "yes", "on"):
            value = True
          else:
            value = False
        else:
          try:
            value = type(value)
          except:
            value = None
        optionList.append(value)
      
    #myfingershurt: verbose log output
    if logIniReads == 1:
      Log.debug("Config.getOptions: %s.%s = %s" % (section, option, str(optionList)))
    return optionList, options
  
  def getTipText(self, section, option):
    """
    Return the tip text for a configuration key.
    
    @param section:   Section name
    @param option:    Option name
    @return:          Tip Text String
    """
    
    global logIniReads, logUndefinedGets

    try:
      text = self.prototype[section][option].tipText
    except KeyError:
      if logUndefinedGets == 1:
        Log.warn("Config key %s.%s not defined while reading %s." % (section, option, self.fileName))
      text = None
      
    #myfingershurt: verbose log output
    if logIniReads == 1:
      Log.debug("Config.getTipText: %s.%s = %s" % (section, option, text))
    return text

#-------------------------
# glorandwarf: returns the default value of a configuration key
  def getDefault(self, section, option):
    """
    Read the default value of a configuration key.
    
    @param section:   Section name
    @param option:    Option name
    @return:          Key value
    """
    global logIniReads, logUndefinedGets


    try:
      type    = self.prototype[section][option].type
      default = self.prototype[section][option].default
    except KeyError:
      if logUndefinedGets == 1:
        Log.warn("Config key %s.%s not defined while reading %s." % (section, option, self.fileName))
      type, default = str, None
  
    value = default
    if type == bool:
      value = str(value).lower()
      if value in ("1", "true", "yes", "on"):
        value = True
      else:
        value = False
    else:
      try:
        value = type(value)
      except:
        value = None
      
    #myfingershurt: verbose log output
    if logIniReads == 1:
      Log.debug("Config.getDefault: %s.%s = %s" % (section, option, value))
    return value
#-------------------------

  def set(self, section, option, value):
    """
    Set the value of a configuration key.
    
    @param section:   Section name
    @param option:    Option name
    @param value:     Value name
    """

    global logUndefinedGets

    try:
      prototype[section][option]
    except KeyError:
      if logUndefinedGets == 1:
        Log.warn("Config key %s.%s not defined while writing %s." % (section, option, self.fileName))
    
    if not self.config.has_section(section):
      self.config.add_section(section)

    if type(value) == unicode:
      value = value.encode(encoding)
    else:
      value = str(value)

    self.config.set(section, option, value)
    
    f = open(self.fileName, "w")
    self.config.write(f, self.type)
    f.close()

  def getModOptions1(self, twoChord, hopo8th, compact = False):
    #For use with single digit flags
    #Do not change the order

    #0    
    #Value 0 not used / 1 used
    if twoChord > 0:
      twoChordUsed = 1
    else:
      twoChordUsed = 0

    #1      
    #Value 0 off / 1 on
    disableVBPMUsed = int(self.get("game", "disable_vbpm"))

    #2    
    #Value 0 on / 1 on - removed.
    hopoDisableUsed = 0

    #3
    #Value 0 FoF / 1 RFmod
    hopoMarks = 1

    #4    
    #Value 0 FoF / 1 RFmod / 2 RFmod2
    hopoStyle = int(self.get("game", "hopo_system"))

    #5    
    #Value 0 FoF / 1 GH / 2 Custom
    pov = int(self.get("fretboard", "point_of_view"))

    #6    
    #Value 0 FoF / 1 Capo -- akedrou: no game effect - removing key.
    margin = 0

    #7    
    #Value 0 no / 1 yes
    hopo8thUsed = int(hopo8th)

    #8    
    #Value 0 bpm / 1 difficulty
    boardSpeed = 0

    encode = "%d%d%d%d%d%d%d%d%d" % (twoChordUsed, disableVBPMUsed, hopoDisableUsed, hopoMarks, hopoStyle, pov, margin, hopo8thUsed, boardSpeed)
    #return encode
    return "000000000"
  
  def getModOptions2(self):
    #For use with more than single digit flags
    #Do not change order
    #Will be used for some of the Cmod values
    encode = ""
    boardSpeedMult = 0
    return encode
    

  def prettyModOptions(self, modOptions):
    encode = ""
    
    modOptions1, mod2 = modOptions.split(',', 2)

    if modOptions1 != "" and modOptions1 != "Default":
      if modOptions1[0] == '1':
        encode += "2"
      if modOptions1[1] == '1':
        encode += "v"
      if modOptions1[2] == '1':
        encode += "h"
      if modOptions1[3] == '1':
        encode += "m"
      if modOptions1[4] == '1':
        encode += "k"
      elif modOptions1[4] == '2':
        encode += "K"
      if modOptions1[5] == '1':
        encode += "p"
      elif modOptions1[5] == '2':
        encode += "P"
      if modOptions1[6] == '1':
        encode += "C"
      if modOptions1[7] == '1':
        encode += "9"
      if modOptions1[8] == '1':
        encode += "d"

    if mod2 != "" and mod2 != "Default":
      encode += ","
      modOptions2 = mod2.split(',')
      if modOptions2[0] != "":
        encode += "BS=%s" % (modOptions2[0])
    return encode
  
def get(section, option):
  """
  Read the value of a global configuration key.
  
  @param section:   Section name
  @param option:    Option name
  @return:          Key value
  """
  global config
  
  #MFH - for init config.gets
  #if config == None:
  #  load()

  return config.get(section, option)
  
def set(section, option, value):
  """
  Write the value of a global configuration key.
  
  @param section:   Section name
  @param option:    Option name
  @param value:     New key value
  """
  global config
  return config.set(section, option, value)

#-------------------------
# glorandwarf: returns the default value of the configuration key
def getDefault(section, option):
  """
  Read the default value of a global configuration key.
  
  @param section:   Section name
  @param option:    Option name
  @return:          Key value
  """
  global config
  return config.getDefault(section, option)

def getTipText(section, option):
  """
  Return the tip text for a global configuration key.
  
  @param section:   Section name
  @param option:    Option name
  @return:          Tip Text String
  """
  global config
  return config.getTipText(section, option)

#-------------------------
def getOptions(section, option):
  """
  Read the default value of a global configuration key.
  
  @param section:   Section name
  @param option:    Option name
  @return:          Tuple of Key list and Values list
  """
  global config
  return config.getOptions(section, option)

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