# -*- 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
import os
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, git, directory):
        self.directory = directory
        self.git = git
        makeDirectory(directory)

    def __iter__(self):
        for dn in listDirectory(self.directory, onlyDir=True, fullPath=True):
            if self.git.is_git(dn):
                yield ProfileRepository(self.git, 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(self.git, 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, git, name, storage):
        self._storage = storage
        self.name = name
        self.git = git

    @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, git, url, storage, branch=DEFAULT_BRANCH):
        name = path.basename(url)
        if name.endswith(".git"):
            name = name[:-4]
        rpath = path.join(storage.directory, name)
        if path.exists(rpath):
            removeDir(rpath)
        if git.is_private_url(url):
            try:
                makeDirectory(rpath)
                os.chmod(rpath, 0o700)
            except OSError:
                pass
        git.cloneRepository(url, rpath, branch)
        pr = cls(git, 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(git, 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):
        return self.git.get_url(self.directory, "origin")

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

    def sync(self):
        """
        Синхронизировать репозиторий
        """
        if not self.git.pullRepository(self.directory, quiet_error=True):
            self.git.resetRepository(self.directory, to_origin=True)
            self.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)
