"""
:mod: Pfn

.. module: Pfn

:synopsis: pfn URI (un)parsing

.. moduleauthor:: Krzysztof.Ciba@NOSPAMgmail.com

"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

__RCSID__ = "$Id$"

# # imports
import os
# # from DIRAC
from DIRAC import S_OK, S_ERROR, gLogger
from six.moves.urllib import parse as urlparse


def pfnunparse(pfnDict, srmSpecific=True):
  """ Wrapper for backward compatibility
      Redirect either to the old hand made style of unparsing
      the pfn, which works for srm, or to the standard one
      which seems to work for the rest

      :param srmSpecific: use the srm specific parser (default True)
  """
  if srmSpecific:
    return srm_pfnunparse(pfnDict)
  return default_pfnunparse(pfnDict)


def srm_pfnunparse(pfnDict):
  """
  Create PFN URI from pfnDict

  :param dict pfnDict:
  """
  # # make sure all keys are in
  allDict = dict.fromkeys(["Protocol", "Host", "Port", "WSUrl", "Path", "FileName"], "")
  if not isinstance(pfnDict, dict):
    return S_ERROR("pfnunparse: wrong type for pfnDict argument, expected a dict, got %s" % type(pfnDict))
  allDict.update(pfnDict)
  pfnDict = allDict

  # # c
  # # /a/b/c
  filePath = os.path.normpath('/' + pfnDict["Path"] + '/' + pfnDict["FileName"]).replace('//', '/')

  # # host
  uri = pfnDict["Host"]
  if pfnDict["Host"]:
    if pfnDict["Port"]:
      # host:port
      uri = "%s:%s" % (pfnDict["Host"], pfnDict["Port"])
    if pfnDict["WSUrl"]:
      if "?" in pfnDict["WSUrl"] and "=" in pfnDict["WSUrl"]:
        # host/wsurl
        # host:port/wsurl
        uri = "%s%s" % (uri, pfnDict["WSUrl"])
      else:
        # host/wsurl
        # host:port/wsurl
        uri = "%s%s?=" % (uri, pfnDict["WSUrl"])

  if pfnDict["Protocol"]:
    if uri:
      # proto://host
      # proto://host:port
      # proto://host:port/wsurl
      uri = "%s://%s" % (pfnDict["Protocol"], uri)
    else:
      # proto:
      uri = "%s:" % pfnDict["Protocol"]

  pfn = "%s%s" % (uri, filePath)

  # c
  # /a/b/c
  # proto:/a/b/c
  # proto://host/a/b/c
  # proto://host:port/a/b/c
  # proto://host:port/wsurl/a/b/c
  return S_OK(pfn)


def default_pfnunparse(pfnDict):
  """
  Create PFN URI from pfnDict

  :param dict pfnDict:
  """

  try:
    if not isinstance(pfnDict, dict):
      return S_ERROR("pfnunparse: wrong type for pfnDict argument, expected a dict, got %s" % type(pfnDict))
    allDict = dict.fromkeys(['Protocol', 'Host', 'Port', 'Path', 'FileName', 'Options'], '')
    allDict.update(pfnDict)

    scheme = allDict['Protocol']

    netloc = allDict['Host']
    if allDict['Port']:
      netloc += ':%s' % allDict['Port']

    path = os.path.join(allDict['Path'], allDict['FileName'])
    query = allDict['Options']

    pr = urlparse.ParseResult(scheme=scheme, netloc=netloc, path=path, params='', query=query, fragment='')

    pfn = pr.geturl()

    return S_OK(pfn)

  except Exception as e:  # pylint: disable=broad-except
    errStr = "Pfn.default_pfnunparse: Exception while unparsing pfn: %s" % pfnDict
    gLogger.exception(errStr, lException=e)
    return S_ERROR(errStr)


def pfnparse(pfn, srmSpecific=True):
  """ Wrapper for backward compatibility
      Redirect either to the old hand made style of parsing
      the pfn, which works for srm, or to the standard one
      which seems to work for the rest

      :param srmSpecific: use the srm specific parser (default True)
  """
  if srmSpecific:
    return srm_pfnparse(pfn)
  return default_pfnparse(pfn)


def srm_pfnparse(pfn):
  """
  Parse pfn and save all bits of information into dictionary

  :param str pfn: pfn string
  """
  if not pfn:
    return S_ERROR("wrong 'pfn' argument value in function call, expected non-empty string, got %s" % str(pfn))
  pfnDict = dict.fromkeys(["Protocol", "Host", "Port", "WSUrl", "Path", "FileName"], "")
  try:
    if ":" not in pfn:
      # pfn = /a/b/c
      pfnDict["Path"] = os.path.dirname(pfn)
      pfnDict["FileName"] = os.path.basename(pfn)
    else:
      # pfn = protocol:/a/b/c
      # pfn = protocol://host/a/b/c
      # pfn = protocol://host:port/a/b/c
      # pfn = protocol://host:port/wsurl?=/a/b/c
      pfnDict["Protocol"] = pfn[0:pfn.index(":")]
      # # remove protocol:
      pfn = pfn[len(pfnDict["Protocol"]):]
      # # remove :// or :
      pfn = pfn[3:] if pfn.startswith("://") else pfn[1:]
      if pfn.startswith("/"):
        # # /a/b/c
        pfnDict["Path"] = os.path.dirname(pfn)
        pfnDict["FileName"] = os.path.basename(pfn)
      else:
        # # host/a/b/c
        # # host:port/a/b/c
        # # host:port/wsurl?=/a/b/c
        if ":" not in pfn:
          # # host/a/b/c
          pfnDict["Host"] = pfn[0:pfn.index("/")]
          pfn = pfn[len(pfnDict["Host"]):]
          pfnDict["Path"] = os.path.dirname(pfn)
          pfnDict["FileName"] = os.path.basename(pfn)
        else:
          # # host:port/a/b/c
          # # host:port/wsurl?=/a/b/c
          pfnDict["Host"] = pfn[0:pfn.index(":")]
          # # port/a/b/c
          # # port/wsurl?=/a/b/c
          pfn = pfn[len(pfnDict["Host"]) + 1:]
          pfnDict["Port"] = pfn[0:pfn.index("/")]
          # # /a/b/c
          # # /wsurl?=/a/b/c
          pfn = pfn[len(pfnDict["Port"]):]
          WSUrl = pfn.find("?")
          WSUrlEnd = pfn.find("=")
          if WSUrl == -1 and WSUrlEnd == -1:
            # # /a/b/c
            pfnDict["Path"] = os.path.dirname(pfn)
            pfnDict["FileName"] = os.path.basename(pfn)
          else:
            # # /wsurl?blah=/a/b/c
            pfnDict["WSUrl"] = pfn[0:WSUrlEnd + 1]
            # # /a/b/c
            pfn = pfn[len(pfnDict["WSUrl"]):]
            pfnDict["Path"] = os.path.dirname(pfn)
            pfnDict["FileName"] = os.path.basename(pfn)
    return S_OK(pfnDict)
  except Exception:  # pylint: disable=broad-except
    errStr = "Pfn.srm_pfnparse: Exception while parsing pfn: " + str(pfn)
    gLogger.exception(errStr)
    return S_ERROR(errStr)


def default_pfnparse(pfn):
  """
    Parse pfn and save all bits of information into dictionary

  :param str pfn: pfn string
  """
  if not pfn:
    return S_ERROR("wrong 'pfn' argument value in function call, expected non-empty string, got %s" % str(pfn))
  pfnDict = dict.fromkeys(["Protocol", "Host", "Port", "WSUrl", "Path", "FileName"], "")
  try:

    parse = urlparse.urlparse(pfn)
    pfnDict['Protocol'] = parse.scheme
    if ':' in parse.netloc:
      pfnDict['Host'], pfnDict['Port'] = parse.netloc.split(':')
    else:
      pfnDict['Host'] = parse.netloc
    pfnDict["Path"] = os.path.dirname(parse.path)
    pfnDict["FileName"] = os.path.basename(parse.path)
    if parse.query:
      pfnDict['Options'] = parse.query
    return S_OK(pfnDict)
  except Exception as e:  # pylint: disable=broad-except
    errStr = "Pfn.default_pfnparse: Exception while parsing pfn: " + str(pfn)
    gLogger.exception(errStr, lException=e)
    return S_ERROR(errStr)
