#!/usr/bin/env python

## IMPORTS
import glob
import os
import sys
import uuid
import xml.etree.ElementTree as etree

########## GLOBAL CONSTANTS
USER_COLLECTION_PATH="/home/raul/.qet/elements/temp_tb/" #path to create terminal blocks elements
TB_BLOCKS_IN_A_FOLIO = 50
TB_HEAD_WIDTH = 40
TB_HEAD_UNION_HEIGHT = 45
TB_HEAD_UNION_WIDTH = 5
TB_HEIGHT = 90
TB_WIDTH = 20

DEBUG = False


########## getListOfBorneElements
def getListOfBorneElements (proj):
    """Return a list of component in library(collection) that
    are 'terminals' and english name is 'terminal block'.
    Case insensitive
    
    @param QETproject: root of the QET project
    @return [] list with el names of elements that are terminal as 'like_type'"""

    collection = proj.find('collection') #collection root
    ret = [] #return list

    for element in collection.findall ('.//element'):
        definition = element[0]
        if 'link_type' in definition.attrib:
            if definition.attrib['link_type'] == 'terminal':
                ret.append (element.attrib['name'])
    return ret


########## getListOfUsedElements
def getListOfUsedElements (proj, elementsToFind):
    """Return a list of all elements in elements 'elementsToFind'
    that are used in diagram pages.
    
    @param QETproject: root of the QET project
    @param elementsToFind: list of elements to find
    @return [] list of list where:
        [element_uuid, terminal_xref, terminal block, terminal number, cable_number1, cable_number2] """
    

    ret = [] #return list
    pageOffset = int(proj.attrib[ 'folioSheetQuantity' ]) #check for a index of folios page
    
    for diagram in proj.findall ('diagram'): #all diagrams
        for element in diagram.findall ('.//element'): #all elements in diagram
            if 'type' in element.attrib: #elements must have a 'type'
                for el in elementsToFind: 
                    offset = len(element.attrib['type']) - len(el)
                    if ( element.attrib['type'][offset:] == el): #check if element is one of elements in collection collected
                        text = element.find( 'inputs' ).find( 'input' ).attrib['text'] #text like 'X1:1'
                        if ( ':' in text ) and ( len(text) > 2 ):   #TODO: improve with regex

                            element_data = [] #list for current element
                            if DEBUG: print ("<getListOfUsedElements> searching: %s  \t offset: %i \t %s \t %s" %(el, offset, element.attrib['type'], element.attrib['type'][offset:]))
                            if DEBUG: print ("<getListOfUsedElements> Working with element %s" %element.attrib['uuid'])
                            element_data.append( element.attrib['uuid'] ) #uuid
                            element_data.append( getXRef (diagram, element, pageOffset) ) #xref
                            element_data.append( text.split( ':' )[0].upper() ) #terminal block name
                            element_data.append( text.split( ':' )[1].upper() )# terminal number
                            terminals = element.find( 'terminals' ).findall( 'terminal' )
                            element_data.append( getCableNum( diagram, terminals[0].attrib['id'] ) ) #cable 1
                            element_data.append( getCableNum( diagram, terminals[1].attrib['id'] ) )#cable 2

                            ret.append (element_data)
                            
    return ret


########## getXRef
def getXRef (diagram, element, iPageOffset):
    """Return a string with the xreference of the 'element' at page 'diagam'.
    The page number incremented in one if there are a "index" page
    
    @param diagram: diagram(page) XML etree object
    @param element: element XML etree object
    @param iPagesOffset: offset to add in page_order references
    @return: string like "p-rc" (page - rowLetter colNumber)"""

    
    #get requiered data
    page_order = int( diagram.attrib['order'] ) + iPageOffset
    cols = int( diagram.attrib['cols'] )
    col_size = int( diagram.attrib['colsize'] )
    rows = int( diagram.attrib['rows'] )
    row_size = int( diagram.attrib['rowsize'] )
    element_x = int( element.attrib ['x'] )
    element_y = int( element.attrib ['y'] )
    rows_letters =  [chr(x+65) for x in range(rows)]

    if DEBUG: print ("<getXRef>: Page order: %i\tCol size: %i\tRow size: %i\tX position: %i\tY Position: %i" \
                     %(page_order, col_size, row_size, element_x, element_y))

    row_letter = rows_letters[ int(element_y / row_size) -1 + 1 ]  #+1: cal calc. -1 index of lists start 0.
    column =  str( int( element_x / col_size ) +1 )
    return str(page_order) + "-" + row_letter + column 
    

########## getCableNum
def getCableNum (diagram, terminalId):
    """Return the cable number connected at 'terminalId' in the page 'diagram'
    
    @param diagram: diagram(page) XML etree object
    @param terminalId: text with the terminal Id
    @return: string whith cable  number"""

    ret=''
    for cable in diagram.find( 'conductors' ).findall( 'conductor' ):
        for cable_terminal in [x for x in cable.attrib if x[:8]=='terminal']:
            if cable.attrib[cable_terminal] == terminalId:
                ret = cable.attrib['num']
    return ret
       
            
########## createQETCollectionFile
def createQETCollectionFile (sCollectionPath):
    """Delete files o temp collection and create collection QET file.
    @param sCollectionPath end with '/'
    @return: none"""

    # delete contents
    fs = glob.glob(sCollectionPath + '*')
    for f in fs:
        os.remove(f)

    #create QET directory file
    f = open (sCollectionPath + 'qet_directory', 'w')
    xml = ['<qet-directory>',
            '    <names>',
            '        <name lang="en">TEMP: Terminals</name>',
            '    </names>',
            '</qet-directory>']
    f.writelines(["%s\n" % line for line in xml])
    f.close()


########## getOrder
def getOrder (s):
    """Return a int value according first char of s:
    * start by letter: index about -300
    * start by '-': index about -200
    * start by '+': index abput -100
    * start by digit: positive value
    This is usefull to sort lists with numbers and letters, but sortering the number values as
    integers and not as strings"""

    
    if s:
        #calc offset
        if s[0] == '-':
            offset = -200
        elif s[0] == '+': offset = -100
        else: offset = 0
        
    try:
        foo = int(s) #provoke a exception?
        return int(s) + offset
        
    except:
        return ord(s[0]) -1000
    

    
########## drawTerminalBlock
def drawTerminalBlock (sFilePath, sTerminalBlockID, data):
    """Creates a XML file with the terminal block elements. if creates more than one folio
    (a lot of terminals) creates a index (0,1,...) as file name suffix.
    Uses GLOBAL CONSTANTS
    @param sFilePath: path where create collection with end '/'
    @param sTerminalBlockID: id/name of the terminal block.
    @param data: unsortened list of list with terminal data of the corresponding TerminalBlockID.
                 [element_uuid, terminal_xref, terminal block, terminal number, cable_number1, cable_number2]
    @return: none"""
 
    data.sort(key=lambda x: getOrder(x[3])) #first sort by alphanimeric order

    last_cable_number1 = '' #used to draw a bridge between terminals
    last_cable_number2 = ''
    
    i2 = 0
    for i1 in range (0, len(data) , TB_BLOCKS_IN_A_FOLIO):
    #process every Term- Block of max length per folio
        
        i2 =  (i2 + TB_BLOCKS_IN_A_FOLIO)
        if i2 > len( data ): i2 = len( data )
        subData = data[ i1:i2 ]

        iQuantity = len( subData )
        totalWith = TB_HEAD_WIDTH + iQuantity * TB_WIDTH
        sUUID = uuid.uuid1().urn[9:]
        sID = sTerminalBlockID + '_' + str(i1 + 1) + '-' + str(i2)

        f = open (sFilePath + sID + '.elmt', 'w')

        rect_style =  ' antialias="false" style="line-style:normal;line-weight:normal;filling:white;color:black"'
        line_style =  ' antialias="false" style="line-style:normal;line-weight:normal;filling:none;color:black"'


        xml = ['<definition hotspot_x="4" hotspot_y="4" width="%i" height="%i" orientation="dyyy" link_type="simple" type="element">' \
               %(totalWith, max( TB_HEIGHT, TB_HEIGHT )),
               '  <uuid uuid="%s"/>' %sUUID,
               '  <names>',
               '    <name lang="en">%s</name>' %sID,
               '  </names>',
               '  <description>']
        x=0
        #head
        xml.append( '    <rect x="%i" y="0" width="%i" height="%i"' \
                    %(x, TB_HEAD_WIDTH, TB_HEIGHT) + rect_style + '/>' )
        x += TB_HEAD_WIDTH
        
        #head union
        xml.append( '    <rect x="%i" y="%i" width="%i" height="%i"' \
                    %(x, TB_HEAD_UNION_HEIGHT/2, TB_HEAD_UNION_WIDTH, TB_HEAD_UNION_HEIGHT) + \
                    rect_style + '/>' )
        x += TB_HEAD_UNION_WIDTH

        #TB ID
        xml.append( '    <input text="%s" tagg="label" rotation="270" size="10" x="%i" y="%i"' \
                    %(sTerminalBlockID, TB_HEAD_WIDTH/2, TB_HEIGHT/2 + (len(sTerminalBlockID)/2)*10) + '/>' )

        #All terminals
        for t in subData:
            halfx = x + TB_WIDTH/2
            xml.append('    <rect x="%i" y="0" width="%i" height="%i"' \
                       %(x, TB_WIDTH, TB_HEIGHT) + rect_style + '/>')
            xml.append('    <text text="%s" rotation="270" size="9" x="%i" y="54"/>' %(t[3], halfx + 4) )
            xml.append('    <text text="%s" rotation="270" size="6" x="%i" y="30"/>' %(t[1], halfx + 4) )
            xml.append('    <input text="%s" tagg="none" rotation="270" size="6" x="%i" y="%i"/>' %(t[4], halfx - 4, -10) )
            xml.append('    <input text="%s" tagg="none" rotation="270" size="6" x="%i" y="%i"/>' %(t[5], halfx - 4, TB_HEIGHT + 20) )

            xml.append('    <line length2="1.5" y1="%i" y2="%i" x1="%i" end1="none" x2="%i" end2="none" length1="1.5"' \
                       %(0, -20, halfx,halfx) + line_style + '/>')
            xml.append('    <line length2="1.5" y1="%i" y2="%i" x1="%i" end1="none" x2="%i" end2="none" length1="1.5"' \
                       %(TB_HEIGHT, TB_HEIGHT + 20, halfx, halfx) + line_style + '/>')

            xml.append('    <terminal orientation="n" x="%i" y="%i"/>' %(halfx, 0 - 20))
            xml.append('    <terminal orientation="s" x="%i" y="%i"/>' %(halfx, TB_HEIGHT + 20))
            xml.append('    <circle x="%i" y="%i" diameter="4"' %(halfx - 2, TB_HEIGHT - 20) + line_style + '/>')

            #bridge
            if (t[4] == t[5]) and (last_cable_number1 == last_cable_number2) and (t[4] == last_cable_number1):
                xml.append('    <line length2="1.5" y1="%i" y2="%i" x1="%i" end1="none" x2="%i" end2="none" length1="1.5"' \
                       %(TB_HEIGHT - 20 + 2 , TB_HEIGHT - 20 + 2, halfx - TB_WIDTH, halfx) + line_style + '/>')
            last_cable_number1 = t[4]
            last_cable_number2 = t[5]
                
            x += TB_WIDTH
          
        xml.append( '  </description>' )
        xml.append( '</definition>' )

        f.writelines(["%s\n" % line for line in xml])

       
########## MAIN
if __name__ == '__main__':

    print ("\n<< Terminal Block maker for QET - by Raul Roda >>\n")
    print ("Remember the next conditions:")
    print ("  * Terminal elements must be 'terminal' as type in the Element Editor.")
    print ("  * If terminal elements has more than one textfield, the first one is considered.")
    print ("  * Name of terminals must be in form 'x:a'. x = terminal block name; a = terminal name in the block.\n")

    #must exist one argument
    if len(sys.argv) > 1:
        QET_PROJECT = sys.argv[1]
    else:
        print("\n\nUsage: QET_TBmaker.py qetfileproject.qet\n")
        exit()
    qet_tree = etree.parse(QET_PROJECT)  
    QETproject = qet_tree.getroot()

    borneElements = getListOfBorneElements (QETproject) #list with 'terminal' elements in collection

    usedBorneElements = getListOfUsedElements (QETproject, borneElements) #[element_uuid, terminal_xref, terminal block, terminal number, cable_number1, cable_number2]

    if DEBUG: print ([e for e in usedBorneElements])
    createQETCollectionFile( USER_COLLECTION_PATH)

    #Draw every different block terminal name: X1, X2,...
    allTB = list ( set( [x[2] for x in usedBorneElements] ) ) #get unique TerminalBlockID
    for TB in allTB:
        drawTerminalBlock (USER_COLLECTION_PATH, TB, [x for x in usedBorneElements if x[2] == TB] )    

