# -*- coding: utf-8 -*-

# Copyright 2015-2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import sys
from calculate.core.server.func import Action, Tasks
from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate
from calculate.lib.cl_template import TemplatesError
from calculate.lib.utils.files import FilesError
from calculate.update.emerge_parser import EmergeError
from calculate.update.update import UpdateError
from ..datavars import BuilderError
from calculate.lib.utils.git import GitError
from calculate.lib.utils.portage import (EmergeLogNamedTask,
                                         PackageList, EmergeLog)
from calculate.install.distr import DistributiveError
from calculate.update.update_tasks import EmergeMark

_ = lambda x: x
setLocalTranslate('cl_builder3', sys.modules[__name__])
__ = getLazyLocalTranslate(_)


class BuilderConditions(object):
    @staticmethod
    def was_installed(pkg, task_name):
        def func(Get):
            task = EmergeLog(EmergeLogNamedTask(task_name),
                             prefix=Get('cl_builder_path'))
            return bool(PackageList(task.list)[pkg])

        return func

    @staticmethod
    def need_depclean(pkg, task_name):
        def func(Get):
            task = EmergeLog(EmergeLogNamedTask(task_name),
                             prefix=Get('cl_builder_path'))
            return bool(PackageList(task.list)[pkg])

        return func


class ClBuilderUpdateAction(Action):
    """
    Действие обновление конфигурационных файлов
    """
    # ошибки, которые отображаются без подробностей
    native_error = (DistributiveError, FilesError, UpdateError,
                    TemplatesError,
                    BuilderError, GitError, EmergeError)

    successMessage = None
    failedMessage = None
    interruptMessage = __("System update manually interrupted")

    # список задач для действия
    tasks = [
                {'name': 'check_build_run',
                 'method': 'Builder.check_build_run()'},
                {'name': 'check_chroot_run',
                 'method': 'Builder.check_chroot_run()'},
                {'name': 'clear_log',
                 'method': 'Builder.clear_log(cl_builder_id_path)',
                 },
                {'name': 'allow_scan_binhost',
                 'method': 'Builder.setVariable("cl_builder_binhost_scan_set",'
                           '"on",True)'
                },
                {'name': 'apply_template',
                 'message': __("Configuring build"),
                 'method': 'Builder.applyTemplates(cl_builder_target,False,'
                           'False,None)',
                 },
                {'name': 'invalidate_vars',
                 'method': 'Builder.invalidateVariables('
                           '"cl_builder_linux_datavars")'
                 },
                {'name': 'reconfigure_vars',
                 'method': 'Builder.reconfigureProfileVars('
                           'cl_builder_linux_datavars,cl_builder_path)'
                 },
                {'name': 'apply_branch_vars',
                 'method': 'Builder.apply_branch_variables()'
                 },
                {'name': 'reps_synchronization',
                 'group': __("Repositories synchronization"),
                 'tasks': [
                     # запасная синхронизация, в ходе которой ветки
                     #  обновляются до master
                     {'name': 'sync_reps_fallback',
                      'foreach': 'cl_builder_sync_rep',
                      'message':
                          __("Fallback syncing the {eachvar:capitalize} "
                             "repository"),
                      'method': 'Builder.syncRepositories(eachvar)',
                      'condition': lambda Get: (
                          Get('cl_builder_binary_set') == "on" and
                          not Get('update.cl_update_binhost_data')[0])
                      },
                     # обновление переменных информации из binhost
                     {'name': 'invalidate_vars',
                      'method':
                          'Builder.invalidateVariables('
                          '"cl_builder_linux_datavars")',
                      'condition': lambda Get: (
                          Get('cl_builder_binary_set') == "on" and
                          not Get('update.cl_update_binhost_data')[0])
                      },
                     {'name': 'binhost_unavailable',
                      'error': __("Update server is unavailable"),
                      'condition': lambda Get: (
                          Get('cl_builder_binary_set') == "on" and
                          not Get('update.cl_update_binhost_data')[0])
                      },
                     {'name': 'sync_reps',
                      'foreach': 'cl_builder_sync_rep',
                      'message': __(
                          "Syncing the {eachvar:capitalize} repository"),
                      'method': 'Builder.syncRepositories(eachvar)',
                      'condition': lambda Get: Get('cl_builder_sync_rep')
                      },
                     {'name': 'check_binhost',
                      'method': 'Builder.check_binhost(True)',
                      'condition': lambda Get: Get(
                          'cl_builder_binary_set') == "on"
                      },
                     {'name': 'sync_other_reps',
                      'foreach': 'builder.cl_builder_other_rep_name',
                      'message': __(
                          "Syncing the {eachvar:capitalize} repository"),
                      'method': 'Builder.syncLaymanRepository(eachvar)',
                      'condition': lambda Get: Get(
                          'update.cl_update_other_set') == 'on'
                      },
                     {'name': 'trim_reps',
                      'foreach': 'cl_builder_sync_rep',
                      'message': __("Cleaning the history of the "
                                    "{eachvar:capitalize} repository"),
                      'method': 'Builder.trimRepositories(eachvar)',
                      'condition': lambda Get: (
                          Get('cl_builder_sync_rep') and
                          Get('update.cl_update_onedepth_set') == 'on')
                      },
                     {'name': 'regen_cache',
                      'foreach': 'cl_builder_sync_overlay_rep',
                      'essential': False,
                      'method': 'Builder.regenCache(eachvar)',
                      'condition': (lambda Get: (
                          Get('builder.cl_builder_outdate_set') == 'on' and
                          Get('update.cl_update_egencache_force') != 'skip' or
                          Get('update.cl_update_egencache_force') == 'force'))
                      },
                     {'name': 'eix_update',
                      'message': __("Updating the eix cache for "
                                    "{cl_builder_eix_repositories}"),
                      'method': 'Builder.eixUpdate(cl_builder_repository_name)',
                      'condition': (lambda Get: (
                          Get('builder.cl_builder_outdate_set') == 'on' and
                          Get('update.cl_update_eixupdate_force') != 'skip' or
                          Get('update.cl_update_eixupdate_force') == 'force'))
                      },
                     {'name': 'sync_reps:cleanpkg',
                      'message': __(
                          "Removing obsolete distfiles and binary packages"),
                      'method': 'Builder.cleanpkg()',
                      'condition': lambda Get: (
                          Get('builder.cl_builder_outdate_set') == 'on' and
                          Get('update.cl_update_cleanpkg_set') == 'on'),
                      'essential': False
                      },
                     # сообщение удачного завершения при обновлении репозиториев
                     {'name': 'success_syncrep',
                      'message': __("Synchronization finished"),
                      'depend': (Tasks.success() & Tasks.has_any(
                          "sync_reps", "sync_other_reps", "emerge_metadata",
                          "eix_update")),
                      }
                 ]
                 },
                {'name': 'reps_synchronization',
                 'group': __("System configuration"),
                 'tasks': [
                     {'name': 'revision',
                      'message': __("Fixing the settings"),
                      'method': 'Builder.apply_templates(cl_builder_path,'
                                'cl_template_clt_set,True,None,False,"sync",'
                                'cl_builder_linux_datavars)',
                      'condition': lambda Get: Get('cl_templates_locate')
                      },
                     {'name': 'dispatch_conf',
                      'message': __("Updating configuration files"),
                      'method': 'Builder.dispatchConf(None,cl_builder_path)',
                      'condition': lambda Get: Get('cl_dispatch_conf') != 'skip'
                      },
                 ]
                 },
                {'name': 'emerge_update_world',
                 'group': __("Updating packages"),
                 'tasks': [
                     {'name': 'update_world',
                      'message': __("Calculating dependencies"),
                      'method': 'Builder.emerge_ask('
                                'update.cl_update_pretend_set,'
                                '"-uDN","--changed-deps",'
                                '"--with-bdeps=y","@world")'
                      }
                 ],
                 'condition': lambda Get: (
                     Get('update.cl_update_sync_only_set') == 'off')
                 },
                {'name': 'update_other',
                 'condition': lambda Get: (
                     Get('update.cl_update_pretend_set') == 'off' and
                     Get('update.cl_update_sync_only_set') == 'off')
                 },
                {'name': 'update_other:group_changed_packages',
                 'group': __("Rebuild modified packages"),
                 'tasks': [
                     {'name': 'changed_packages',
                      'message': __("Calculating dependencies"),
                      'method': 'Builder.rebuild_changed_packages('
                                'cl_builder_path,'
                                'cl_builder_repository_data)',
                      'condition': lambda Get:
                      Get('cl_builder_rebuild_changed_set') == 'on'
                      }
                 ]
                 },
                {'name': 'update_other:update_perl',
                 'group': __("Updating Perl"),
                 'tasks': [
                     {'name': 'perl_cleaner',
                      'message': __(
                          'Find & rebuild packages and Perl header files '
                          'broken due to a perl upgrade'),
                      'method': 'Builder.chroot_emergelike(cl_builder_path,'
                                '"perl-cleaner","all")',
                      'condition': BuilderConditions.was_installed(
                          'dev-lang/perl$', EmergeMark.PerlCleaner),
                      'decoration': 'Builder.update_task("%s")'
                                    % EmergeMark.PerlCleaner
                      },
                 ]
                 },
                {'name': 'update_other:depclean',
                 'group': __("Cleaning the system from needless packages"),
                 'tasks': [
                     {'name': 'update_depclean',
                      'message': __("Calculating dependencies"),
                      'method': 'Builder.depclean()',
                      },
                 ]
                 },
                {'name': 'update_world:update_modules',
                 'group': __("Rebuilding dependent modules"),
                 'tasks': [
                     {'name': 'update_world:module_rebuild',
                      'message': __('Updating Kernel modules'),
                      'method': 'Builder.emerge(cl_builder_path,"",'
                                '"@module-rebuild")',
                      'condition': BuilderConditions.was_installed(
                          'sys-kernel/.*source', EmergeMark.KernelModules),
                      'decoration': 'Builder.update_task("%s")' %
                                    EmergeMark.KernelModules
                      },
                     {'name': 'update_world:x11_module_rebuild',
                      'message': __('Updating X.Org server modules'),
                      'method': 'Builder.emerge(cl_builder_path,"",'
                                '"@x11-module-rebuild")',
                      'condition': BuilderConditions.was_installed(
                          'x11-base/xorg-server', EmergeMark.XorgModules),
                      'decoration': 'Builder.update_task("%s")' %
                                    EmergeMark.XorgModules
                      },
                     {'name': 'update_world:preserved_rebuild',
                      'message': __('Updating preserved libraries'),
                      'method': 'Builder.emerge(cl_builder_path,"",'
                                '"@preserved-rebuild")',
                      'condition': BuilderConditions.was_installed(
                          '.*', EmergeMark.PreservedLibs),
                      'decoration': 'Builder.update_task("%s")' %
                                    EmergeMark.PreservedLibs
                      },
                     {'name': 'update_world:revdev_rebuild',
                      'message': __('Checking reverse dependencies'),
                      'method': 'Builder.chroot_revdep_rebuild(cl_builder_path,'
                                '"revdep-rebuild")',
                      'condition': lambda Get: (
                          Get('update.cl_update_skip_rb_set') == 'off' and
                          BuilderConditions.was_installed(
                              '.*', EmergeMark.RevdepRebuild)(Get)),
                      'decoration': 'Builder.update_task("%s")' %
                                    EmergeMark.RevdepRebuild
                      },
                     {'name': 'update_world:dispatch_conf_end',
                      'message': __("Updating configuration files"),
                      'method': 'Builder.dispatchConf(None,cl_builder_path)',
                      'condition': lambda Get: Get('cl_dispatch_conf') != 'skip'
                      },
                     {'name': 'update_world:binary_cleaning',
                      'message': __("Cleaning the binary repository"),
                      'method': 'Builder.binaryCleaning()',
                      'condition': lambda Get: Get(
                          'cl_builder_binary_set') == "off"
                      },
                 ],
                 'depend': Tasks.has("update_other")
                 },
                {'name': 'update_other:reading_news',
                 'method': 'Builder.reading_news(cl_builder_path)',
                 'essential': False
                 },
                {'name': 'update_other:check_obsolete',
                 'method': 'Builder.check_obsolete(cl_builder_path)'
                 }
            ] + [
                {'name': 'failed',
                 'error': __("Failed to update the system"),
                 'depend': (Tasks.failed() & Tasks.hasnot("interrupt") &
                            Tasks.success_all("check_build_run"))
                 },
                # сообщение удачного завершения при обновлении ревизии
                {'name': 'update_other:success_rev',
                 'message': __("The system was successfully updated"),
                 }
            ]
