# -*- 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 manages a list of groups to which a
#  specific user may belong.

from Plugins.DataSource.DataLists import ADataList
from Library.IO.Logging.Logs import AMessageWithTimeStamp

## The error message used when no data source is provided to the group list
#  instance
glNoDataSource = "no data source was provided"

## The error message used when failing to retrieve the group list
glErrorRetrievingList = ("failed to retrieve the list of groups to which \"{}\""
                         " belongs because: {}")
## The error message used when failing to append to the group list
glErrorAppendingToList = "failed to add user \"{}\" to group \"{}\""
## The error message used when failing to delete from the group list
glErrorDeletingFromList = "failed to remove user \"{}\" from group \"{}\""

## This class represents an error that may occur while retrieving or
#  updating group membership information.
class AGroupError(AMessageWithTimeStamp):
    pass

## This class represents a list of groups to which a specific user may
#  belong.
class AGroupList(ADataList):
    ## Constructor
    #
    #  In addition to the parameters expected by ADataList.__init__(), this
    #  routine expects that \p params will contain the following items:
    #  - \p category -- The category (or table) within the data source from
    #                   which the list will draw.
    #  - \p user -- The name of the user for which the list will be constructed.
    def __init__(Self, **params):
        ADataList.__init__(Self, **params)
        
        if "category" in Self._params:
            Self._category = Self._params["category"]
            del Self._params["category"]
            
        if "user" in Self._params:
            Self._user = Self._params["user"]
            del Self._params["user"]
            
    ## Initializer
    def init(Self):
        result = ADataList.init(Self)

        if result:
            # Establish private properties
            # The name of the table where group information is stored
            Self._category = None
            # The name of the user that belongs to the groups in the list
            Self._user = None

        return result

    ## Fetch the list of groups to which the specified user belongs.
    def fetch(Self):
        result = 0
        
        Event = Self.generate("fetch")
        if Event.handled():
            result = Event.result()
            del Event
            return result
            
        del Event
        
        if Self._user is not None:
            try:
                Rows = Self._Source.fetch(Self._category,
                    fieldNames = (
                        "id", "groupName"
                    ),
                    criteria = [
                        ("{field:userName} = {}", Self._user)
                    ]
                )
                
                # Fetch the list of groups
                for Row in Rows:
                    Self._items[Row["groupName"]] = Row["id"]
                    
            except Exception, Err:
                Self.error(AGroupError(glErrorRetrievingList,
                    Self._user, str(Err)))
                    
            result = len(Self._items)
            
        return result

    ## Add the user to the specified group.
    def append(Self, groupName):
        result = -1
        
        Event = Self.generate("append", item=groupName)
        if Event.handled():
            result = Event.result()
            del Event
            return result
            
        del Event
        
        # If the group is not already specified for the user, then add it
        if groupName in Self("items") == False:
            result = Self._Source.insert(Self._category,
                fieldsValues = {
                    "userName": Self._user,
                    "groupName": groupName
                }
            )
            
            if result > 0:
                Self._items[groupName] = result

            else:
                Self.error(AGroupError(glErrorAppendingToList, 
                    Self._user, groupName))

        return result

    ## Remove the user from the specified group.
    def remove(Self, groupName):
        result = -1
        
        Event = Self.generate("remove", item=groupName)
        if Event.handled():
            result = Event.result()
            del Event
            return result
            
        del Event

        # Only remove groups that already exist in the list
        if groupName in Self._items:
            result = Self._Source.delete(Self._category,
                criteria = [
                    ("{field:id} = {}", Self._items[groupName])
                ]
            )
            
            if result >= 0:
                del Self._items[groupName]
                
            else:
                Self.error(AGroupError(glErrorDeletingFromList,
                    Self._user, groupName))

        return result

    # Properties ---------------------------------------------------------------
    ## Retrieve the name of the category in the data source used by the group
    #  list
    def category(Self):
        return Self._category

    ## Retrieve the name of the user for whom the group list applies.
    def user(Self):
        return Self._user



