###############################################################################
# (c) Copyright 2018 CERN                                                     #
#                                                                             #
# This software is distributed under the terms of the GNU General Public      #
# Licence version 3 (GPL Version 3), copied verbatim in the file "LICENSE".   #
#                                                                             #
# In applying this licence, CERN does not waive the privileges and immunities #
# granted to it by virtue of its status as an Intergovernmental Organization  #
# or submit itself to any jurisdiction.                                       #
###############################################################################

__author__ = "Marco Clemencic <marco.clemencic@cern.ch>"

import os
import sys

assert sys.version_info >= (2, 6), "Python >= 2.6 is required"


def _cmakeStr(txt):
    return " ".join(
        '"{}"'.format(entry.replace('"', '\\"')) for entry in str(txt).split(os.pathsep)
    )


_CMTPROJECTPATH_FMT_SH = (
    'export CMTPROJECTPATH="{0}' '${{CMTPROJECTPATH:+:${{CMTPROJECTPATH}}}}"\n'
)
_CMTPROJECTPATH_FMTS = {
    "csh": """if ( $?CMTPROJECTPATH ) then
  setenv CMTPROJECTPATH "{0}:${{CMTPROJECTPATH}}"
else
  setenv CMTPROJECTPATH "{0}"
endif
""",
    "sh": _CMTPROJECTPATH_FMT_SH,
}


class SearchPathEntry:
    def __init__(self, path):
        self.path = path

    def __str__(self):
        return str(self.path)

    def toCMake(self):
        return f"list(INSERT CMAKE_PREFIX_PATH 0 {_cmakeStr(self)})\n"

    def toCMT(self, shell="sh"):
        # FIXME: should we escape quotes?
        return _CMTPROJECTPATH_FMTS[shell].format(self)

    def __repr__(self):
        return f"{self.__class__.__name__}({self.path!r})"


class EnvSearchPathEntry(SearchPathEntry):
    def __init__(self, envname, default=None):
        if default is None and envname not in os.environ:
            raise ValueError(f"environment variable {envname} not defined")
        self.envname = envname
        self.default = default

    def __str__(self):
        return str(os.environ.get(self.envname, self.default))

    def toCMake(self):
        return (
            f"if(NOT ENV{{{self.envname}}})\n"
            "  # use a default value\n"
            f"  set(ENV{{{self.envname}}} {_cmakeStr(self)})\n"
            "endif()\n"
            f'list(INSERT CMAKE_PREFIX_PATH 0 "$ENV{{{self.envname}}}")\n'
        )

    def toCMT(self, shell="sh"):
        if self.envname == "CMAKE_PREFIX_PATH":
            return ""  # ignore CMAKE_PREFIX_PATH for CMT builds
        if shell == "csh":
            fmt = (
                "if ( ! $?{0} ) then\n"
                "  # use a default value\n"
                '  setenv {0} "{1}"\n'
                "endif\n"
            )
        else:
            fmt = (
                'if [ -z "${0}" ] ; then\n'
                "  # use a default value\n"
                '  export {0}="{1}"\n'
                "fi\n"
            )
        # FIXME: should we escape quotes?
        return fmt.format(self.envname, self) + _CMTPROJECTPATH_FMTS[shell].format(
            "${" + self.envname + "}"
        )

    def __repr__(self):
        return f"{self.__class__.__name__}({self.envname!r}, {str(self)!r})"


def _toEntry(entry):
    """ensure that the entry is a SearchPathEntry"""
    if isinstance(entry, SearchPathEntry):
        return entry
    return SearchPathEntry(entry)


class SearchPath:
    def __init__(self, entries):
        self.entries = list(entries)

    def __iter__(self):
        from itertools import chain

        return filter(
            None,
            chain.from_iterable(str(entry).split(os.pathsep) for entry in self.entries),
        )

    def insert(self, index, value):
        return self.entries.insert(index, value)

    def append(self, value):
        return self.entries.append(value)

    def extend(self, values):
        return self.entries.extend(values)

    def __setitem__(self, index, value):
        if isinstance(value, SearchPath):
            value = value.entries
        return self.entries.__setitem__(index, value)

    def __getitem__(self, index):
        if isinstance(index, slice):
            return self.__class__(self.entries[index])
        else:
            return self.entries[index]

    def __str__(self):
        return os.pathsep.join(self)

    def __nonzero__(self):
        return bool(self.entries)

    def __add__(self, other):
        if isinstance(other, SearchPath):
            other = other.entries
        return SearchPath(self.entries + other)

    def toCMake(self):
        # we need to add them in reverse order because the 'prepend'
        return "\n".join(_toEntry(entry).toCMake() for entry in self.entries[::-1])

    def toCMT(self, shell="sh"):
        # we need to add them in reverse order because the 'prepend'
        return "\n".join(_toEntry(entry).toCMT(shell) for entry in self.entries[::-1])

    def __repr__(self):
        return f"{self.__class__.__name__}({self.entries!r})"

    def __len__(self):
        return len(self.entries)


def _defaultPath():
    """
    Return the default search path, based on the variables CMAKE_PREFIX_PATH
    and CMTPROJECTPATH.
    """
    env_vars = ["CMAKE_PREFIX_PATH", "CMTPROJECTPATH"]
    path = []
    for name in env_vars:
        try:
            path.append(EnvSearchPathEntry(name, ""))
        except ValueError:
            pass  # ignore undefined variables
    path.append(
        EnvSearchPathEntry("LHCBPROJECTPATH", default="/cvmfs/lhcb.cern.ch/lib/lhcb")
    )
    return path


path = SearchPath(_defaultPath())


class Error(RuntimeError):
    """
    Base class for LbRun exceptions.
    """
