import os
import shutil
from biicode.client.exception import ClientException
from biicode.common.utils import file_utils
from biicode.common.find.policy import Policy
from biicode.client.store import hivedb
from biicode.common.utils.file_utils import save, load
from biicode.common.settings.settings import Settings
from biicode.client.workspace.bii_paths import (BiiPaths, DEP_DIR, BIN_DIR, BUILD_DIR, CMAKE_DIR,
                                                LIB_DIR, BII_DIR, SRC_DIR)
from biicode.client.workspace.walk_block import walk_bii_folder, walk_anonymous_block
import fnmatch
from biicode.common.model.brl.block_name import BlockName
from biicode.common.edition.bii_config import BiiConfig
from biicode.common.model.blob import Blob


class HiveDiskImage(object):
    """Handle all actions related to hive in disk."""

    def __init__(self, bii_paths, user_cache, biiout):
        assert isinstance(bii_paths, BiiPaths)
        self._bii_paths = bii_paths
        self._user_cache = user_cache
        self._biiout = biiout
        self._hivedb = None
        self._settings = None
        self._policies = None

    @property
    def paths(self):
        return self._bii_paths

    def initialize(self):
        self._hivedb = hivedb.factory(self._bii_paths.new_project_db)
        self.settings
        self.policies

    def clean(self):
        try:
            self.hivedb.clean()
        except Exception as e:
            self._biiout.error("Unable to properly clean project DB:\n%s" % str(e))

        protect = {os.path.normpath(self._bii_paths.get_by_name(p)): p for p in (BII_DIR, SRC_DIR)}
        protect[self._bii_paths.project_root] = "project root"
        for folder in [BUILD_DIR, BIN_DIR, CMAKE_DIR, DEP_DIR, LIB_DIR]:
            try:
                path_folder = os.path.normpath(self._bii_paths.get_by_name(folder))
                if os.path.exists(path_folder):
                    if path_folder in protect:
                        self._biiout.warn("%s folder will not be cleaned" % folder)
                        self._biiout.warn("It matches the '%s' folder" % protect[path_folder])
                    else:
                        shutil.rmtree(path_folder)
            except Exception as e:
                self._biiout.error("Unable to delete %s folder\n%s" % (folder, str(e)))

    @property
    def settings(self):
        """ Return Hive settings.
        If settings.bii not present, creates and initialize a default hive settings.bii
        """
        if self._settings is None:
            settings_path = self._bii_paths.settings
            if not os.path.exists(settings_path):        # CREATE new hive settings file
                settings = Settings()           # empty settings, only OS information
                save(settings_path, settings.dumps())    # save settings.bii
                self._settings = settings
            else:                               # LOAD existing settings.bii file
                try:
                    self._settings = Settings.loads(load(settings_path))
                except Exception as e:
                    raise ClientException('%s\nIn file %s'
                                          % (str(e), settings_path.replace('\\', '/')))
        return self._settings

    @settings.setter
    def settings(self, value):
        """Set hive settings and save.

        :param value: new hive settings
        :type value: biicode.common.settings.settings.Settings

        """
        self._settings = value
        save(self._bii_paths.settings, value.dumps())

    @property
    def policies(self):
        if self._policies is None:
            policies_path = self._bii_paths.policies
            if not os.path.exists(policies_path):
                policies = self._user_cache.default_policies
                save(policies_path, policies)
            else:
                policies = load(policies_path)
                # Migration to new simple policies.bii format
                if policies.lstrip().startswith("# This is the file"):
                    self._biiout.warn("Upgrading your find policies to new format")
                    policies = self._user_cache.default_policies
                    save(policies_path, policies)
            if "YOUR_USER_NAME" in policies:
                user = self._user_cache.username
                if user is not None:
                    policies = policies.replace("YOUR_USER_NAME", user)
                    save(policies_path, policies)
            self._policies = Policy.loads(policies)
        return self._policies

    def create_new_block(self, block_name):
        ''' Creates block folders and main files if the language is specified'''
        assert block_name
        user, block = block_name.split('/')  # Windows uses backslashes
        # Create the block folder
        new_block_path = os.path.join(self._bii_paths.blocks, user, block)
        if not os.path.exists(new_block_path):
            os.makedirs(new_block_path)
            msg_succes = "Success: created {block_name} folder in your blocks directory!"
            self._biiout.success(msg_succes.format(block_name=block_name))
        else:
            msg_info = "{block_name} folder already exists in your blocks directory"