#-*- coding: utf-8 -*-

# Copyright 2008-2012 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import os
import re
import subprocess
import time

class NoneProgressBar:
    """Abstract class of progress bar. It do nothing."""
    def __init__(self,title,dialog=None):
        pass

    def openDialog(self,title,max=None):
        """Create dialog with progress bar, prepare date"""
        pass

    def shutdownDialog(self):
        """Destroy dialog"""
        pass

    def setValue(self,value):
        """Set value of progress bar relative of maximum"""
        pass

    def setMaximum(self,max):
        """Set maximum value of progress bar"""
        pass

    def setTitle(self,title):
        """Set title of progress dialog"""
        pass

class GProgressBar(NoneProgressBar):
    """GProgressBar uses Xdialog program for display progressbar."""
    def __init__(self,title,xdialog=None):
        self.title = title 
        self.bgPID = 0
        self.value = 0
        self.pipein = None
        if xdialog == None:
            self.openDialog(self.title)

    def openDialog(self,title,max=None):
        if max != None:
            self.setMaximum(max)
        
        title = re.sub("<[^>]+>", "", title)
        self.title = title
        if os.system('which Xdialog &>/dev/null') == 0:
            pipe = subprocess.Popen(
                '/usr/bin/Xdialog --title="" --progress "%s" 6 80'\
                                    %(self.title),
                                    stdin=subprocess.PIPE,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,
                                    close_fds=True,
                                    env=os.environ, shell=True)
            self.pipein = pipe.stdin
            self._set(1)
            pipe.stdout.close()
            pipe.stderr.close()

    def shutdownDialog(self):
        '''Принудительно уничтожить процесс dialog'''
        if self.pipein:
            self._set(100)
            time.sleep(0.1)
            self.pipein.close()
            self.pipein = None

    def _set(self,value):
        if self.pipein:
            try:
                self.pipein.write("%02d\n"%value)
                self.pipein.flush()
            except:
                pass

    def setValue(self,value):
        '''Установить текущее значения для прогресса'''
        if self.pipein and value <= self.max:
            progress_value = int(value / self.kmax)
            if progress_value > int(self.value / self.kmax) and progress_value < 100:
                self._set(progress_value)
            self.value = value

    def setMaximum(self,max):
        '''Установить максимальное значения для прогресса'''
        self.max = max
        self.kmax = (max / 100.0) or 1.0

    def setTitle(self,title):
        '''Установить описания прогресса'''
        pass 

class KProgressBar(NoneProgressBar):
    """KProgressBar uses kdialog program and dbus for display progressbar."""

    suffixSet = 'org.freedesktop.DBus.Properties.Set \
org.kde.kdialog.ProgressDialog'
    execenv = {"HOME":"/root"}
    max = 100
    kdialog = None
    label = None
    def __init__(self,title,kdialog=None):
        self.title = title
        self.value = 0
        if kdialog is None:
            self.openDialog(self.title)

    def openDialog(self,title,max=None):
        if max != None:
            self.max = max
        self.title = title
        if os.system('which kdialog >/dev/null') == 0:
            self.label ="LOGINKDIALOG=%d" % os.getpid()
            env = {}
            env.update(os.environ.items() + self.execenv.items() +\
                       [tuple(self.label.split("="))])
            pipe = subprocess.Popen('/usr/bin/kdialog --progressbar "%s" %d'\
                                    %(" "*(len(title)+20), self.max),
                                    stdin=subprocess.PIPE,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,
                                    close_fds=True,
                                    env=env, shell=True)
            pipe.stdin.close()
            # wait for terminate kdialog, which say dbus dialog id
            if pipe.poll() is None:
                # ожидание в 5 сек
                for t in range(500):
                    time.sleep(0.01)
                    if pipe.poll() != None:
                        break

            # waiting is ok
            if pipe.poll() == 0:
                self.kdialog = pipe.stdout.readline().strip()
                while not "org.kde.kdialog" in self.kdialog:
                    s = pipe.stdout.readline()
                    # if bad result of kdialog then shutdown dialog
                    if s == "":
                        pipe.stdout.close()
                        pipe.stderr.close()
                        self.shutdownDialog()
                        break
                    self.kdialog = s.strip()
                self.setTitle(self.title)
                pipe.stdout.close()
                pipe.stderr.close()
            # waiting is failed
            else:
                pipe.stdout.close()
                pipe.stderr.close()
                self.shutdownDialog()

    def shutdownDialog(self):
        '''Принудительно уничтожить процесс kdialog'''
        self.kdialog = None
        pipe = subprocess.Popen("/bin/ps axeo pid,cmd",
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                close_fds=True,
                                env=os.environ, shell=True)
        if self.label != None:
            for s in pipe.stdout.readlines():
                if self.label in s:
                    try:
                        os.kill( int(s.split()[0]), 9 )
                    except (OSError,ValueError):
                        pass
            self.label = None

    def setValue(self,value):
        '''Установить текущее значения для прогресса'''
        if self.kdialog and value <= self.max:
            env = ""
            if self.execenv:
                env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\
                               self.execenv)) + " "
            oldperc = int(self.value / self.kmax)
            newperc = int(value / self.kmax)
            if newperc > oldperc:
                os.system(env + '/usr/bin/qdbus %s %s value %d >/dev/null'\
                          %(self.kdialog,self.suffixSet, newperc))
                self.value = value

    def setMaximum(self,max):
        '''Установить максимальное значения для прогресса'''
        if self.kdialog and self.max == 0 and max != 0:
            env = ""
            if self.execenv:
                env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\
                               self.execenv)) + " "
            os.system(env + '/usr/bin/qdbus %s %s maximum %d >/dev/null'\
                      %(self.kdialog,self.suffixSet, 100))
        self.max = max
        self.kmax = (max / 100.0) or 1.0


    def setTitle(self,title):
        '''Установить описания прогресса'''
        self.title = title
        if self.kdialog:
            env = ""
            if self.execenv:
                env = " ".join(map(lambda x: '%s="%s"'%(x[0],x[1]),\
                               self.execenv)) + " "
            os.system(env + '/usr/bin/qdbus %s setLabelText "%s" >/dev/null'\
                      %(self.kdialog,self.title))

def ProgressBar(*args,**kwarg):
    """Return instance of object for progress bar"""
    if os.system('which kdialog &>/dev/null') == 0:
        return KProgressBar(*args,**kwarg)
    elif os.system('which Xdialog &>/dev/null') == 0:
        return GProgressBar(*args,**kwarg)
    return NoneProgressBar(*args,**kwarg)
