import subprocess
import sys
import select
import time

####################################
#
# mdb - wrapper for Solaris mdb utility (based on subprocess)
#
# (c) Sergey Klyaus, Tune-IT, 2012
# myaut@tune-it.ru
#
# 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; either version 2
# of the License, or (at your option) any later version.
#
####################################

TIMEOUT = 0.1
TIMEOUT_THRESHOLD = 10

class MDBException(Exception):
    pass

class MDB:
    def __init__(self, cmd, interactive=True):
        self.mdb_proc = subprocess.Popen(cmd, shell=True, 
                    stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)
        
        self.interactive = interactive
        self.logging = None
    
    def log(self, why, what):
        if self.logging:
            print >> sys.stderr, "%s [%s] %s" % (why, str(self.logging), repr(what))
    
    def send(self, mdb_cmd, wrap_hack=False):
        # MDB hack: redirect output to cat
        # This disables MDB_IOB_AUTOWRAP, so mdb does'nt wrap a reply
        # (may happen if vpath name is too long)
        # but may be waste of performance
        if wrap_hack:
            mdb_cmd += ' !cat\n'
        else:
            mdb_cmd += '\n'
        
        self.mdb_proc.stdin.write(mdb_cmd)
        
        self.log('SEND', mdb_cmd)
        
        if not self.interactive:
            self.mdb_proc.stdin.close()

    def is_dead(self):
        return self.last_line == '' and self.mdb_proc.poll() != None
    
    def wait_for_io(self):
        stderr_fd = self.mdb_proc.stderr.fileno()
        stdout_fd = self.mdb_proc.stdout.fileno()
        timeout = 0
        
        while timeout < TIMEOUT_THRESHOLD:
            sel = select.select([stderr_fd, stdout_fd], [], [], TIMEOUT)[0]
            
            if stderr_fd in sel:
                error = self.mdb_proc.stderr.readline()
                
                if len(error) > 0:
                    if not 'warning' in error:
                        #ignore warnings
                        raise MDBException(error)
                    else:
                        print >> sys.stderr, error
                    
            
            if stdout_fd in sel:
                break
                
            timeout += TIMEOUT
        else:
             raise MDBException("Timeout exceeded")
    
    def readline(self):
        #Not so fast!
        self.wait_for_io()
        
        line = self.mdb_proc.stdout.readline()
        self.log('RECV', line)
        
        self.last_line = line.strip()
        
        return self.last_line
    
    def readfield(self):
        self.readline()
        
        l = self.last_line.split("=", 1)
        value = l[1].lstrip()
        
        return value
    
    def readstring(self):
        string = self.readfield()
        
        if '"' in string:
            string = string.split(" ", 1)[1]
        else:
            string = '???'
            
        return string
    
    def __del__(self):
        if self.interactive:
            self.mdb_proc.stdin.close()
        
        self.mdb_proc.wait()
        
    # Generator
    
    def __iter__(self):
        if self.interactive:
            raise Exception, 'Interactive MDB processes couldn\'t be generators'
        
        return self
    
    def next(self): 
        line = self.readline()
    
        if self.is_dead():
            raise StopIteration
        
        return line

