# -*- coding: utf-8 -*-
"""This code is part of Hydra Toolkit library

.. module:: lib.database.dbo.dbodriver
   :platform: Unix
   :synopsis: DBODriver abstract class
.. moduleauthor:: Petr Czaderna <pc@hydratk.org>

"""

from abc import ABCMeta, abstractmethod
import inspect
import sys
import os

class DBODriver(object):
    """Class DBODriver
    """
    
    __metaclass__ = ABCMeta
    
    _cursor         = None
    _dbcon          = None
    _result_as_dict = True
    _dsn            = None 
    _driver_options = {}
    _username       = None
    _password       = None

    @property
    def dbcon(self):      
        """ dbcon property getter """
          
        return self._dbcon 
    
    @property
    def cursor(self):
        """ cursor property getter """
        
        return self._cursor
    
    def __init__(self, dsn, username=None, password=None, driver_options={}, autoconnect=True):        
        """Class constructor
        
        Called when object is initialized
        
        Args:   
           dsn (str): dsn
           username (str): username
           password (str): password
           driver_options (dict): driver options
           autoconnect (bool): connect to DB within object creation
           
        Raises:
           exception: Exception
                
        """ 
                
        self._dsn            = dsn
        self._username       = username
        self._password       = password
        if len(driver_options) > 0:
            self._apply_driver_options(driver_options)
                    
        if self._parse_dsn(dsn) is not True:
            raise Exception("Error initialize database driver, dsn parse {0} error".format(dsn))
        
        try:
            if autoconnect == True:
                self.connect()
            
        except Exception as e:
            exc = DBODriverException(str(e))
            exc.parent_exc = e
            raise exc     
        
    @abstractmethod
    def _parse_dsn(self, dsn):
        """Method parses driver specific dsn string
                
        Args: 
           dsn (string): driver specific dsn format
                              
        Raises:
           exception: DBODriverException
                                
        """
        pass
     
    @abstractmethod
    def _apply_driver_options(self):
        pass
    
    def dict_factory(self, cursor, row):
        """Method transforms database row to dictionary        
                
        Args: 
           cursor (obj): database cursor
           row (obj): database row
                              
        Returns: 
           dict
                                
        """        
        
        d = {}
        for idx, col in enumerate(cursor.description):
            d[col[0]] = row[idx]
        return d
 
    @abstractmethod
    def connect(self):
        pass

    @abstractmethod
    def close(self):
        pass
    
    @abstractmethod
    def commit(self):
        pass
    
    @abstractmethod
    def error_code(self):
        pass
    
    @abstractmethod
    def error_info(self):
        pass
    
    @abstractmethod
    def qexec(self):
        pass
    
    @abstractmethod
    def get_attribute(self):
        pass    
    
    @abstractmethod
    def in_transaction(self):
        pass
    
    @abstractmethod
    def last_insert_id(self):
        pass
    
    @abstractmethod
    def prepare(self):
        pass
    
    @abstractmethod
    def query(self):
        pass
    
    @abstractmethod
    def quote(self):
        pass
    
    @abstractmethod
    def rollback(self):
        pass
    
    @abstractmethod
    def set_attribute(self):
        pass
    
    @abstractmethod
    def table_exists(self, table_name):
        pass
    
    @abstractmethod
    def database_exists(self):
        pass
    
    @abstractmethod
    def remove_database(self):
        pass
    

class DBODriverStatement(object):
    """Class DBODriverStatement
    """
    
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def bind_column(self):
        pass
    
    @abstractmethod
    def bind_param(self):
        pass
    
    @abstractmethod
    def bind_value(self):
        pass
    
    @abstractmethod
    def close_cursor(self):
        pass
    
    @abstractmethod
    def column_count(self):
        pass
    
    @abstractmethod
    def debug_dump_params(self):
        pass
    
    @abstractmethod
    def error_info(self):
        pass
    
    @abstractmethod
    def execute(self):
        pass
    
    @abstractmethod
    def fetch(self):
        pass
    
    @abstractmethod
    def fetch_all(self):
        pass
    
    @abstractmethod
    def fetch_column(self):
        pass
    
    @abstractmethod
    def fetch_object(self):
        pass
    
    @abstractmethod
    def get_attribute(self):
        pass
    
    @abstractmethod
    def get_column_meta(self):
        pass
    
    @abstractmethod
    def next_rowset(self):
        pass
    
    @abstractmethod
    def row_count(self):
        pass
    
    @abstractmethod
    def set_attribute(self):
        pass
    
    @abstractmethod
    def set_fetch_mode(self):
        pass
    

class DBODriverException(Exception):
    """Class DBODriverException
    """
    
    _error_info = {}
    _call_path  = None
    _message    = None
    _module     = None 
    _func       = None  
    _file       = None
    _line       = None
    _parent_exc = None
    
    @property
    def message(self):
        """ message propery getter """
        
        return self._message
    
    @property
    def call_path(self):
        """ call_path property getter """
        
        return self._call_path
        
    @property
    def file(self):
        """ file property getter """
        
        return self._file
    
    @property
    def line(self):
        """ line property getter """
        
        return self._line

    @property
    def func(self):
        """ func property getter """
        
        return self._func
    
    @property
    def module(self):
        """ module propery getter """
        
        return self._module    
        
    @property
    def parent_exc(self):
        """ parent_exc property getter, setter """
        
        return self._parent_exc
    
    @parent_exc.setter
    def parent_exc(self,exc):
        """ parent_exc property setter """
        
        self._parent_exc = exc
    
    def __init__(self, msg):
        """Class constructor
        
        Called when object is initialized
        
        Args:   
           msg (str): message
                
        """ 
                
        self._message   = msg
        trace           = self.get_trace(2)
        self._file      = trace['file']
        self._line      = trace['line']
        self._call_path = trace['call_path']
        self._func      = trace['func']
        self._module    = trace['module']
        
    def __str__(self):
        """Method overried __str__
        
        Args:  
           none 
           
        Returns:
           str
                
        """ 
                
        return repr(self._message)
    
    def get_trace(self, level):
        """Method gets traceback
        
        Args:   
           level (int): trace level
           
        Returns:
           dict: traceback
                
        """ 
                
        fname = sys._getframe(level).f_code.co_filename
        modname = os.path.basename(fname)
        modarg = modname.split('.')                   
        frm = inspect.stack()[1]
        mod = inspect.getmodule(frm[0])
        call_path = mod.__name__ if hasattr(mod,'__name__') else '__main__'        
        return {'file': sys._getframe(1).f_code.co_filename,
                'line' : sys._getframe(1).f_lineno,
                'module' : modarg[0],
                'func': sys._getframe(1).f_code.co_name,
                'call_path' : call_path  
                }  