# The Admin4 Project
# (c) 2013-2022 Andreas Pflug
#
# Licensed under the Apache License, 
# see LICENSE.TXT for conditions of usage


import wx
import logger
import adm
from wh import xlt, Menu
import wx.propgrid as wxpg


class GenericEntry(adm.NotebookPanel):
  name=xlt("Generic")
  def __init__(self, dlg, notebook, resname=None):
    adm.NotebookPanel.__init__(self, dlg, notebook, resname)
    self.grid.Bind(wxpg.EVT_PG_CHANGED, self.OnGridChange)
    self.grid.Bind(wx.EVT_RIGHT_DOWN, self.OnGridRightClick)
    self.grid.Bind(wx.EVT_MOTION, self.OnMouseMove)
    self.availableObjectClasses={}
    self.may=[]
    self.lastRdnOid=None
    self.dialog.BindMenuId(self.OnDelAttrs)

    self.Bind("ObjectClass", self.OnClassChange)
    self.Bind("AddObjectClass", self.OnAddObjectClass)
    self.Bind("DelObjectClass", self.OnDelObjectClass)
    self.Bind("RDN", self.OnRDN)


  def AddExtraControls(self, res):
    self.grid=wxpg.PropertyGrid(self)
    res.AttachUnknownControl("ValueGrid", self.grid)
    self.grid.SetMarginColour(wx.Colour(255,255,255))


  def Go(self):
    self.lastRdnOid=self.dialog.rdnOid
    while self['ObjectClass'].GetCount() > 1:
      self['ObjectClass'].Delete(1)
    for cls in self.dialog.objectClasses:
      self['ObjectClass'].Append(cls)
    self['ObjectClass'].SetSelection(0)

    if self.dialog['Notebook'].GetPageCount() > 1:
      self['RDN'].Disable()
    if self.dialog.node:
      self['RDN'].Disable()
      self.RDN=self.dialog.node.name
    self.OnClassChange()


  def addProp(self, oid):
    attr=self.dialog.attribs[oid]
    value=attr.GetValue()
    if oid == self.dialog.rdnOid:
      icon="attribRDN"
    elif oid in self.dialog.mustAttribs:
      icon="attribMust"
    else:
      icon="attribMay"

    if attr.IsBinary():
      prop=wxpg.StringProperty(attr.name, "", xlt("<binary data; can't edit>"))
    elif attr.IsSingleValue():
      if attr.IsInteger():
          prop=wxpg.IntProperty(attr.name, oid, value)
      else:
        if not value:
          value=""
        prop=wxpg.StringProperty(attr.name, oid, value)
    else:
      if not value:
        value=[]
      prop=wxpg.ArrayStringProperty(attr.name, oid, value)
    self.grid.Append(prop)
    self.grid.SetPropertyImage(prop, self.dialog.GetBitmap(icon))

    if not attr.IsBinary():
      attr.items[self.grid]=prop
    return prop

  def updateRdnOid(self, attr):
    if self.lastRdnOid:
      prop=self.dialog.attribs[self.lastRdnOid].items.get(self.grid)
      if prop:
        self.grid.SetPropertyImage(prop, self.dialog.GetBitmap("attribMust"))
    if self.dialog.rdnOid:
      prop=attr.items.get(self.grid)
      if prop:
        self.grid.SetPropertyImage(prop, self.dialog.GetBitmap("attribRDN"))
    self.lastRdnOid=self.dialog.rdnOid


  def Check(self):
    if self.grid.IsEditorFocused():
      self.grid.CommitChangesFromEditor()
    ok=True
    ok=self.dialog.CheckValid(ok, self.dialog.objectClasses, xlt("Select a structural object class first"))
    if self.RDN:
      rdn=self.RDN.split('=')
      ok=self.dialog.CheckValid(ok, len(rdn) == 2 and rdn[0] and rdn[1], xlt("Invalid RDN format: <attr>=<value>"))
      oid=self.GetServer().GetOid(rdn[0])
      ok=self.dialog.CheckValid(ok, oid in self.dialog.mayAttribs, xlt("RDN must use a valid attribute"))
    else:
      ok=self.dialog.CheckValid(ok, False, xlt("Enter RDN"))
    return ok


  def OnRDN(self, evt):
    rdn=self.RDN.split('=')
    if len(rdn) == 2:
      oid=self.GetServer().GetOid(rdn[0])
      attr=self.dialog.attribs.get(oid)
      if attr:
        if attr.IsSingleValue():
          value=rdn[1]
        else:
          value=attr.GetValue()[:]
          value[0]=rdn[1]
        self.dialog.setRdnOid(self.RDN)
        self.updateRdnOid(attr)
        self.dialog.SetValue(oid, value, None)
    self.dialog.OnCheck()


  def OnClassChange(self, evt=None):
    self.grid.Freeze()
    self.grid.Clear()
    for attr in self.dialog.attribs.values():
      attr.items[self.grid]=None

    if self['ObjectClass'].GetSelection() == 0:
      self.EnableControls("DelObjectClass", False)
      selectedClasses=self.dialog.objectClasses
    else:
      self.EnableControls("DelObjectClass", True)
      selectedClasses=[self.ObjectClass]

    if selectedClasses:
      must, self.may= self.GetServer().GetClassSchemaMustMayOids(selectedClasses)
      ocoid=self.dialog.objectClassOid
      must.remove(ocoid)
      self.may.remove(ocoid)

      oids=list(self.dialog.attribs.keys())
      oids.remove(ocoid)

      for oid in must:
        if oid not in oids:
          attr=self.GetServer().GetAttributeSchema(oid)
          if attr:
            logger.debug("Need attribute: %s %s", oid, attr.names)
          else:
            logger.debug("Need attribute without schema: %s", oid)

      for oid in oids:
        if oid in self.may:
          self.addProp(oid)

      newOids=[]
      for oid in self.may:
        if oid not in oids:
          newOids.append(self.GetServer().GetName(oid))

      class MultiChoiceProperty(wxpg.PGProperty):
        pass
                                
      if newOids:
        prop=self.grid.Append(wxpg.MultiChoiceProperty(xlt("<new>"), "", newOids, []))
        self.grid.SetPropertyImage(prop, self.dialog.GetBitmap("attribEmpty"))

    self.grid.Thaw()


  def OnAddObjectClass(self, evt=None):
    self.GetServer().AllObjectClasses()

    presentMarker="("
    def addClass(childlist, level, onlyStructural):
      for cls in childlist:
        if onlyStructural and cls.kind != 0:
          continue
        tab="                                                                     "[:level*3]
        if cls.name.lower() in map(lambda x: x.lower(), self.dialog.objectClasses):
          pm=presentMarker
          pme=")"
        else:
          pm=" "
          pme=""
        ocs.append("%s%s%s%s" % (tab, pm, cls.names[0],pme))

        addClass(cls.children, level+1, onlyStructural)


    oldlen=len(self.dialog.objectClasses)

    ocs=[]
    onlyStructural=(oldlen == 0)
    addClass(self.GetServer().GetClassSchema('top').children, 0, onlyStructural)

    if onlyStructural:
      caption= xlt("Available structural objectClasses")
    else:
      caption= xlt("Available objectClasses")

    dlg=wx.MultiChoiceDialog(self, xlt("Add objectClass"), caption, ocs)
    if dlg.ShowModal() == wx.ID_OK:
      for i in dlg.GetSelections():
        cls=ocs[i].strip()
        if cls.startswith(presentMarker):
          continue
        if cls not in self.dialog.objectClasses:
          self.dialog.objectClasses.append(cls)

      self.dialog.UpdateObjectClasses()
      if not oldlen or not len(self.dialog.objectClasses):
        self.dialog.OnCheck()


  def OnDelObjectClass(self, evt):
    self.dialog.objectClasses.remove(self.ObjectClass)
    self.dialog.UpdateObjectClasses()


  def OnMouseMove(self, ev):
    txt=""
    pos=self.grid.HitTest(ev.GetPosition())
    if pos:
      prop=pos.GetProperty()
      if prop:
        oid=prop.GetName()
        if oid:
          attr=self.dialog.attribs[oid]
          syntax=""
          if attr.schema.syntax:
            synSch=self.GetServer().GetSyntaxSchema(attr.schema.syntax)
            if synSch:
              if attr.schema.syntax_len:
                syntax="(%s{%d})" % (synSch.desc, attr.schema.syntax_len)
              else:
                syntax="(%s)" % synSch.desc
          txt="%s %s\nOID: %s\n\n%s"  % (attr.name, syntax, oid, attr.schema.desc)
    self.grid.SetToolTip(txt)



  def OnDelAttrs(self, evt):
    prop=self.grid.GetSelection()
    if prop:
      oid=prop.GetName()
      if oid:
        self.dialog.DelValue(oid, None)
        self.dialog.OnCheck()

  def OnGridRightClick(self, evt):
    prop=self.grid.GetSelection()
    if prop:
      oid=prop.GetName()
      if oid:
        name=self.dialog.attribs[oid].name
        cm=Menu(self.dialog)
        cm.Add(self.OnDelAttrs, xlt("Remove %s") % name, xlt("Remove attribute \"%s\"") % name)
        cm.Popup(evt)


  def OnGridChange(self, ev):
    if not self.IsShown():
      return
    prop=ev.GetProperty()

    if prop:
      if prop.GetName():
        self.dialog.SetValue(prop.GetName(), prop.GetValue())
      else: # <new>
        names=prop.GetValue()
        if names:
          for name in names:
            self.dialog.SetValue(self.GetServer().GetOid(name), None)
          self.OnClassChange()
        self.GrandParent.Raise()
    self.dialog.OnCheck()



  def DelValue(self, attrval):
    prop=attrval.items.get(self.grid)
    if prop:
      self.grid.DeleteProperty(prop)
      del attrval.items[self.grid]


  def SetValue(self, attrval):
    if attrval.GetOid() == self.dialog.rdnOid:
      rdn="%s=%s" % (attrval.name, attrval.value[0])
      if rdn != self.RDN:
        self.RDN=rdn
    prop=attrval.items.get(self.grid)
    if not prop:
      oid=attrval.GetOid()
      if oid in self.may:
        prop=self.addProp(oid)
    if prop:
      value=attrval.GetValue()
      if prop.GetValue() != value:
        prop.SetValue(value)

nodeinfo=[]