#!/usr/bin/env python

# Copyright 2008, 2009 Ronan DANIELLOU
# Copyright 2008, 2009 Onen (onen.om@free.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import sys
try:
 	import pygtk
  	pygtk.require("2.0")
except:
  	pass
try:
	import gtk
  	import gtk.glade
except:
	sys.exit(1)
import logging
import gobject
import os.path

import openbmap.logger

class openBmapGTK:
    """This is a GTK frontend for the openBmap logger."""

    def __init__(self):

        #Set the Glade file
        possibleGladeFile = None
        pathsTried = ''
        for path in [sys.path[0], os.path.split(sys.path[0])[0]]:
            for p in ['', os.path.join('share', 'openBmap')]:
                possibleGladeFile = os.path.join(path, p, 'Main.glade')
                pathsTried += '\n' + possibleGladeFile
                if os.path.exists(possibleGladeFile):
                    print 'Try loading %s glade file.' % possibleGladeFile
                    break
                else:
                    possibleGladeFile = None
            if possibleGladeFile:
                break

        if not possibleGladeFile:
            print 'No graphical interface file found! Have tried: %s' % pathsTried
            sys.exit(-1)
        else:
            del pathsTried

        self.wTree = gtk.glade.XML(possibleGladeFile)
		
        #Get the Main Window, and connect the "destroy" event
        self.window = self.wTree.get_widget("windowMain")
        self._windowMenu = self.wTree.get_widget('windowMenu')

        if (self.window):
            self.window.connect("destroy", self.exit_obm)
            
        self._gsmLabel = self.wTree.get_widget('displayGsm')
        self._gsmLabel2 = self.wTree.get_widget('displayCidStrength')
        self._gpsPositionLabel = self.wTree.get_widget('displayGpsPosition')
        self._gpsAltitudeLabel = self.wTree.get_widget('displayGpsAlt')
        self._dops = self.wTree.get_widget('displayDops')
        self._speed = self.wTree.get_widget('displaySpeed')
        self._stopLogging = self.wTree.get_widget('buttonStopLogging')
        self._startLogging = self.wTree.get_widget('buttonGenerateLog')

        self._gpsViewWithFix = self.wTree.get_widget('vboxGPSWithFix')
        self._gpsViewNoFix = self.wTree.get_widget('tableGPSNoFix')

        self._mccLabel = self.wTree.get_widget('labelMCCValue')
        self._mncLabel = self.wTree.get_widget('labelMNCValue')
        self._lacLabel = self.wTree.get_widget('labelLACValue')
        self._cidLabel = self.wTree.get_widget('labelCIDValue')
        self._ssLabel = self.wTree.get_widget('labelCellSignalStrengthValue')
        self._actType = self.wTree.get_widget('labelAccessTypeValue')
        self._gsmServingLabelList = [self._mccLabel,
                                     self._mncLabel,
                                     self._lacLabel,
                                     self._cidLabel,
                                     self._ssLabel,
                                     self._actType]
        
        self._latitudeLabel = self.wTree.get_widget('labelLatitudeValue')
        self._longitudeLabel = self.wTree.get_widget('labelLongitudeValue')
        self._altitudeLabel = self.wTree.get_widget('labelAltitudeValue')
        self._speedLabel = self.wTree.get_widget('labelSpeedValue')
        self._hpvDopsLabel = self.wTree.get_widget('labelHPVDopsValue')
        self._gpsLabelList = [self._latitudeLabel,
                              self._longitudeLabel,
                              self._altitudeLabel,
                              self._speedLabel,
                              self._hpvDopsLabel]
        
        self._nbServSinceStartLabel = self.wTree.get_widget('labelNbServSinceStart')
        self._nbServSinceLaunchLabel = self.wTree.get_widget('labelNbServSinceLaunch')
        self._nbNeigSinceStartLabel = self.wTree.get_widget('labelNbNeigSinceStart')
        self._nbNeigSinceLaunchLabel = self.wTree.get_widget('labelNbNeigSinceLaunch')
        self._nbNeigCurrentLabel = self.wTree.get_widget('labelNbNeigCurrent')

        self._LoggingStatusLabel = self.wTree.get_widget('labelLoggingStatus')

        #Create our dictionary and connect it
        dic = { "NextWindow" : self.next_window,
               "StopLogging" : self.stop_logging,
               "StartLogging" : self.start_logging,
               "Upload" : self.upload,
               "ShowMenu" : self.show_menu_window,
               "ShowMainWindow" : self.show_main_window }
        self.wTree.signal_autoconnect(dic)
        
        self._plsWait = gtk.MessageDialog(self.window,
                                    gtk.DIALOG_MODAL,
                                    gtk.MESSAGE_INFO,
                                    gtk.BUTTONS_NONE,
                                    'Stopping the logger. Please wait...')
        
        self._obmlogger = openbmap.logger.ObmLogger()
        self._obmlogger.register(self)
        self._obmlogger.init_openBmap()
        
        context = self._cidLabel.get_pango_context()
        font = context.get_font_description()
        self._biggerLabels = [self._cidLabel, self._speedLabel, self._hpvDopsLabel]
        self._biggerLabels2 = [self._hpvDopsLabel, self._nbServSinceStartLabel,
                               self._nbServSinceLaunchLabel,
                               self._nbNeigSinceStartLabel, self._nbNeigSinceLaunchLabel,
                               self._nbNeigCurrentLabel,
                               self._LoggingStatusLabel]
        font.set_size(int(font.get_size() * 3))
        for l in self._biggerLabels:
            l.modify_font(font)
        font.set_size(int(font.get_size() *2/3))
        for l in self._biggerLabels2:
            l.modify_font(font)

    def next_window(self, widget):
        """Cycle through the windows."""
        #TODO: not yet implemented: Exit right now
        # timeout_add schedules the first call at the soonest in 'timeout' seconds
        self.exit_obm(widget)
        gobject.timeout_add_seconds(2, self.exit_obm, widget)
        
    def show_menu_window(self, widget):
        self._windowMenu.show()
        logging.debug('GUI: shows menu window.')
        self.window.hide()
        logging.debug('GUI: hides main window.')

    def show_main_window(self, widget):
        self.window.show()
        logging.debug('GUI: shows main window.')
        self._windowMenu.hide()
        logging.debug('GUI: hides the menu window.')

    def stop_logging(self, widget):
        self._plsWait.text = 'Stopping the logger. Please wait...'
        self._plsWait.show_all()
        while gtk.events_pending():
            gtk.main_iteration(False)
        self._obmlogger.stop_logging()
        
    def exit_obm(self, widget):
        logging.info('Exiting on request of the user.')
        if self._obmlogger.is_logging():
            self.stop_logging(widget)
            logging.debug('Still logging, wait...')
            # to keep being called by timeout_add
            return True
        else:
            self._obmlogger.exit_openBmap()
            gobject.idle_add( logging.info, 'Exiting...' )
            gobject.idle_add(gtk.main_quit, )
        
    def start_logging(self, widget):
        self._obmlogger.start_logging()
        self.show_main_window(widget)
        
    def notify(self):
        """This method will be called by the observable we are registered too upon change."""
        logging.debug('View notified for changes.')
        (valid, servingCell, neighbourCells) = self._obmlogger.get_gsm_data()
        if valid:
            self._gsmLabel.set_text("%s / %s / %s" % servingCell[:3])
            self._gsmLabel2.set_text("%s / %s / %s" % servingCell[3:6])
            
            self._mccLabel.set_text('%s' % servingCell[0])
            self._mncLabel.set_text('%s' % servingCell[1])
            self._lacLabel.set_text('%s' % servingCell[2])
            self._cidLabel.set_text('%s' % servingCell[3])
            self._ssLabel.set_text('%s' % servingCell[4])
            self._actType.set_text('%s' % servingCell[5])

            self._nbNeigCurrentLabel.set_text('%s' % len(neighbourCells))
            (nbServCurrent,
             nbNeighCurrent,
             nbServTotal,
             nbNeighTotal) = self._obmlogger.get_seen_cells_stats()
             # we update only if logging is on. This way, when we stop,
             # we still read meaningful information: the number of cells
             # seen during last start/stop.
            if self._obmlogger.is_logging():
                self._nbServSinceStartLabel.set_text('%s' % nbServCurrent)
                self._nbNeigSinceStartLabel.set_text('%s' % nbNeighCurrent)
            self._nbServSinceLaunchLabel.set_text('%s' % nbServTotal)
            self._nbNeigSinceLaunchLabel.set_text('%s' % nbNeighTotal)
        else:
            for w in [self._gsmLabel, self._gsmLabel2]:
                w.set_text("N/A")
            for w in self._gsmServingLabelList:
                w.set_text('N/A')
            self._nbNeigCurrentLabel.set_text('N/A')

        (valid, tstamp, lat, lng, alt, pdop, hdop, vdop, speed, heading) = self._obmlogger.get_gps_data()
        if valid:
            self._gpsPositionLabel.set_text('%s / %s' % (lat, lng))
            self._gpsAltitudeLabel.set_text('%s' % alt)
            self._dops.set_text('%s/%s/%s' % (pdop, hdop, vdop))
            self._speed.set_text('%s' % speed)
            
            self._latitudeLabel.set_text('%s' % lat)
            self._longitudeLabel.set_text('%s' % lng)
            self._altitudeLabel.set_text('%i' % alt)
            self._speedLabel.set_text('%.2f' % speed)
            self._hpvDopsLabel.set_text('%s\n%s\n%s' % (pdop, hdop, vdop))
        else:
            for w in [self._gpsPositionLabel, self._gpsAltitudeLabel, self._dops,
                       self._speed]:
                w.set_text('N/A')
            
            for w in self._gpsLabelList:
                w.set_text('N/A')

            # quick improvement for notifying the user that: the GPS is active, and what it is waiting
            # for in order to display GPS data
            self._gpsPositionLabel.set_text('GPS is on')
            self._gpsAltitudeLabel.set_text('Waiting for a 3D fix')

        if self._gpsViewWithFix.get_property('visible') != valid:
            self._gpsViewWithFix.set_property('visible', valid)
            self._gpsViewNoFix.set_property('visible', not valid)
            self._gpsViewWithFix.parent.show()
            logging.debug('GUI: GPS view switched because fix status has changed.')
        else:
            logging.debug('GUI: No need to switch GPS graphical interface view.')

        isLogging = self._obmlogger.is_logging()
        self._startLogging.set_sensitive(not isLogging)
        self._stopLogging.set_sensitive(isLogging)
        if not isLogging:
            self._plsWait.hide()
            self._LoggingStatusLabel.set_text('OFF')
        else:
            self._LoggingStatusLabel.set_text('ON')

        # a sanity check
        if (not self.window.get_property('visible')) and (not self._windowMenu.get_property('visible')):
            logging.error('GUI: both windows are set to invisible! Shows main window.')
            self.show_main_window(None)

    def upload(self, widget):
        """Upload logs to OpenBmap database."""
        if not self.check_credentials():
            logging.debug('Upload aborted because credentials not validated.')
            return

        plsWait = gtk.MessageDialog(self.window,
                                    gtk.DIALOG_MODAL,
                                    gtk.MESSAGE_INFO,
                                    gtk.BUTTONS_NONE,
                                    'Trying to upload logs. Please wait...')
        plsWait.show_all()
        while gtk.events_pending():
            gtk.main_iteration(False)
        (uploaded, totalFilesUploaded, totalFilesToUpload) = self._obmlogger.send_logs()
        plsWait.destroy()
        if uploaded:
            plsWait = gtk.MessageDialog(self.window,
                                        gtk.DIALOG_MODAL,
                                        gtk.MESSAGE_INFO,
                                        gtk.BUTTONS_OK,
                                        '%i out of %i log files uploaded.\nThanks for your contribution!' %
                                        (totalFilesUploaded, totalFilesToUpload))
        else:
            plsWait = gtk.MessageDialog(self.window,
                                        gtk.DIALOG_MODAL,
                                        gtk.MESSAGE_ERROR,
                                        gtk.BUTTONS_OK,
                                        'Upload failed.\nSee application log for details.')
        plsWait.run()
        plsWait.destroy()

        plsWait = gtk.MessageDialog(self.window,
                                    gtk.DIALOG_MODAL,
                                    gtk.MESSAGE_QUESTION,
                                    gtk.BUTTONS_YES_NO,
                                    'Do you want to delete all logs located in the Processed folder?')
        if plsWait.run() == gtk.RESPONSE_YES:
            plsWait.destroy()
            plsWait = gtk.MessageDialog(self.window,
                                        gtk.DIALOG_MODAL,
                                        gtk.MESSAGE_INFO,
                                        gtk.BUTTONS_OK,
                                        '%i processed log files deleted.' %
                                        self._obmlogger.delete_processed_logs())
            plsWait.run()
        plsWait.destroy()

    def check_credentials(self):
        """Returns True if credentials are validated, False otherwise"""
        dialog = gtk.Dialog(title='Upload?',
                            flags=gtk.DIALOG_MODAL,
                            parent=self.window,
                            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                     gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
        result = False
        labelLogin = gtk.Label('Login:')
        entryLogin = gtk.Entry(0)
        login, password = self._obmlogger.get_credentials()
        entryLogin.set_text(login)
        labelPassword = gtk.Label('Password:')
        entryPassword = gtk.Entry(0)
        entryPassword.set_text(password)
        widgetsTuple = labelLogin, entryLogin, labelPassword, entryPassword
        for w in widgetsTuple:
            dialog.vbox.add(w)
        dialog.show_all()

        if dialog.run() == gtk.RESPONSE_ACCEPT:
            newLogin = entryLogin.get_text()
            newPassword = entryPassword.get_text()
            if (login != newLogin) or (password != newPassword):
                logging.debug('Credentials modified (\'%s\', \'%s\') -> (\'%s\', \'%s\').' % \
                            (login, password, newLogin, newPassword))
                logging.info('Credentials changed, saving...')
                self._obmlogger.set_credentials(newLogin, newPassword)
            else:
                logging.debug('Credentials unchanged.')
            result = True
        dialog.destroy()
        return result

if __name__ == "__main__":
	hwg = openBmapGTK()
	gtk.main()
