# -*- 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 expands upon the ADataRow class to allow the retrieval and
#  modification of a user record for arena.

from Library.DataSource.Queries import ADataRow
from Library.IO.Logging.Logs import AMessageWithTimeStamp
from Library.UI.Channels import chMetaItemsSeparator, \
    chMetaKeysValuesSeparator
from Library.String.Utilities import String

## The error message used when no data source is available
suNoDataSource = "no data source is available"
## The error message used when failing to update a user record
suUserUpdateError = "Failed to update information for user \"{}\" because: {}"
## The error message used when failing to locate a user record
suUserRetrievalError = ("Failed to retrieve information for user \"{}\" "
                        "because: {}")

## This class represents an error that can occur when retrieving or
#  updating user information.
class AUserError(AMessageWithTimeStamp):
    pass

## This class represents a user record for arena.
class AUser(ADataRow):
    ## Construct a new user instance.
    def __init__(Self, fieldsValues=None, **params):
        ADataRow.__init__(Self, fieldsValues, **params)
        

    ## Initializer
    def init(Self):
        result = ADataRow.init(Self)

        if result:
            # Establish private attributes
            # User metadata
            Self._meta = {}                   
            
            # Establish public attributes
            # Whether or not changes can be made to user data
            Self.readOnly = False

        return result

    ## Flush changes made to the user record back to the data source.
    def flush(Self, *criteria):
        cri = list(criteria)
        
        if len(Self._changes) > 0:
            cri.append(
                ("{field:id} = {}", Self["id"])
            )
            
            # Remove the "id" field since it is immutable and the data source
            # will complain if we attempt to update it
            del Self._fieldsValues["id"]

            # Condense metadata
            Self._fieldsValues["meta"] = \
              String.implode(Self._meta, chMetaItemsSeparator, 
                  chMetaKeysValuesSeparator)
                  
            ADataRow.flush(Self, *cri)            

    ## Fetch the user record from the data source.
    def fetch(Self, userName):
        result = Self._Source.fetch(Self._category,
            criteria = [
                ("{field:userName} = {}", userName)
            ]
        )
        
        for row in result:
            Self._fieldsValues.update(row)
        
        # Expand user metadata
        if ("meta" in Self) and (len(Self["meta"]) > 0):
            Self._meta = String.explode(Self["meta"], dict, 
                chMetaItemsSeparator, chMetaKeysValuesSeparator)
                
        return result   
                      
    # Properties ---------------------------------------------------------------
    ## Retrieve or set the dictionary of channel metadata, or a single item 
    #  from the metadata
    def meta(Self, *keys, **keysValues):
        # If no extra parameters are specified, return the entire dictionary
        if len(keys) == 0:
            return Self._meta
            
        # If a key is specified, return just that key            
        if keys[0] in Self._meta:
            return Self._meta[keys[0]]
            
        # If keys and values are specified, then set those keys and values 
        # within the meta dictionary            
        if len(keysValues) > 0:
            Self._meta.update(keysValues)
            return
            
        raise KeyError(keyName)
        
    ## Return the name of the user
    def name(Self):
        if "userName" in Self:
            return Self["userName"]
            
        else:
            return ADataRow.name(Self)
