# @author dumitru.fostic@ypg.com
# @date   May , 2014
import MySQLdb
import warnings
from tlib.base.ExceptionHelper import TLibException


class ConnectionError(TLibException):
    def __init__(self, err_code, err_description):
        self.code, self.description = err_code, err_description
    def __str__(self):
        return 'Connection to DB FAILURE with error(/code: description/) /%d: %s/' % (self.code, self.description)


class AutoCommitError(TLibException):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return "Wrong argument '%s' passed in <get_autocommit_status> function. Valid args are str: 'local', 'global' or 'session'." % self.value


class QueryError(TLibException):
    def __init__(self, err_code, err_description):
        self.code, self.description = err_code, err_description
    def __str__(self):
        return 'query function FAILURE with error(/code: description/) /%d: %s/' % (self.code, self.description)


class ExecuteError(TLibException):
    def __init__(self, err_code, err_description):
        self.code, self.description = err_code, err_description
    def __str__(self):
        return 'execute function FAILURE with error (/code: description/) /%d: %s/' % (self.code, self.description)


class MysqlConnector(object):
    """
    Connects to MySQL DB, executes queries
    Autocommit is disabled by default, commit is done with commit function
    Warnings are turned into exceptions
    """
    def __init__(self, config_data):
        """
        Initialize DB connection. Turning warning into exceptions
        """
        self.db = None
        warnings.filterwarnings('error', category=MySQLdb.Warning)  # turns warnings into exceptions
        try:
            self.db = MySQLdb.connect(**config_data)
            self.cursor = self.db.cursor(MySQLdb.cursors.DictCursor)
            self.db.autocommit(False)   # in case of autocommit will change by default to ON in future
        except MySQLdb.Error, err:
            raise ConnectionError(err.args[0], err.args[1])

    def get_autocommit_status(self, var_type='local'):
        """
        returns tuple Autocommit status i.e. ({'Value': 'ON', 'Variable_name':'autocommit'},)
        """
        if not var_type in ['local', 'global', 'session']:
            raise AutoCommitError(var_type)

        status_query = "show %s variables where variable_name = 'autocommit'" % var_type
        return self.query_select(status_query)

    def query_select(self, query):
        """
        Send/execute SELECT queries, returns data from DB
        :param: str MySQL query
        :return: tuple with arguments dictionaries, each dictionary is a row of DB i.e. ({row_first}, {row_second}, ... ,{row_last})
        """
        try:
            self.cursor.execute(query)
            return self.cursor.fetchall()
        except MySQLdb.Error, err:
            raise QueryError(err.args[0], err.args[1])

    def query_execute(self, query):
        """
        Sends query to DB. Can be DELETE, UPDATE
        :param: str query
        :return: dict {DictCursor}
        """
        try:
            self.cursor.execute(query)
            return self.cursor
        except MySQLdb.Error, err:
            raise ExecuteError(err.args[0], err.args[1])

    def commit(self):
        """
        Commits all changes, returns Exception in case of failure
        """
        self.db.commit()

    def rollback(self):
        """
        Rolls back all commits, returns exception in case of failure
        """
        self.db.rollback()

    def close_cursor(self):
        """
        Close cursor. Connection to DB is still open, new cursor can be created
        """
        self.cursor.close()

    def close_db(self):
        """
        Close DB connection. All uncommitted changes are lost
        """
        self.db.close()

    def __del__(self):
        self.cursor.close()
        self.db.close()








