'''
Update Table Linkbase is an example of a plug-in to both GUI menu and command line/web service
that updates a table linkbase from Eurofiling 2010 syntax to XII 2011 PWD syntax and saves it.

See COPYRIGHT.md for copyright information.
'''
from arelle.Version import authorLabel, copyrightLabel

def generateUpdatedTableLB(dts, updatedTableLinkbaseFile):
    import os, io
    from arelle import XmlUtil, XbrlConst
    from arelle.ViewUtil import viewReferences, referenceURI
    from arelle.ModelRenderingObject import ModelEuAxisCoord

    if dts.fileSource.isArchive:
        dts.error("genTblLB:outFileIsArchive",
                 _("Updated Table Linkbase file cannot be an archive: %(tableLBOutputFile)s."),
                 modelObject=dts, tableLBOutputFile=updatedTableLinkbaseFile)
        return
    tblAxisRelSet = dts.relationshipSet(XbrlConst.euTableAxis)
    axisMbrRelSet = dts.relationshipSet(XbrlConst.euAxisMember)
    if len(tblAxisRelSet.modelRelationships) == 0:
        dts.error("genTblLB:noInputTables",
                 _("DTS does not contain Eurofiling 2010 tables and axes: %(entryFile)s."),
                 modelObject=dts, entryFile=dts.uri)
        return

    file = io.StringIO('''
<nsmap>
<link:linkbase
xmlns:label="http://xbrl.org/2008/label"
xmlns:gen="http://xbrl.org/2008/generic"
xmlns:df="http://xbrl.org/2008/filter/dimension"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:reference="http://xbrl.org/2008/reference"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:table="http://xbrl.org/2011/table"
xmlns:formula="http://xbrl.org/2008/formula"
xsi:schemaLocation="
http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd
http://xbrl.org/2008/generic http://www.xbrl.org/2008/generic-link.xsd
http://xbrl.org/2008/reference http://www.xbrl.org/2008/generic-reference.xsd
http://xbrl.org/2008/label http://www.xbrl.org/2008/generic-label.xsd
http://xbrl.org/2011/table http://www.xbrl.org/2011/table.xsd
http://xbrl.org/2008/filter/dimension http://www.xbrl.org/2008/dimension-filter.xsd">
  <link:arcroleRef arcroleURI="http://xbrl.org/arcrole/2011/table-filter" xlink:type="simple"
   xlink:href="http://www.xbrl.org/2011/table.xsd#table-filter"/>
  <link:arcroleRef arcroleURI="http://xbrl.org/arcrole/2011/table-axis" xlink:type="simple"
   xlink:href="http://www.xbrl.org/2011/table.xsd#table-axis"/>
  <link:arcroleRef arcroleURI="http://xbrl.org/arcrole/2011/axis-subtree" xlink:type="simple"
   xlink:href="http://www.xbrl.org/2011/table.xsd#axis-subtree"/>
  <link:arcroleRef arcroleURI="http://xbrl.org/arcrole/2011/axis-filter" xlink:type="simple"
   xlink:href="http://www.xbrl.org/2011/filter-axis.xsd#axis-filter"/>
</link:linkbase>
</nsmap>
<!--  Generated by Arelle(r) http://arelle.org -->
'''
     )
    from arelle.ModelObjectFactory import parser
    parser, parserLookupName, parserLookupClass = parser(dts,None)
    from lxml import etree
    xmlDocument = etree.parse(file,parser=parser,base_url=updatedTableLinkbaseFile)
    file.close()
    nsmapElt = xmlDocument.getroot()
    #xmlDocument.getroot().init(self)  ## is this needed ??
    for lbElement in  xmlDocument.iter(tag="{http://www.xbrl.org/2003/linkbase}linkbase"):
        break

    class DocObj:  # fake ModelDocument for namespaces
        def __init__(self):
            self.xmlRootElement = lbElement
            self.xmlDocument = xmlDocument
    docObj = DocObj()

    numELRs = 0
    numTables = 0

    def copyAttrs(fromElt, toElt, attrTags):
        for attr in attrTags:
            if fromElt.get(attr):
                toElt.set(attr, fromElt.get(attr))

    def generateTable(newLinkElt, newTblElt, srcTblElt, tblAxisRelSet, axisMbrRelSet, visited):
        if srcTblElt is not None:
            for rel in tblAxisRelSet.fromModelObject(srcTblElt):
                srcAxisElt = rel.toModelObject
                if isinstance(srcAxisElt, ModelEuAxisCoord):
                    visited.add(srcAxisElt)
                    newAxisElt = etree.SubElement(newLinkElt, "{http://xbrl.org/2011/table}ruleAxis")
                    copyAttrs(srcAxisElt, newAxisElt, ("id",
                                                       "{http://www.w3.org/1999/xlink}type",
                                                       "{http://www.w3.org/1999/xlink}label"))
                    newAxisElt.set("abstract", "true") # always true on root element
                    newArcElt = etree.SubElement(newLinkElt, "{http://xbrl.org/2011/table}axisArc")
                    copyAttrs(rel, newArcElt, ("id",
                                               "{http://www.w3.org/1999/xlink}type",
                                               "{http://www.w3.org/1999/xlink}from",
                                               "{http://www.w3.org/1999/xlink}to",
                                               "order"))
                    newArcElt.set("{http://www.w3.org/1999/xlink}arcrole", XbrlConst.tableBreakdown)
                    newArcElt.set("axisDisposition", rel.axisDisposition)
                    generateAxis(newLinkElt, newAxisElt, srcAxisElt, axisMbrRelSet, visited)
                    visited.discard(srcAxisElt)

    def generateAxis(newLinkElt, newAxisParentElt, srcAxisElt, axisMbrRelSet, visited):
        for rel in axisMbrRelSet.fromModelObject(srcAxisElt):
            tgtAxisElt = rel.toModelObject
            if isinstance(tgtAxisElt, ModelEuAxisCoord) and tgtAxisElt not in visited:
                visited.add(tgtAxisElt)
                newAxisElt = etree.SubElement(newLinkElt, "{http://xbrl.org/2011/table}ruleAxis")
                copyAttrs(tgtAxisElt, newAxisElt, ("id",
                                                   "abstract",
                                                   "{http://www.w3.org/1999/xlink}type",
                                                   "{http://www.w3.org/1999/xlink}label"))
                if tgtAxisElt.primaryItemQname:
                    newRuleElt = etree.SubElement(newAxisElt, "{http://xbrl.org/2008/formula}concept")
                    newQnameElt = etree.SubElement(newRuleElt, "{http://xbrl.org/2008/formula}qname")
                    newQnameElt.text = XmlUtil.addQnameValue(docObj, tgtAxisElt.primaryItemQname)
                for dimQname, memQname in tgtAxisElt.explicitDims:
                    newRuleElt = etree.SubElement(newAxisElt, "{http://xbrl.org/2008/formula}explicitDimension")
                    newRuleElt.set("dimension", XmlUtil.addQnameValue(docObj, dimQname))
                    newMbrElt = etree.SubElement(newRuleElt, "{http://xbrl.org/2008/formula}member")
                    newQnameElt = etree.SubElement(newMbrElt, "{http://xbrl.org/2008/formula}qname")
                    newQnameElt.text = XmlUtil.addQnameValue(docObj, memQname)
                newArcElt = etree.SubElement(newLinkElt, "{http://xbrl.org/2011/table}axisArc")
                copyAttrs(rel, newArcElt, ("id",
                                           "{http://www.w3.org/1999/xlink}type",
                                           "{http://www.w3.org/1999/xlink}from",
                                           "{http://www.w3.org/1999/xlink}to",
                                           "order"))
                newArcElt.set("{http://www.w3.org/1999/xlink}arcrole", XbrlConst.tableAxisSubtree)
                generateAxis(newLinkElt, newAxisElt, tgtAxisElt, axisMbrRelSet, visited)
                visited.discard(tgtAxisElt)

    # sort URIs
    linkroleUris = sorted([linkroleUri
                           for linkroleUri in tblAxisRelSet.linkRoleUris])

    firstNewLinkElt = None
    roleRefUris = set()
    for linkroleUri in linkroleUris:
        numELRs += 1
        newLinkElt = etree.SubElement(lbElement, "{http://xbrl.org/2008/generic}link")
        newLinkElt.set("{http://www.w3.org/1999/xlink}type", "extended")
        newLinkElt.set("{http://www.w3.org/1999/xlink}role", linkroleUri)
        if firstNewLinkElt is None: firstNewLinkElt = newLinkElt
        # To do: add roleRef if needed
        tblAxisRelSet = dts.relationshipSet(XbrlConst.euTableAxis, linkroleUri)
        axisMbrRelSet = dts.relationshipSet(XbrlConst.euAxisMember, linkroleUri)
        for srcTblElt in tblAxisRelSet.rootConcepts:
            if srcTblElt.tag == "{http://www.eurofiling.info/2010/rendering}table":
                numTables += 1
                newTblElt = etree.SubElement(newLinkElt, "{http://xbrl.org/2011/table}table")
                newTblElt.set("aspectModel", "dimensional")
                copyAttrs(srcTblElt, newTblElt, ("id",
                                                 "{http://www.w3.org/1999/xlink}type",
                                                 "{http://www.w3.org/1999/xlink}label"))
                generateTable(newLinkElt, newTblElt, srcTblElt, tblAxisRelSet, axisMbrRelSet, set())

                if linkroleUri not in roleRefUris:
                    srcRoleRefElt = XmlUtil.descendant(srcTblElt.getroottree(), XbrlConst.link, "roleRef", "roleURI", linkroleUri)
                    if srcRoleRefElt is not None:
                        roleRefUris.add(linkroleUri)
                        newRoleRefElt = etree.Element("{http://www.xbrl.org/2003/linkbase}roleRef")
                        copyAttrs(srcRoleRefElt, newRoleRefElt, ("roleURI",
                                                                 "{http://www.w3.org/1999/xlink}type",
                                                                 "{http://www.w3.org/1999/xlink}href"))
                        firstNewLinkElt.addprevious(newRoleRefElt)

    fh = open(updatedTableLinkbaseFile, "w", encoding="utf-8")
    XmlUtil.writexml(fh, xmlDocument, encoding="utf-8")
    fh.close()

    dts.info("info:updateTableLinkbase",
             _("Updated Table Linkbase of %(entryFile)s has %(numberOfLinkroles)s linkroles, %(numberOfTables)s tables in file %(tableLBOutputFile)s."),
             modelObject=dts,
             entryFile=dts.uri, numberOfLinkroles=numELRs, numberOfTables=numTables, tableLBOutputFile=updatedTableLinkbaseFile)

def updateTableLBMenuEntender(cntlr, menu):
    # Extend menu with an item for the savedts plugin
    menu.add_command(label="Update table linkbase",
                     underline=0,
                     command=lambda: updateTableLBMenuCommand(cntlr) )

def updateTableLBMenuCommand(cntlr):
    # save DTS menu item has been invoked
    if cntlr.modelManager is None or cntlr.modelManager.modelXbrl is None:
        cntlr.addToLog("No taxonomy loaded.")
        return

        # get file name into which to save log file while in foreground thread
    updatedTableLinkbaseFile = cntlr.uiFileDialog("save",
                                                  title=_("arelle - Save Updated Table Linkbase file"),
            initialdir=cntlr.config.setdefault("tableLinkbaseFileDir","."),
            filetypes=[(_("Linkbase file .xml"), "*.xml")],
            defaultextension=".xml")
    if not updatedTableLinkbaseFile:
        return False
    import os
    cntlr.config["tableLinkbaseFileDir"] = os.path.dirname(updatedTableLinkbaseFile)
    cntlr.saveConfig()

    try:
        generateUpdatedTableLB(cntlr.modelManager.modelXbrl, updatedTableLinkbaseFile)
    except Exception as ex:
        dts = cntlr.modelManager.modelXbrl
        dts.error("exception",
            _("Updated table linkbase generation exception: %(error)s"), error=ex,
            modelXbrl=dts,
            exc_info=True)

def updateTableLBCommandLineOptionExtender(parser):
    # extend command line options with a save DTS option
    parser.add_option("--save-updated-table-linkbase",
                      action="store",
                      dest="updatedTableLinkbaseFile",
                      help=_("Update table linkbase file from Eurofiling 2010 to XII 2011 syntax."))

def updateTableLBCommandLineXbrlRun(cntlr, options, modelXbrl, *args, **kwargs):
    # extend XBRL-loaded run processing for this option
    if getattr(options, "updatedTableLinkbaseFile", None):
        if cntlr.modelManager is None or cntlr.modelManager.modelXbrl is None:
            cntlr.addToLog("No taxonomy loaded.")
            return
        generateUpdatedTableLB(cntlr.modelManager.modelXbrl, options.updatedTableLinkbaseFile)


__pluginInfo__ = {
    'name': 'Update Table Linkbase',
    'version': '0.9',
    'description': "This plug-in updates a table linkbase from Eurofiling 2010 syntax to XII 2011 PWD syntax. "
                   "The updated linkbase is saved to a local file.  "
                   "Hrefs in the new file are identical the prior hrefs (not offset considering save-to directory).  ",
    'license': 'Apache-2',
    'author': authorLabel,
    'copyright': copyrightLabel,
    # classes of mount points (required)
    'CntlrWinMain.Menu.Tools': updateTableLBMenuEntender,
    'CntlrCmdLine.Options': updateTableLBCommandLineOptionExtender,
    'CntlrCmdLine.Xbrl.Run': updateTableLBCommandLineXbrlRun,
}
