#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import subprocess
import os
import wx
import threading
import time
from   stomamisc import *

class MountManDialog(wx.Dialog):
    ID_MOUNT = wx.NewId()
    HINT = 'to (un)mount - double click or mark coloured item and press (Un)Mount button'
    
    def __init__(self, parent, config, writeToLogwin):        
        """   """
        wx.Dialog.__init__(self, parent, -1, TITLE + ' - Manage mount fs',                           
                           style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        self.Bind(wx.EVT_CLOSE, self.onButtonClose)
        self.Bind(wx.EVT_RIGHT_DOWN, self.onContext)
        self.parent = parent
        self.config = config
        self.writeToLogwin = writeToLogwin
        self.mountman = MountMan()
        self.thread = None
        self.selindex = -1
        
        sizer = wx.BoxSizer(wx.VERTICAL)        
        sb = wx.StaticBox(self, -1, ' Mount infos ')
        row1 = wx.StaticBoxSizer(sb, wx.VERTICAL)
        self.lc = wx.ListCtrl(self, -1, style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES | wx.BORDER_SUNKEN)
        self.lc.InsertColumn(0, 'File system / device', width = 240)
        self.lc.InsertColumn(1, 'Mount point', width = 140)
        self.lc.InsertColumn(2, 'Type', width = 90)
        self.lc.InsertColumn(3, 'Status', width = 100)
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onActivated)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onSelected)
        self.lc.Bind(wx.EVT_RIGHT_DOWN, self.onContext)
        self.updateMountTab()
        row1.Add(self.lc, 1, wx.GROW | wx.ALL, GAP)
        self.msgtext = wx.StaticText(self, -1, self.HINT)
        font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
        self.msgtext.SetFont(font)
        row1.Add(self.msgtext, 0, wx.GROW | wx.ALL, GAP)
        #
        self.butmount = wx.Button(self, self.ID_MOUNT, '(Un)&Mount')
        self.butclose = wx.Button(self, wx.ID_CLOSE)
        self.Bind(wx.EVT_BUTTON, self.onButtonClose, id = wx.ID_CLOSE)
        self.Bind(wx.EVT_BUTTON, self.onButtonMount, id = self.ID_MOUNT)
        row2 = wx.BoxSizer(wx.HORIZONTAL)
        row2.Add(self.butmount, 1, wx.GROW | wx.RIGHT, GAP)
        row2.Add(self.butclose, 1, wx.GROW | wx.LEFT, GAP)
        
        sizer.Add(row1, 1, wx.GROW | LRT, BORDER)
        sizer.Add(row2, 0, wx.GROW | wx.ALL, BORDER)        
        self.SetSizer(sizer)
        self.SetMinSize(wx.Size(640, 400))
        self.Fit()
        self.Centre()
        self.initContextMenu()
    #-------------------------------------------------------------------------------------------

    def initContextMenu(self):
        self.popupID0 = wx.NewId()  
        self.popupID1 = wx.NewId()          
        
        self.Bind(wx.EVT_MENU, self.onButtonMount, id = self.popupID0)
        self.Bind(wx.EVT_MENU, self.onButtonClose, id = self.popupID1)
    #-------------------------------------------------------------------------------------------

    def onContext(self, event):
        """ context menu handler """
        menu = wx.Menu()
        menu.Append(self.popupID0, '(Un)Mount device')
        menu.AppendSeparator()
        menu.Append(self.popupID1, 'Close dialog')
        self.PopupMenu(menu)
        menu.Destroy()
    #-------------------------------------------------------------------------------------------

    def updateMountTab(self):  
        self.fstab, self.notmounted = self.mountman.getMountInfos()
        self.lc.DeleteAllItems()
        row = 0
        for item in self.fstab:
            self.lc.InsertStringItem(row, item[0])
            self.lc.SetStringItem(row, 1, item[1])
            self.lc.SetStringItem(row, 2, item[2])
            if item in self.notmounted:
                self.lc.SetStringItem(row, 3, 'not mounted')
                self.lc.SetItemTextColour(row, wx.RED)                
            else:
                self.lc.SetStringItem(row, 3, 'mounted')
                if item in self.config['Misc']['mountedbysm']:
                    self.lc.SetItemTextColour(row, wx.GREEN)
            row += 1
        mtabmounted = self.mountman.getMtabNotInFstab()
        for item in mtabmounted:
            self.lc.InsertStringItem(row, item[0])
            self.lc.SetStringItem(row, 1, item[1])
            self.lc.SetStringItem(row, 2, item[2])
            self.lc.SetStringItem(row, 3, 'mounted')
            row += 1            
    #-------------------------------------------------------------------------------------------

    def onDummyHandler(self, event):
        wx.SafeYield(self)
    #-------------------------------------------------------------------------------------------

    def onSelected(self, event):
        self.selindex = event.GetIndex()
    #-------------------------------------------------------------------------------------------
        
    def onActivated(self, event):
        self.selindex = event.GetIndex()
        self.onButtonMount()
    #-------------------------------------------------------------------------------------------        

    def callDoMount(self, entry):
        self.retval = self.mountman.doMount(entry)
    #-------------------------------------------------------------------------------------------        

    def callDoUnmount(self, entry):
        self.retval = self.mountman.doUnmount(entry)
    #-------------------------------------------------------------------------------------------        
        
    def onButtonMount(self, event = None):
        if self.selindex == -1:
            dlg = wx.MessageDialog(self, "no entry to (un)mount selected!", TITLE + ' - (un)mount fs', wx.OK | wx.ICON_INFORMATION)
            dlg.ShowModal()
            return
        
        device = self.lc.GetItem(self.selindex).GetText()

        entry = None
        for item in self.notmounted:
            if device == item[0]:
                entry = item
                mount = True
                break
            
        if entry == None:
            for item in self.config['Misc']['mountedbysm']:
                if device == item[0]:
                    entry = item
                    mount = False
                    break
                
            if entry == None:    
                dlg = wx.MessageDialog(self, "this fs is not (un)mountable by StoreMan!", TITLE + ' - (Un)Mount fs', wx.OK | wx.ICON_INFORMATION)
                dlg.ShowModal()
                return

        if entry[2] not in ['nfs', 'cifs']:
            dlg = wx.MessageDialog(self, "sorry - this fs type is not supported by StoreMan!", TITLE + ' - (Un)Mount fs', wx.OK | wx.ICON_INFORMATION)
            dlg.ShowModal()
            return                
        
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onDummyHandler)
        self.Bind(wx.EVT_CLOSE, self.onDummyHandler)
        self.butmount.Enable(False)
        self.butclose.Enable(False)
        self.retval = -2
        if mount:
            self.writeToLogwin("(Un)Mount fs - try to mount '%s' (%s)" % (entry[1], entry[0]))
            wx.SafeYield(self)
            self.thread = threading.Thread(target = self.callDoMount, args = (entry,))
            self.thread.start()
            timecount = 90
            while self.retval == -2:
                self.msgtext.SetLabel('waiting %d remaining seconds for system mount command to complete ... ' % (timecount))
                wx.SafeYield(self)
                time.sleep(1)
                timecount -= 1
                if not timecount:
                    del self.thread
                    wx.SafeYield(self)
                    break
            self.msgtext.SetLabel(self.HINT)
            
            if self.retval == 0:
                self.writeToLogwin("(Un)Mount fs - '%s' (%s) successfully mounted" % (entry[1], entry[0]))
                self.config['Misc']['mountedbysm'].append(entry)
            else:
                dlg = wx.MessageDialog(self, "Could not mount the selected item!", TITLE + ' - (Un)Mount fs',  wx.OK | wx.ICON_ERROR)
                dlg.ShowModal()
        else:
            self.writeToLogwin("(Un)Mount fs - try to unmount '%s' (%s)" % (entry[0], entry[1]))
            wx.SafeYield(self)
            self.thread = threading.Thread(target = self.callDoUnmount, args = (entry,))
            self.thread.start()
            timecount = 90
            while self.retval == -2:
                self.msgtext.SetLabel('waiting %d remaining seconds for system umount command to complete ... ' % (timecount))
                wx.SafeYield(self)
                time.sleep(1)
                timecount -= 1
                if not timecount:
                    del self.thread
                    wx.SafeYield(self)
                    break
            self.msgtext.SetLabel(self.HINT)
            
            if self.retval == 0:
                self.writeToLogwin("(Un)Mount fs - '%s' (%s) successfully unmounted" % (entry[1], entry[0]))
                self.config['Misc']['mountedbysm'].remove(entry)
            else:
                dlg = wx.MessageDialog(self, "Could not unmout selected item!", TITLE + ' - (Un)Mount fs',  wx.OK | wx.ICON_ERROR)
                dlg.ShowModal()           
            
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.onActivated)
        self.Bind(wx.EVT_CLOSE, self.onButtonClose)
        self.butmount.Enable(True)
        self.butclose.Enable(True)
        self.updateMountTab()
    #-------------------------------------------------------------------------------------------
  
    def onButtonClose(self, event):
        try:
            del self.thread
            wx.SafeYield(self)
        except:
            pass
        self.Destroy()
# - class MountManDialog(wx.Dialog):---------------------------------------------------------------


class MountMan(object):

    def __init__(self):
        self.getMountInfos()
    #-------------------------------------------------------------------------------------------
        
    def readMtab(self):
        """ key == mountpoint
            entry [0]    [1]        [2]
                  device mountpoint type  """
        self.mtab = {}
        try:
            lines = [line.strip('\n').split() for line in open ('/etc/mtab', 'r').readlines()]
            for line in lines:
                self.mtab[line[1]] = line
        except:
            pass
    #-------------------------------------------------------------------------------------------
        
    def readFstab(self):
        """ [0]        [1]        [2]
            filesystem mountpoint type options dump pass """
        self.fstab = []
        try:
            lines = [line.strip('\n').split() for line in open ('/etc/fstab', 'r').readlines()]
            for entry in lines:
                if entry and not entry[0].startswith('#'):
                    newentry = []
                    for item in entry:
                        if len(item):
                            newentry.append(item.strip())
                    if newentry[0].startswith('UUID') and newentry[1] != 'none':
                        newentry[0] = self.mtab[newentry[1]][0]
                        
                    fs = newentry[1]
                    if fs not in ['/proc', 'none']:
                        self.fstab.append(newentry)
        except:
            pass
    #-------------------------------------------------------------------------------------------
    
    def notMounted(self):
        self.notmounted = []
        for entry in self.fstab:
            if not self.mtab.has_key(entry[1]) and entry[1] != 'none' and not entry[1].startswith('/media/cdrom'):
                self.notmounted.append(entry)
    #-------------------------------------------------------------------------------------------

    def getMtabNotInFstab(self):
        mtab_mps = self.mtab.keys()
        fstab_mps = []
        for entry in self.fstab:
            fstab_mps.append(entry[1])
            
        retlist = []
        for mp in mtab_mps:
            if mp not in fstab_mps and self.mtab[mp][2] == 'vfat':
                retlist.append(self.mtab[mp])
                
        return retlist    
    #-------------------------------------------------------------------------------------------

    def getMountInfos(self):
        self.readMtab()
        self.readFstab()
        self.notMounted()
        return self.fstab, self.notmounted
    #-------------------------------------------------------------------------------------------
    
    def doMount(self, item):
        """doMount(item) """
        try:
            if item[2] == 'cifs':
                username = ''
                password = ''
                splititem = item[3].split(',')
                for subitem in splititem:
                    if subitem.startswith('user'): username = subitem
                    if subitem.startswith('pass'): password = subitem
                p = subprocess.Popen(['mount.' + item[2], item[0], item[1], '-o %s,%s' % (username, password)])
                status = p.wait()
            else:
                p = subprocess.Popen(['mount.' + item[2], item[0], item[1]])
                status = p.wait()
            return status
        except:
            return -1
    #-------------------------------------------------------------------------------------------

    def doUnmount(self, item):
        """doMount(item) """
        try:
            p = subprocess.Popen(['umount.' + item[2], item[1]]) 
            status = p.wait()
            return status
        except:
            return -1
# - class MountMan(object):---------------------------------------------------------------------

if __name__ == '__main__':
    mountman = MountMan()
    fstab, notmounted = mountman.getMountInfos()
    
    print 'fstab'
    for item in fstab:
        print item
    
    print 'notmounted'
    for item in notmounted:
        print item
    

