"""
    FileCatalogClientBase is a base class for the clients of file catalog-like
    services built within the DIRAC framework.

    The class contains variables defining lists of implemented catalog methods
    READ_METHODS
    WRITE_METHODS
    NO_LFN_METHODS
    ADMIN_METHODS

    Those lists must be complemented in the derived classes to include specific
    implemented methods.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__RCSID__ = "$Id$"

from DIRAC.Core.Base.Client import Client, createClient
from DIRAC.Core.Utilities.ReturnValues import S_OK
from DIRAC.Resources.Catalog.Utilities import checkCatalogArguments


@createClient('DataManagement/FileCatalog')
class FileCatalogClientBase(Client):
  """ Client code to the DIRAC File Catalogue
  """

  # Default mandatory methods having all-allowing implementation in the base class
  READ_METHODS = ['hasAccess', 'exists', 'getPathPermissions']

  # List of methods that can be complemented in the derived classes
  WRITE_METHODS = []
  NO_LFN_METHODS = []
  ADMIN_METHODS = []

  def __init__(self, url=None, **kwargs):
    """ Constructor function.
    """
    super(FileCatalogClientBase, self).__init__(**kwargs)
    if url:
      self.serverURL = url
    self.available = False

  def isOK(self, timeout=120):
    """ Check that the service is OK
    """
    if not self.available:
      rpcClient = self._getRPC(timeout=timeout)
      res = rpcClient.isOK()
      if not res['OK']:
        self.available = False
      else:
        self.available = True
    return S_OK(self.available)

#######################################################################################
#  The following methods must be implemented in derived classes
#######################################################################################

  @classmethod
  def getInterfaceMethods(cls):
    """ Get the methods implemented by the File Catalog client

    :return tuple: ( read_methods_list, write_methods_list, nolfn_methods_list )
    """
    return (cls.READ_METHODS,
            cls.WRITE_METHODS,
            cls.NO_LFN_METHODS)

  @classmethod
  def hasCatalogMethod(cls, methodName):
    """ Check of a method with the given name is implemented

    :param str methodName: the name of the method to check
    :return: boolean Flag True if the method is implemented
    """
    return methodName in (cls.READ_METHODS + cls.WRITE_METHODS + cls.NO_LFN_METHODS)

########################################################################################
#  Some default methods to always return successfully if we do not care
########################################################################################
  @checkCatalogArguments
  def hasAccess(self, paths, _opType):
    """ Default method: returns True for all paths and all actions

      :param lfn paths: has to be formatted this way :
                  { lfn : { se1 : pfn1, se2 : pfn2, ...}, ...}
      :param str _opType: dummy string

      :return: { successful : { lfn : [ ses ] } : failed : { lfn : { se : msg } } }
    """
    lfns = list(paths)
    return S_OK({'Failed': {}, 'Successful': dict.fromkeys(lfns, True)})

  @checkCatalogArguments
  def exists(self, lfns):
    """ Default method: returns False for all paths

      :param lfn paths: has to be formatted this way :
                  { lfn : { se1 : pfn1, se2 : pfn2, ...}, ...}

      :return: { successful : { lfn : [ ses ] } : failed : { lfn : { se : msg } } }

    """
    return S_OK({'Failed': {}, 'Successful': dict.fromkeys(lfns, False)})

  @checkCatalogArguments
  def getPathPermissions(self, lfns):
    """ Default method: returns Read & Write permission for all the paths

      :param lfn paths: has to be formatted this way :
                  { lfn : { se1 : pfn1, se2 : pfn2, ...}, ...}

      :return: { successful : { lfn : [ ses ] } : failed : { lfn : { se : msg } } }

    """
    return S_OK({'Failed': {},
                 'Successful': dict.fromkeys(lfns, {'Write': True, "Read": True})})
