"""
 *
 * This file is part of rasdaman community.
 *
 * Rasdaman community 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.
 *
 * Rasdaman community is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY 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 rasdaman community.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright 2003 - 2016 Peter Baumann / rasdaman GmbH.
 *
 * For more information please see <http://www.rasdaman.org>
 * or contact Peter Baumann via <baumann@rasdaman.com>.
 *
"""
from abc import ABCMeta, abstractmethod

from .tpinstaller import PipInstaller, \
    TomcatInstaller, PackagerInstall, UserCreationInstaller, MainPathInstaller, \
    CustomCmakeInstaller, PathInstaller, LdconfigInstall


class OSInstaller:
    """
    Wraps installation details specific to a particular OS version.
    """
    __metaclass__ = ABCMeta

    def __init__(self, profile):
        """
        :param Profile profile: the installation profile
        """
        self.profile = profile
        self.pkg_netcdf_python = 'netCDF4==1.2.7'
        self.pkg_grpcio_python = 'grpcio==1.9.0'
        self.pkg_protobuf_python = 'protobuf==3.6.1'
        self.pkg_gdal_python = ['GDAL==1.11.2']
        self.pkg_postgis = ['postgis']
        self.pkg_pyproj = 'pyproj==2.0.1'
        self.pkg_setuptools = 'setuptools'
        self.pip_pkg_pylint = 'pylint==2.13.4'

    def install(self):
        self.validate()
        self.get_deps().install()

    def get_deps(self):
        """
        Returns a chain of installers to setup rasdaman according to the profile.
        :rtype: TPInstaller
        """
        # get dependencies from the concrete OS installers
        ret = self.get_dependencies()
        # get pip dependencies
        ret = self.get_pip_deps(ret)
        # configure external tomcat
        if self.profile.webapps_deployment == "external" and \
           not self.profile.generate_package:
            ret = TomcatInstaller(ret)
        # build package
        if self.profile.generate_package:
            ret = PackagerInstall(ret)
        # create rasdaman user
        ret = UserCreationInstaller(ret, self.profile.user,
                                    self.profile.install_path)
        # setup /opt/rasdaman directory
        ret = MainPathInstaller(ret, self.profile.install_path)
        # download custom cmake if needed
        if self.profile.install and self.profile.install_type != "package":
            ret = CustomCmakeInstaller(ret)
        # install /etc/profile.d/rasdaman.sh
        ret = PathInstaller(ret, self.profile.install_path)
        # execute ldconfig
        ret = LdconfigInstall(ret)
        return ret

    def get_pip_deps(self, ret):
        """
        Returns a chain of PipInstaller to install pip packages
        :rtype: TPInstaller
        """
        if not self.profile.install_dependencies:
            return ret
        # install first dependencies (e.g. pyproj must be installed first, then pygrib)
        for pkg in [self.pkg_setuptools, self.pkg_pyproj]:
            if pkg is not None:
                ret = PipInstaller(ret, [pkg])
        # install separately on CentOS 7, as it has a few specific parameters
        if len(self.pkg_gdal_python) > 0:
            ret = PipInstaller(ret, self.pkg_gdal_python)
        # install remaining pip packages
        pip_pkgs = []
        if self.profile.install_type != "package":
            pip_pkgs += self.get_build_pip_packages()
        if not self.profile.generate_package:
            pip_pkgs += self.get_run_pip_packages()
            if self.profile.systemtest:
                pip_pkgs += self.get_run_systemtest_pip_packages()
        return PipInstaller(ret, pip_pkgs)

    def get_packages(self, profile):
        """
        Returns a lit of system packages needed to *build and run* rasdaman on this OS.
        :rtype: list[str]
        """
        self.profile = profile
        # packages needed to build rasdaman
        packages = self.get_build_packages()
        # packages needed to run rasdaman
        if profile.generate_package is not True:
            packages += self.get_run_packages()
        return packages

    def get_build_packages(self):
        """
        Returns a list of packaged that are needed to *build* rasdaman for this OS
        :rtype: list[str]
        """
        ret = self.get_build_core_packages()
        if self.profile.webapps_enabled:
            ret += self.get_build_java_packages()
        ret += self.get_build_cmake_packages()
        if self.profile.generate_package:
            ret += self.get_packaging_packages()
        if self.profile.generate_docs:
            ret += self.get_build_doc_packages()
        return ret

    @abstractmethod
    def get_build_core_packages(self):
        """
        Returns the core packages needed to build rasdaman
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_build_java_packages(self):
        """
        Returns the java packages needed to build the java code of rasdaman
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_build_cmake_packages(self):
        """
        Returns the packages needed to build rasdaman with cmake
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_build_doc_packages(self):
        """
        Returns the packages needed to generate rasdaman documentation
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_build_pip_packages(self):
        """
        Returns the pip packages needed to build rasdaman
        :rtype: list[str]
        """
        pass

    def get_run_packages(self):
        """
        Returns a list of packages that are needed only when running rasdaman
        :rtype: list[str]
        """
        ret = self.get_run_core_packages()
        if self.profile.webapps_enabled:
            ret += self.get_run_java_packages()
        if self.profile.systemtest:
            ret += self.get_run_systemtest_packages()
        if self.profile.enterprise:
            ret += self.pkg_postgis
        return ret

    @abstractmethod
    def get_run_core_packages(self):
        """
        Returns the core packages needed to run rasdaman
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_run_java_packages(self):
        """
        Returns the java packages needed to run the java code
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_run_pip_packages(self):
        """
        Returns packages needed to be installed with pip
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_run_systemtest_packages(self):
        """
        Returns packages needed to be installed for running the systemtest
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_run_systemtest_pip_packages(self):
        """
        Returns packages needed to be installed for running the systemtest
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_packaging_packages(self):
        """
        Returns packages needed for building rasdaman packages
        :rtype: list[str]
        """
        pass

    @abstractmethod
    def get_dependencies(self):
        """
        Returns the tp dependencies
        :rtype: TPInstaller
        """
        pass

    @abstractmethod
    def validate(self):
        """
        Validates the operating system and warns user if he has to do some action
        that this script can't do for him
        """
        pass
