'''
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
from . 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(str('Output filename').encode('utf-8'))
        self.top_outfile_name = QtGui.QLineEdit()    #location is pre-defined
        self.top_outfile_name.setToolTip(str('Define output file').encode('utf-8'))
        self.top_outfile_name.returnPressed.connect(self.get_fname_input)
        
        top_outfile_button = QtGui.QPushButton(str('Select file').encode('utf-8'))
        top_outfile_button.clicked.connect(self.get_fname)
        
        top_grpBox = QtGui.QGroupBox(str('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(str("Tracer").encode('utf-8'))
        self.tracer_tab.setEnabled(False)
        self.dynamic_tab = nemo_ncml_tab_widget.Ncml_tab(str("Dynamics").encode('utf-8'))
        self.dynamic_tab.setEnabled(False)
        self.ice_tab = nemo_ncml_tab_widget.Ncml_tab(str("Ice").encode('utf-8'))
        self.ice_tab.setEnabled(False)
        self.ecosys_tab = nemo_ncml_tab_widget.Ncml_tab(str("Ecosystem").encode('utf-8'))
        self.ecosys_tab.setEnabled(False)
        self.grid_tab = nemo_ncml_tab_widget.Ncml_tab(str("Grid").encode('utf-8'))
        self.grid_tab.setEnabled(False)
                
        self.tabWidget.addTab(self.tracer_tab, str("Tracer").encode('utf-8'))
        self.tabWidget.addTab(self.dynamic_tab, str("Dynamics").encode('utf-8'))
        self.tabWidget.addTab(self.ice_tab, str("Ice").encode('utf-8'))
        self.tabWidget.addTab(self.ecosys_tab, str("Ecosystem").encode('utf-8')) # should be disabled
        self.tabWidget.addTab(self.grid_tab, str("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(str('Generate').encode('utf-8'))
        go_btn.setToolTip(str('Add all variable definitions before generating NcML file.').encode('utf-8'))
        cancel_btn = QtGui.QPushButton(str('Cancel').encode('utf-8'))
        enable_btn = QtGui.QPushButton(str('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(str("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(str('Disable Tab').encode('utf-8'))
        else:
            enable_btn.setText(str('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(str('Enable Tab').encode('utf-8'))
        else:
            self.tabWidget.widget(self.tabWidget.currentIndex()).setEnabled(True)
            enable_btn.setText(str('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, str('Something is wrong').encode('utf-8'), str('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, str('Something is wrong').encode('utf-8'), str('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, str('Something is wrong').encode('utf-8'), str('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, str('Something is wrong').encode('utf-8'), str('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, str('Something is wrong').encode('utf-8'), str('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, str('Something is wrong').encode('utf-8'), str('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, str('Success.').encode('utf-8'), str('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+str('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+str('aggregation').encode('utf-8'), name=str(str(tab.name)).encode('utf-8'), type=str('joinExisting').encode('utf-8'), dimName=str('time_counter').encode('utf-8')) #tab.name already encoded                
                for nc_url in urls:
                    tcNetcdf = ET.Element(ns+str('netcdf').encode('utf-8'), location=str(str(nc_url)).encode('utf-8'))
                    aggE.append(tcNetcdf)
                netcdfE.append(aggE)
            else:
                scanE = ET.Element(ns+str('scan').encode('utf-8'), location=str(str(tab.src)).encode('utf-8'), regExp=str(str(tab.regex)).encode('utf-8'))
                if tab.subdirs == True:
                    scanE.set(str('subdirs').encode('utf-8'), str('true').encode('utf-8'))
                aggE = ET.Element(ns+str('aggregation').encode('utf-8'), name=str(str(tab.name)).encode('utf-8'), type=str('joinExisting').encode('utf-8'), dimName=str('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 = str('variable').encode('utf-8')
                #v is None          
                if tab.name == str('temperature').encode('utf-8') and tab.old_name != str('votemper').encode('utf-8'):
                    v = ET.Element(ns+vname, name='votemper', orgName = str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('salinity').encode('utf-8') and tab.old_name != str('vosaline').encode('utf-8'):
                    v = ET.Element(ns+vname, name='vosaline', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('ice_thickness').encode('utf-8') and tab.old_name != str('iicethic').encode('utf-8'):
                    v = ET.Element(ns+vname, name='iicethic', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('leads_fraction').encode('utf-8') and tab.old_name != str('ileadfra').encode('utf-8'):
                    v = ET.Element(ns+vname, name='ileadfra', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('snow_thickness').encode('utf-8') and tab.old_name != str('isnowthi').encode('utf-8'):
                    v = ET.Element(ns+vname, name='isnowthi', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('zonal_velocity').encode('utf-8') and tab.old_name != str('vozocrtx').encode('utf-8'):
                    v = ET.Element(ns+vname, name='vozocrtx', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('meridian_velocity').encode('utf-8') and tab.old_name != str('vomecrty').encode('utf-8'):
                    v = ET.Element(ns+vname, name='vomecrty', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('sea_surface_height').encode('utf-8') and tab.old_name != str('sossheig').encode('utf-8'):
                    v = ET.Element(ns+vname, name='sossheig', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('depth_at_t_points').encode('utf-8') and tab.old_name != str('gdept').encode('utf-8'):
                    v = ET.Element(ns+vname, name='gdept', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('depth_at_w_points').encode('utf-8') and tab.old_name != str('gdepw').encode('utf-8'):
                    v = ET.Element(ns+vname, name='gdepw', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('number_of_wet_levels').encode('utf-8') and tab.old_name != str('mbathy').encode('utf-8'):
                    v = ET.Element(ns+vname, name='mbathy', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('vertical_scale_factors_at_t_points').encode('utf-8') and tab.old_name != str('e3t').encode('utf-8'):
                    v = ET.Element(ns+vname, name='e3t', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('vertical_scale_factors_at_u_points').encode('utf-8') and tab.old_name != str('e3u').encode('utf-8'):
                    v = ET.Element(ns+vname, name='e3u', orgName =  str(tab.old_name))
                    self.root.append(v)
                elif tab.name == str('vertical_scale_factors_at_v_points').encode('utf-8') and tab.old_name != str('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 xxx_todo_changeme:
            (errno, strerror) = xxx_todo_changeme.args
            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 as 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