# -*- coding: UTF-8 -*-
#    This file is part of arena.
#   
#    arena 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 3 of the License or, at your
#    option, any later version.
#
#    arena is distributed in the hope that it will be useful, but
#    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABLITY
#    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
#    License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with arena.  If not, see <http://www.gnu.org/licenses/>. 

## @file
#  This file defines a class that specializes ADataSource to apply to 
#  databases, as well as a class that represents data operations (such as 
#  queries) executed against a database.

from Library.Base.Events import ALoggingEventfulObject
from Library.DataSource.DataSources import ADataSource

## The events generated by ADataOperation
doEvents = {
  # Before the operation is executed
  "execute": None
}

## The events generated by ADatabase
dbEvents = {
  # Before a specific database is opened
  "open": None
}

## A basic operation performed against a database (i.e., a query or
#  command).
class ADataOperation(ALoggingEventfulObject):
    ## Construct a new data operation that will operate on the specified data
    #  source.
    #
    #  The constructor expects the following items to be provided in \p params:
    #  - \p DataSource -- a reference to the instance of  ADataSource on which 
    #                     the data operation is to take place.
    def __init__(Self, **params):
        ALoggingEventfulObject.__init__(Self, **params)
        
        if "DataSource" in Self._params:
            Self._Source = Self._params["DataSource"]
            del Self._params["DataSource"]
        
    ## Initializer
    def init(Self):
        result = ALoggingEventfulObject.init(Self)
        
        if result == True:
            # Establish private attributes
            # The data source against which the operation will execute
            Self._Source = None
            # The handle assigned to the operation
            Self._handle = None
            # The execution time of the operation
            Self._runtime = 0.0
            # The result code of the operation
            Self._result = 0
            # The message returned by the data source for the operation
            Self._message = ""
            
            # Update events generated by the object
            Self._events.update(doEvents)
            
        return result
        
    ## Execute the operation.
    #    
    #  This routine should be implemented by descendants of ADataOperation.
    #  It should return a status code to indicate whether the command or query
    #  was accepted and executed by the data source.  It should also store 
    #  this status code in the public 'result' attribute.  A status code of
    #  zero (0) should be used to indicate success.
    #
    #  This method generates the \p execute event.
    def execute(Self):
        result = 0
        
        # Generate the "execute" event
        Event = Self.generate("execute")
        
        if Event.handled():
            result = Event.result()
            del Event
            
        return result
        
    # Properties ---------------------------------------------------------------
    ## Retrieve the data source used by the operation.
    def Source(Self):
        return Self._Source
    
    ## Retrieve the handle assigned to the operation by the data source.   
    def handle(Self):
        return Self._handle
        
    ## Retrieve the amount of time elapsed while executing the operation.
    def runtime(Self):
        return Self._runtime
        
    ## Retrieve the result code returned by the data source after executing the 
    #  operation.
    def result(Self):
        return Self._result
        
    ## Retrieve the message generated by the data source while executing the
    #  operation, if any.
    def message(Self):
        return Self._message

## The abstract interface to a database.
class ADatabase(ADataSource):
    ## Initializer
    def init(Self):
        result = ADataSource.init(Self)
        
        if result:
            # Establish private attributes
            # Update the events generated by the class
            Self._events.update(dbEvents)
            
        return result
        
    ## Connect to the database.
    #    
    #  This routine should be implemented by descendant classes. A status
    #  code should be returned to indicate success or failure.  Zero (0) is
    #  assumed to mean that a connection was successfully made.
    #
    #  This routine expects that \p params will contain the following items:
    #  - \p password -- The password to use, if any, when connecting to the 
    #                   database.  This parameter may be omitted if the database
    #                   in question does not require a password for connections,
    #                   as is the case with sqlite databases.  This parameter
    #                   is kept separate from those passed to the constructor
    #                   in order to prevent it from being printed when a string
    #                   representation of ADatabase is created by calling
    #                   ADatabase.toString().
    def connect(Self, **params):
        result = -1
        
        # Generate the "connect" event
        Event = Self.generate("connect", params=params)
        
        if Event.handled():
            result = Event.result()
        
        del Event
            
        return result
        
    ## Open the specified database.
    #
    #  This method should be implemented by descendant classes.  It can be used
    #  when connected to a database provider to specify which database to use.  
    #  For certain database providers, such as sqlite, this method may do 
    #  nothing at all.
    def open(Self, databaseName, **params):
        result = -1
        
        Event = Self.generate("open", databaseName=databaseName, params=params)
        if Event.handled():
            result = Event.result()
            
        del Event
        
        return result
        
    ## Return the last message generated by the database.
    #
    #  Not all databases may support this method; if they do, however, 
    #  this method will return the last status or error message generated by
    #  the data source; otherwise, this routine should return \p None.
    def lastMessage(Self):
        return None      
