
import wx

from configtool.decoration import Decoration
from configtool.data import (reInteger, reFloat, offsetChLabel, offsetTcLabel,
                             pinNames)


class Page:
  def __init__(self, font):
    self.modified = False
    self.valid = True
    self.fieldValid = {}
    self.textControls = {}
    self.textControlsOriginal = {}
    self.checkBoxes = {}
    self.radioButtons = {}
    self.radioButtonBoxes = {}
    self.choices = {}
    self.choicesOriginal = {}
    self.boolChoices = {}
    self.deco = Decoration()
    self.font = font

    self.SetBackgroundColour(self.deco.getBackgroundColour())
    self.Bind(wx.EVT_PAINT, self.deco.onPaintBackground)

  def enableAll(self, flag = True):
    for c in self.textControls.keys():
      self.textControls[c].Enable(flag)
    for c in self.checkBoxes.keys():
      self.checkBoxes[c].Enable(flag)
    for c in self.radioButtons.keys():
      self.radioButtons[c].Enable(flag)
    for c in self.choices.keys():
      self.choices[c].Enable(flag)

  def addTextCtrl(self, name, labelWidth, validator):
    lsz = wx.BoxSizer(wx.HORIZONTAL)
    st = wx.StaticText(self, wx.ID_ANY, self.labels[name] + " ",
                       size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
    st.SetFont(self.font)
    lsz.Add(st, 1, wx.TOP, offsetTcLabel)

    tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name)
    tc.SetFont(self.font)
    self.fieldValid[name] = True
    tc.Bind(wx.EVT_TEXT, validator)
    self.textControls[name] = tc
    lsz.Add(tc)

    return lsz

  def addCheckBox(self, name, validator):
    if name in self.labels.keys():
      lbl = self.labels[name]
    else:
      lbl = name
    cb = wx.CheckBox(self, wx.ID_ANY, lbl)
    cb.SetFont(self.font)
    cb.Bind(wx.EVT_CHECKBOX, validator)
    self.checkBoxes[name] = cb

    return cb

  def addRadioButton(self, name, style, validator, sbox = None):
    rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style)
    rb.SetFont(self.font)
    self.Bind(wx.EVT_RADIOBUTTON, validator, rb)
    self.radioButtons[name] = rb
    if sbox is not None:
      self.radioButtonBoxes[name] = sbox

    return rb

  def addChoice(self, name, choices, selection, labelWidth, validator,
                size = (-1, -1)):
    lsz = wx.BoxSizer(wx.HORIZONTAL)
    st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
                       size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
    st.SetFont(self.font)
    lsz.Add(st, 1, wx.TOP, offsetChLabel)

    ch = wx.Choice(self, wx.ID_ANY, choices = choices, size = size, name = name)
    ch.SetBackgroundColour(self.deco.getBackgroundColour())
    ch.SetFont(self.font)
    ch.Bind(wx.EVT_CHOICE, validator)
    ch.SetSelection(selection)
    lsz.Add(ch)
    self.choices[name] = ch

    return lsz

  def addPinChoice(self, name, labelWidth):
    lsz = wx.BoxSizer(wx.HORIZONTAL)
    st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
                       size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
    st.SetFont(self.font)
    lsz.Add(st, 1, wx.TOP, offsetChLabel)

    ch = wx.Choice(self, wx.ID_ANY, name = name, style = wx.CB_SORT)
    ch.SetBackgroundColour(self.deco.getBackgroundColour())
    ch.SetFont(self.font)
    ch.AppendItems(["-"] + pinNames)
    ch.Bind(wx.EVT_CHOICE, self.onChoice)
    self.choices[name] = ch
    lsz.Add(ch)

    return lsz

  # addChoice handles #defines with value, this handles choices for
  # sets of boolean #defines.
  def addBoolChoice(self, name, allowBlank, labelWidth, validator,
                    size = (-1, -1)):
    lsz = wx.BoxSizer(wx.HORIZONTAL)
    st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
                       size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
    st.SetFont(self.font)
    lsz.Add(st, 1, wx.TOP, offsetChLabel)

    ch = wx.Choice(self, wx.ID_ANY, size = size, name = name)
    ch.SetBackgroundColour(self.deco.getBackgroundColour())
    ch.SetFont(self.font)
    ch.Bind(wx.EVT_CHOICE, validator)

    if allowBlank:
      ch.Append("(none)")
      ch.SetSelection(0)

    lsz.Add(ch)
    self.boolChoices[name] = ch

    return lsz

  def setChoice(self, name, cfgValues, default):
    if name in cfgValues.keys() and cfgValues[name][1] == True:
      bv = cfgValues[name][0]
    else:
      bv = default

    s = self.choices[name].FindString(bv)
    if s < 0:
      s = self.choices[name].FindString(default)
      if s < 0:
        s = 0

    self.choices[name].SetSelection(s)

  def onTextCtrlInteger(self, evt):
    self.assertModified(True)
    tc = evt.GetEventObject()
    name = tc.GetName()
    w = tc.GetValue().strip()
    if w == "":
      valid = True
    else:
      m = reInteger.match(w)
      if m:
        valid = True
      else:
        valid = False

    self.setFieldValidity(name, valid)

    if valid:
      tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    else:
      tc.SetBackgroundColour("pink")
    tc.Refresh()
    evt.Skip()

  def onTextCtrlFloat(self, evt):
    self.assertModified(True)
    tc = evt.GetEventObject()
    name = tc.GetName()
    w = tc.GetValue().strip()
    if w == "":
      valid = True
    else:
      m = reFloat.match(w)
      if m:
        valid = True
      else:
        valid = False

    self.setFieldValidity(name, valid)

    if valid:
      tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    else:
      tc.SetBackgroundColour("pink")
    tc.Refresh()
    evt.Skip()

  def onTextCtrlPin(self, evt):
    self.assertModified(True)
    tc = evt.GetEventObject()
    self.validatePin(tc)
    evt.Skip()

  def onTextCtrl(self, evt):
    self.assertModified(True)
    evt.Skip()

  def onChoice(self, evt):
    self.assertModified(True)
    evt.Skip()

  def onCheckBox(self, evt):
    self.assertModified(True)
    evt.Skip()

  def setHelpText(self, ht):
    for k in self.textControls.keys():
      if k in ht.keys():
        self.textControls[k].SetToolTipString(ht[k])

    for k in self.checkBoxes.keys():
      if k in ht.keys():
        self.checkBoxes[k].SetToolTipString(ht[k])

    for k in self.radioButtons.keys():
      if k in ht.keys():
        self.radioButtons[k].SetToolTipString(ht[k])
        if k in self.radioButtonBoxes.keys():
          self.radioButtonBoxes[k].SetToolTipString(ht[k])

    for k in self.choices.keys():
      if k in ht.keys():
        self.choices[k].SetToolTipString(ht[k])

    for k in self.boolChoices.keys():
      for candidate in ht.keys():
        if candidate.startswith(k):
          self.boolChoices[k].SetToolTipString(ht[candidate])
          break

  def insertValues(self, cfgValues):
    self.assertValid(True)
    self.enableAll(True)
    for k in self.fieldValid.keys():
      self.fieldValid[k] = True

    for k in self.checkBoxes.keys():
      if k in cfgValues.keys() and cfgValues[k]:
        self.checkBoxes[k].SetValue(True)
      else:
        self.checkBoxes[k].SetValue(False)

    for k in self.textControls.keys():
      if k in cfgValues.keys():
        self.textControlsOriginal[k] = cfgValues[k]
        if cfgValues[k][1] == True:
          self.textControls[k].SetValue(str(cfgValues[k][0]))
        else:
          self.textControls[k].SetValue("")
      else:
        print "Key " + k + " not found in config data."

    for k in self.choices.keys():
      if k in cfgValues.keys():
        self.choicesOriginal[k] = cfgValues[k]
        self.setChoice(k, cfgValues, "-")
      else:
        print "Key " + k + " not found in config data."

    for k in self.boolChoices.keys():
      choice = self.boolChoices[k]

      # Remove items left behind from the previous configuration.
      while (choice.GetCount() and
             not choice.GetString(choice.GetCount() - 1).startswith('(')):
        choice.Delete(choice.GetCount() - 1)

      # Add items found in this configuration.
      for cfg in cfgValues.keys():
        if cfg.startswith(k):
          if cfg in self.labels.keys():
            choice.Append(self.labels[cfg])
          else:
            choice.Append(cfg)

          # As we want to write the configuration name later, not the user
          # friendly string, we store the configuration name as client data.
          n = choice.GetCount() - 1
          choice.SetClientData(n, cfg)

          if cfgValues[cfg]:
            choice.SetSelection(n)

    self.assertModified(False)

  def getValues(self):
    self.assertModified(False)
    result = {}

    for k in self.checkBoxes.keys():
      cb = self.checkBoxes[k]
      result[k] = cb.IsChecked()

    for k in self.textControls.keys():
      v = self.textControls[k].GetValue()
      if v == "":
        if k in self.textControlsOriginal.keys():
          result[k] = self.textControlsOriginal[k][0], False
        else:
          result[k] = "", False
      else:
        result[k] = v, True

    for k in self.radioButtons.keys():
      result[k] = self.radioButtons[k].GetValue(), True

    for k in self.choices.keys():
      v = self.choices[k].GetSelection()
      s = self.choices[k].GetString(v)
      if s == "-":
        if k in self.choicesOriginal.keys():
          result[k] = self.choicesOriginal[k][0], False
        else:
          result[k] = "", False
      else:
        result[k] = s, True

    for k in self.boolChoices.keys():
      choice = self.boolChoices[k]
      for i in range(choice.GetCount()):
        s = choice.GetClientData(i)
        if s:
          result[s] = (i == choice.GetSelection())

    return result

  def assertModified(self, flag):
    if flag != self.modified:
      self.parent.assertModified(self.id, flag)
      self.modified = flag

  def setFieldValidity(self, name, flag):
    self.fieldValid[name] = flag

    pgValid = True
    for k in self.fieldValid.keys():
      if not self.fieldValid[k]:
        pgValid = False
        break

    self.assertValid(pgValid)

  def assertValid(self, flag):
    if flag != self.valid:
      self.parent.assertValid(self.id, flag)
      self.valid = flag
