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

# Copyright 2014-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 os import path
import shutil
from calculate.lib.utils.files import (listDirectory, readFile, readLinesFile,
                                       makeDirectory, removeDir)
from calculate.lib.utils.git import Git
from update import UpdateError

from calculate.lib.cl_lang import setLocalTranslate, _
setLocalTranslate('cl_update3', sys.modules[__name__])

DEFAULT_BRANCH = Git.Reference.Master

class RepositoryStorageInterface(object):
    def __iter__(self):
        raise StopIteration

    def get_profiles(self, url, branch=DEFAULT_BRANCH):
        return []

    def get_repository(self, url, branch=DEFAULT_BRANCH):
        return None

class RepositoryStorage(RepositoryStorageInterface):
    directory = '/tmp'

    def __init__(self, directory):
        self.directory = directory
        makeDirectory(directory)

    def __iter__(self):
        git = Git()
        for dn in listDirectory(self.directory, onlyDir=True, fullPath=True):
            if git.is_git(dn):
                yield ProfileRepository(path.basename(dn), self)

    def get_profiles(self, url, branch=DEFAULT_BRANCH):
        return []

    def get_repository(self, url, branch=DEFAULT_BRANCH):
        return None


class ProfileStorage(RepositoryStorage):
    def get_profiles(self, url, branch=DEFAULT_BRANCH):
        rep = self.get_repository(url, branch)
        if rep:
            return rep.get_profiles()
        return None

    def get_repository(self, url, branch=DEFAULT_BRANCH):
        return None


class LocalStorage(ProfileStorage):
    """
    Локальное хранилище репозиториев, при запросе по урлу смотрит, доступные
    репозитории если находит подходящий - возвращает его профили
    """
    def get_repository(self, url, branch=DEFAULT_BRANCH):
        for rep in self:
            if rep.is_like(url, branch):
                return rep

class CacheStorage(ProfileStorage):
    """
    Хранилище репозиториев, при запросе по урлу смотрит, доступные
    репозитории если находит подходящий - возвращает его профили,
    если не находит - скачивает и возвращает профили
    """
    def get_repository(self, url, branch=DEFAULT_BRANCH):
        for rep in self:
            if rep.is_like(url, branch):
                return rep
        else:
            return ProfileRepository.clone(url, self, branch or DEFAULT_BRANCH)

class RepositoryStorageSet(RepositoryStorageInterface):
    """
    Набор хранилищ репозиториев
    """
    def __init__(self, *storages):
        self.storages = storages

    def get_profiles(self, url, branch=DEFAULT_BRANCH):
        """
        Получить профили из указанного репозитория
        """
        for storage in self.storages:
            profiles = storage.get_profiles(url, branch)
            if profiles is not None:
                return profiles
        return None

    def __iter__(self):
        for storage in self.storages:
            for rep in storage:
                yield rep

    def get_repository(self, url, branch=DEFAULT_BRANCH):
        """
        Получить репозиторий по параметрам
        """
        for rep in self:
            if rep.is_like(url, branch):
                return rep
        for storage in self.storages:
            rep = storage.get_repository(url, branch)
            if rep:
                return rep
        return None

    def is_local(self, url, branch=DEFAULT_BRANCH):
        """
        Проверить является ли репозиторий с указанными параметрами
        локальным
        """
        rep = self.get_repository(url, branch)
        if rep and isinstance(rep.storage, LocalStorage):
            return True
        return False

    def __repr__(self):
        return "Repository set"

class Profile(object):
    """
    Профиль репозитория
    """
    available_arch = ["amd64", "x86"]

    def __init__(self, repository, profile, arch):
        self.repository = repository
        self.profile = profile
        self.arch = arch

    @property
    def path(self):
        return path.join(self.repository.directory,"profiles", self.profile)

    @classmethod
    def from_string(cls, repository, s):
        parts = filter(None, s.split())
        if len(parts) == 3 and parts[0] in cls.available_arch:
            return Profile(repository, parts[1], parts[0])
        return None

    def __repr__(self):
        return "<Profile (%s) %s:%s from %s>" % (self.arch,
                                                 self.repository.repo_name,
                                                 self.profile,
                                                 self.repository.directory)


class ProfileRepository(object):
    """
    Репозиторий либо скачивается, либо берется из кэша
    """
    def __init__(self, name, storage):
        self._storage = storage
        self.name = name

    @property
    def storage(self):
        return self._storage

    @storage.setter
    def storage(self, storage):
        if storage.directory != self._storage.directory:
            newpath = path.join(storage.directory, self.name)
            if self.directory == newpath:
                return
            try:
                if path.exists(newpath):
                    removeDir(newpath)
                shutil.move(self.directory, newpath)
                self._storage = storage
            except OSError as e:
                raise UpdateError(_("Failed to move the profile: %s") %
                                  str(e))

    @classmethod
    def clone(cls, url, storage, branch=DEFAULT_BRANCH):
        name = path.basename(url)
        if name.endswith(".git"):
            name = name[:-4]
        git = Git()
        rpath = path.join(storage.directory, name)
        if path.exists(rpath):
            removeDir(rpath)
        git.cloneRepository(url, rpath, branch)
        pr = cls(name, storage)
        repo_name = pr.repo_name
        if name != repo_name:
            rpath_new = path.join(storage.directory, repo_name)
            if path.exists(rpath_new):
                removeDir(rpath_new)
            shutil.move(rpath, rpath_new)
            pr = cls(repo_name, storage)
        return pr

    @property
    def repo_name(self):
        return readFile(path.join(self.directory,
                                  "profiles/repo_name")).strip()

    def is_like(self, url, branch=DEFAULT_BRANCH):
        if self.url == url and (branch is None or self.branch == branch):
            return True
        return False

    @property
    def directory(self):
        """
        Получить локальную директорию на данные репозитория
        """
        return path.join(self.storage.directory, self.name)

    @property
    def url(self):
        git = Git()
        return git.get_url(self.directory, "origin")

    @property
    def branch(self):
        git = Git()
        return git.getBranch(self.directory)

    def sync(self):
        """
        Синхронизировать репозиторий
        """
        git = Git()
        if not git.pullRepository(self.directory, quiet_error=True):
            git.resetRepository(self.directory, to_origin=True)
            git.pullRepository(self.directory, quiet_error=True)

    def get_profiles(self):
        """
        Получить список профилей репозитория
        """
        profiles_desc = path.join(self.directory, "profiles/profiles.desc")
        return filter(None, (Profile.from_string(self, line)
                             for line in readLinesFile(profiles_desc)))

    def __repr__(self):
        return "<ProfileRepository %s url=%s>" % (self.directory, self.url)
