'''
Created on 6 Aug 2015

@author: Shirley Crompton, UK Science and Technology Facilities Council
'''
import logging
import os
import xml.etree.ElementTree as ET
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4.QtCore import pyqtSlot
import nemo_ncml_tab_widget
from thredds_crawler.crawl import Crawl

class Ncml_generator(QtGui.QDialog):
    '''
    Gui editor to capture user input for the purpose of generating NCML representation of pynemo source datasets.
    '''

    def __init__(self, basefile):
        '''
        Initialises the UI components
        '''
        super(Ncml_generator, self).__init__()     # no params yet, may be allow user to predefine an input ncml for edit???? 
        #Logging for class
        self.logger = logging.getLogger(__name__)   #logger config'ed in pynemo_exe.py
        
        if not basefile:
            testpath, file_name = os.path.split(__file__)
            self.baseFile = os.path.join(testpath,'base.ncml')
        else:
            self.baseFile = basefile
            print 'ncml baseFile : ', str(self.baseFile)
        
        self.filename = None # store the output file pointer      
        self.initUI()
        
        
    def initUI(self):
        QtGui.QToolTip.setFont(QtGui.QFont('SansSerif', 11))
        '''
        vbox is the top container
        '''
        #the 
        vbox = QtGui.QVBoxLayout(self)
        vbox.setSpacing(10)
        vbox.setContentsMargins(10, 10, 5, 5)
        
        '''
        top panel for output file
        '''
        top_outfile_label = QtGui.QLabel(unicode('Output filename').encode('utf-8'))
        self.top_outfile_name = QtGui.QLineEdit()    #location is pre-defined
        self.top_outfile_name.setToolTip(unicode('Define output file').encode('utf-8'))
        self.top_outfile_name.returnPressed.connect(self.get_fname_input)
        
        top_outfile_button = QtGui.QPushButton(unicode('Select file').encode('utf-8'))
        top_outfile_button.clicked.connect(self.get_fname)
        
        top_grpBox = QtGui.QGroupBox(unicode('Define output file').encode('utf-8'), None)
        top_grid = QtGui.QGridLayout(top_grpBox)
        top_grid.setVerticalSpacing(5)
        top_grid.setHorizontalSpacing(10)
        top_grid.addWidget(top_outfile_label, 1, 0)
        top_grid.addWidget(self.top_outfile_name, 1, 1)
        top_grid.addWidget(top_outfile_button, 1,2, QtCore.Qt.AlignRight)
        
        '''
        middle panel for tab folder
        '''
        self.tabWidget = QtGui.QTabWidget()
        self.tracer_tab = nemo_ncml_tab_widget.Ncml_tab(unicode("Tracer").encode('utf-8'))
        self.tracer_tab.setEnabled(False)
        self.dynamic_tab = nemo_ncml_tab_widget.Ncml_tab(unicode("Dynamics").encode('utf-8'))
        self.dynamic_tab.setEnabled(False)
        self.ice_tab = nemo_ncml_tab_widget.Ncml_tab(unicode("Ice").encode('utf-8'))
        self.ice_tab.setEnabled(False)
        self.ecosys_tab = nemo_ncml_tab_widget.Ncml_tab(unicode("Ecosystem").encode('utf-8'))
        self.ecosys_tab.setEnabled(False)
        self.grid_tab = nemo_ncml_tab_widget.Ncml_tab(unicode("Grid").encode('utf-8'))
        self.grid_tab.setEnabled(False)
                
        self.tabWidget.addTab(self.tracer_tab, unicode("Tracer").encode('utf-8'))
        self.tabWidget.addTab(self.dynamic_tab, unicode("Dynamics").encode('utf-8'))
        self.tabWidget.addTab(self.ice_tab, unicode("Ice").encode('utf-8'))
        self.tabWidget.addTab(self.ecosys_tab, unicode("Ecosystem").encode('utf-8')) # should be disabled
        self.tabWidget.addTab(self.grid_tab, unicode("Grid").encode('utf-8')) # should be disabled
        self.tabWidget.setMovable(False)
#        if self.tabWidget.widget(self.tabWidget.currentIndex()).isEnabled() is True:
        
#       self.connect(self.tabWidget, SIGNAL('currentChanged(int)'),self.enable_btn_update)
        self.tabWidget.currentChanged.connect(lambda: self.enable_btn_update(enable_btn))
        '''
        button bar
        '''
        go_btn = QtGui.QPushButton(unicode('Generate').encode('utf-8'))
        go_btn.setToolTip(unicode('Add all variable definitions before generating NcML file.').encode('utf-8'))
        cancel_btn = QtGui.QPushButton(unicode('Cancel').encode('utf-8'))
        enable_btn = QtGui.QPushButton(unicode('Enable Tab').encode('utf-8'))
        #layout button bar        
        btn_hBox = QtGui.QHBoxLayout(None)
        btn_hBox.setMargin(5)
        btn_hBox.setSpacing(10)
        btn_hBox.setAlignment(QtCore.Qt.AlignRight)
        btn_hBox.addWidget(enable_btn)
        btn_hBox.addWidget(cancel_btn)
        btn_hBox.addWidget(go_btn)
        
        go_btn.clicked.connect(self.generate)
        cancel_btn.clicked.connect(self.close)
        enable_btn.clicked.connect(lambda: self.enable_tab(enable_btn))
#       enable_btn.clicked.connect(self.enable_tab)
        
        '''
        Assemble the top layout container
        '''
        vbox.addWidget(top_grpBox)
        vbox.addWidget(self.tabWidget)
        vbox.addLayout(btn_hBox)
        
        #self.setLayout(grp_box)        
        self.setWindowIcon(QtGui.QIcon('/Users/jdha/anaconda/lib/python2.7/site-packages/pynemo-0.2-py2.7.egg/pynemo/gui/nemo_icon.png'))    #doesn't work       
        self.setWindowTitle(unicode("PyNEMO NcML Generator").encode('utf-8'))
        self.resize(650,300)
        
        #has to change the default focus to stop the output file QTextedit to trigger the widget in focus when enter is pressed.  Not sure why this happens???
        self.tabWidget.setFocus()
        #show the window
        self.show()
        
    
    '''
    file picker call back for output file input field
    '''
    @pyqtSlot()
    def get_fname(self):
        # When you call getOpenFileName, a file picker dialog is created
        # and if the user selects a file, it's path is returned, and if not
        # (ie, the user cancels the operation) None is returned
        fname = QtGui.QFileDialog.getSaveFileName(self, 'Select output file', '', selectedFilter='*.ncml')
        if fname:
            self.filename = fname #returns a QString
            self.top_outfile_name.setText(str(fname))
            #print 'the output file is set to : ' + self.filename
    '''
    output file text box call back handler
    '''
    @pyqtSlot()
    def get_fname_input(self):
        self.filename = self.top_outfile_name.text()
        #print 'the output file is manually set to : ' + self.filename
    '''
    call back to handle the generate button pressed
    '''
    @pyqtSlot()
    def enable_btn_update(self, enable_btn):
        if self.tabWidget.widget(self.tabWidget.currentIndex()).isEnabled() is True:
            enable_btn.setText(unicode('Disable Tab').encode('utf-8'))
        else:
            enable_btn.setText(unicode('Enable Tab').encode('utf-8'))
    '''
    call back to handle the generate button pressed
    '''
    @pyqtSlot()
    def enable_tab(self,enable_btn):
#   def enable_tab(self):
        #validate output file
        if self.tabWidget.widget(self.tabWidget.currentIndex()).isEnabled() is True:
            self.tabWidget.widget(self.tabWidget.currentIndex()).setEnabled(False)
            enable_btn.setText(unicode('Enable Tab').encode('utf-8'))
        else:
            self.tabWidget.widget(self.tabWidget.currentIndex()).setEnabled(True)
            enable_btn.setText(unicode('Disable Tab').encode('utf-8'))

    '''
    call back to handle the generate button pressed
    '''
    @pyqtSlot()
    def generate(self):
        #validate output file
        if self.filename is None or self.filename == "":
            if self.top_outfile_name.text() is None or self.top_outfile_name.text() == "":
                QtGui.QMessageBox.critical(self, unicode('Something is wrong').encode('utf-8'), unicode('No output file specified!').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
                return
            else:
                self.filename = self.top_outfile_name.text()
            
        if(os.path.exists(os.path.dirname(str(self.filename)))) == False:
            #if os.path.dirname(os.path.dirname(os.path.exists(os.path.normpath(str(self.filename))))) == False:
            QtGui.QMessageBox.critical(self, unicode('Something is wrong').encode('utf-8'), unicode('Invalid output directory!  Cannot generate file!').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
            #print 'invalid target directory!  Cannot generate.'
            return
            
                
        #validate if all the variables are defined, use the mandatory src field as a proxy
        # also need to check that the tab is active

        tabsList = []
        if self.tracer_tab.isEnabled() is True:
            if self.tracer_tab.votemper.src != ""  and \
               self.tracer_tab.vosaline.src != "" :
                tabsList.extend([self.tracer_tab.votemper, self.tracer_tab.vosaline])
            else:
                QtGui.QMessageBox.information(self, unicode('Something is wrong').encode('utf-8'), unicode('Not all the variables under the tracer tab have been defined!').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)


        if self.ice_tab.isEnabled() is True:
            if self.ice_tab.ileadfra.src != ""  and \
               self.ice_tab.iicethic.src != ""  and \
               self.ice_tab.isnowthi.src != "" :
                tabsList.extend([self.ice_tab.iicethic, self.ice_tab.ileadfra, self.ice_tab.isnowthi])
            else:
                QtGui.QMessageBox.information(self, unicode('Something is wrong').encode('utf-8'), unicode('Not all the variables under the ice tab have been defined!').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)

        if self.dynamic_tab.isEnabled() is True:
            if self.dynamic_tab.vozocrtx.src != ""  and \
               self.dynamic_tab.vozocrtx.src != ""  and \
               self.dynamic_tab.sossheig.src != "" :
                tabsList.extend([self.dynamic_tab.vozocrtx, self.dynamic_tab.vomecrty, self.dynamic_tab.sossheig])
            else:
                QtGui.QMessageBox.information(self, unicode('Something is wrong').encode('utf-8'), unicode('Not all the variables under the dynamics tab have been defined!').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)

        if self.grid_tab.isEnabled() is True:
            if self.grid_tab.gdept.src != ""    and \
               self.grid_tab.gdepw.src != ""    and \
               self.grid_tab.mbathy.src != ""   and \
               self.grid_tab.e3t.src != ""      and \
               self.grid_tab.e3u.src != ""      and \
               self.grid_tab.e3v.src != "" :
                tabsList.extend([self.grid_tab.gdept, self.grid_tab.gdepw, self.grid_tab.mbathy, self.grid_tab.e3t, self.grid_tab.e3u, self.grid_tab.e3v])
            else:
                QtGui.QMessageBox.information(self, unicode('Something is wrong').encode('utf-8'), unicode('Not all the variables under the grid tab have been defined!').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)

        try:
            self.generateNcML(tabsList) #go ahead and do it
        except:
            raise

        QtGui.QMessageBox.information(self, unicode('Success.').encode('utf-8'), unicode('NcML file generated.').encode('utf-8'), QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)

    '''
    Function to generates the NcML text and write it to the user defined output file
    '''
    def generateNcML(self, tabsList):
        #first open the default base file
        ns = '{http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2}'
        self.tree = self._parseNcml()
        self.root = self.tree.getroot()
        #create a netcdf element for each tab variable
        for tab in tabsList:
            netcdfE = ET.Element(ns+unicode('netcdf').encode('utf-8')) #src directory is converted to the correct format when added/
            if str(tab.src).startswith("http:") or str(tab.src).startswith("https:"):
                #Its url so use thredds crawler to get the urls
                urls = self.url_trawler(tab.src,str(tab.regex))
                aggE = ET.Element(ns+unicode('aggregation').encode('utf-8'), name=unicode(str(tab.name)).encode('utf-8'), type=unicode('joinExisting').encode('utf-8'), dimName=unicode('time_counter').encode('utf-8')) #tab.name already encoded                
                for nc_url in urls:
                    tcNetcdf = ET.Element(ns+unicode('netcdf').encode('utf-8'), location=unicode(str(nc_url)).encode('utf-8'))
                    aggE.append(tcNetcdf)
                netcdfE.append(aggE)
            else:
                scanE = ET.Element(ns+unicode('scan').encode('utf-8'), location=unicode(str(tab.src)).encode('utf-8'), regExp=unicode(str(tab.regex)).encode('utf-8'))
                if tab.subdirs == True:
                    scanE.set(unicode('subdirs').encode('utf-8'), unicode('true').encode('utf-8'))
                aggE = ET.Element(ns+unicode('aggregation').encode('utf-8'), name=unicode(str(tab.name)).encode('utf-8'), type=unicode('joinExisting').encode('utf-8'), dimName=unicode('time_counter').encode('utf-8')) #tab.name already encoded
                aggE.append(scanE)
                netcdfE.append(aggE)
            self.root[0].append(netcdfE)    #add the new netcdf element to the top aggregation 
            
            #deal with variable name change TODO put this into a loop?
            if tab.old_name is not None and tab.old_name != "":
                vname = unicode('variable').encode('utf-8')
                #v is None          
                if tab.name == unicode('temperature').encode('utf-8') and tab.old_name != unicode('votemper').encode('utf-8'):
                    v = ET.Element(ns+vname, name='votemper', orgName = str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('salinity').encode('utf-8') and tab.old_name != unicode('vosaline').encode('utf-8'):
                    v = ET.Element(ns+vname, name='vosaline', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('ice_thickness').encode('utf-8') and tab.old_name != unicode('iicethic').encode('utf-8'):
                    v = ET.Element(ns+vname, name='iicethic', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('leads_fraction').encode('utf-8') and tab.old_name != unicode('ileadfra').encode('utf-8'):
                    v = ET.Element(ns+vname, name='ileadfra', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('snow_thickness').encode('utf-8') and tab.old_name != unicode('isnowthi').encode('utf-8'):
                    v = ET.Element(ns+vname, name='isnowthi', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('zonal_velocity').encode('utf-8') and tab.old_name != unicode('vozocrtx').encode('utf-8'):
                    v = ET.Element(ns+vname, name='vozocrtx', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('meridian_velocity').encode('utf-8') and tab.old_name != unicode('vomecrty').encode('utf-8'):
                    v = ET.Element(ns+vname, name='vomecrty', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('sea_surface_height').encode('utf-8') and tab.old_name != unicode('sossheig').encode('utf-8'):
                    v = ET.Element(ns+vname, name='sossheig', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('depth_at_t_points').encode('utf-8') and tab.old_name != unicode('gdept').encode('utf-8'):
                    v = ET.Element(ns+vname, name='gdept', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('depth_at_w_points').encode('utf-8') and tab.old_name != unicode('gdepw').encode('utf-8'):
                    v = ET.Element(ns+vname, name='gdepw', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('number_of_wet_levels').encode('utf-8') and tab.old_name != unicode('mbathy').encode('utf-8'):
                    v = ET.Element(ns+vname, name='mbathy', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('vertical_scale_factors_at_t_points').encode('utf-8') and tab.old_name != unicode('e3t').encode('utf-8'):
                    v = ET.Element(ns+vname, name='e3t', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('vertical_scale_factors_at_u_points').encode('utf-8') and tab.old_name != unicode('e3u').encode('utf-8'):
                    v = ET.Element(ns+vname, name='e3u', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == unicode('vertical_scale_factors_at_v_points').encode('utf-8') and tab.old_name != unicode('e3v').encode('utf-8'):
                    v = ET.Element(ns+vname, name='e3v', orgName =  str(tab.old_name))
                    self.root.append(v)
                   
        #write ncml to file
        try:
            self.indent(self.root, 0)   #24Aug15 format the xml for pretty printing
            self.tree.write(self.filename, encoding='utf-8')
        except IOError as (errno, strerror):
            self.logger.error("I/O error({0}): {1}".format(errno, strerror))
        except:
            self.logger.error('Error generating ncml file')
            raise
             
    '''
    Function to retrieve the NcML file template
    '''
    def _parseNcml(self):
        try:
            parser = ET.XMLParser(encoding="utf-8")
            tree = ET.parse(self.baseFile, parser=parser)
            return tree
        except ET.ParseError, v:
                row, column = v.position
                print "error on row", row, "column", column, ":", v
                
    '''
    Function to format xml.  Based on code provided by http://effbot.org/zone/element-lib
    '''
    def indent(self, elem, level=0):
        i = "\n" + level*"  "
        if len(elem):
            if not elem.text or not elem.text.strip():
                elem.text = i + "  "
            if not elem.tail or not elem.tail.strip():
                elem.tail = i
            for elem in elem:
                self.indent(elem, level+1)
            if not elem.tail or not elem.tail.strip():
                elem.tail = i
        else:
            if level and (not elem.tail or not elem.tail.strip()):
                elem.tail = i     
    
    """
        This method trawls throught the url with a given expression and returns the
        list of urls that match the expression 
    """
    def url_trawler(self, url, expr):
        if url.endswith(".xml"):
            c = Crawl(url, select=[expr])
        elif url.endswith("/"): # we'll try and add catalog.xml as the user may have just provided a directory
            c = Crawl(url+"catalog.xml", select=[expr])
        else:                   # we'll try and add catalog.xml as the user may have just provided a directory
            c = Crawl(url+"/catalog.xml", select=[expr])
        urls = [s.get("url") for d in c.datasets for s in d.services if s.get("service").lower()=="opendap"]
        return urls