# Source code for binaryninja.pluginmanager

    # Copyright (c) 2015-2017 Vector 35 LLC
    #
    # Permission is hereby granted, free of charge, to any person obtaining a copy
    # of this software and associated documentation files (the "Software"), to
    # deal in the Software without restriction, including without limitation the
    # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    # sell copies of the Software, and to permit persons to whom the Software is
    # furnished to do so, subject to the following conditions:
    #
    # The above copyright notice and this permission notice shall be included in
    # all copies or substantial portions of the Software.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    # IN THE SOFTWARE.

    import ctypes

    # Binary Ninja components
    from binaryninja import _binaryninjacore as core

    # 2-3 compatibility
    from binaryninja import range

    class RepoPlugin(object):
    	"""
    	``RepoPlugin` is mostly read-only, however you can install/uninstall enable/disable plugins. RepoPlugins are
    	created by parsing the plugins.json in a plugin repository.
    	"""
    	from binaryninja.enums import PluginType, PluginUpdateStatus
    	def __init__(self, handle):
    		raise Exception("RepoPlugin temporarily disabled!")
    		self.handle = core.handle_of_type(handle, core.BNRepoPlugin)

    	def __del__(self):
    		core.BNFreePlugin(self.handle)

    	def __repr__(self):
    		return "<{} {}/{}>".format(self.path, "installed" if self.installed else "not-installed", "enabled" if self.enabled else "disabled")

    	@property
    	def path(self):
    		"""Relative path from the base of the repository to the actual plugin"""
    		return core.BNPluginGetPath(self.handle)

    	@property
    	def installed(self):
    		"""Boolean True if the plugin is installed, False otherwise"""
    		return core.BNPluginIsInstalled(self.handle)

    	@installed.setter
    	def installed(self, state):
    		if state:
    			return core.BNPluginInstall(self.handle)
    		else:
    			return core.BNPluginUninstall(self.handle)

    	@property
    	def enabled(self):
    		"""Boolean True if the plugin is currently enabled, False otherwise"""
    		return core.BNPluginIsEnabled(self.handle)

    	@enabled.setter
    	def enabled(self, state):
    		if state:
    			return core.BNPluginEnable(self.handle)
    		else:
    			return core.BNPluginDisable(self.handle)

    	@property
    	def api(self):
    		"""string indicating the api used by the plugin"""
    		return core.BNPluginGetApi(self.handle)

    	@property
    	def description(self):
    		"""String short description of the plugin"""
    		return core.BNPluginGetDescription(self.handle)

    	@property
    	def license(self):
    		"""String short license description (ie MIT, BSD, GPLv2, etc)"""
    		return core.BNPluginGetLicense(self.handle)

    	@property
    	def license_text(self):
    		"""String complete license text for the given plugin"""
    		return core.BNPluginGetLicenseText(self.handle)

    	@property
    	def long_description(self):
    		"""String long description of the plugin"""
    		return core.BNPluginGetLongdescription(self.handle)

    	@property
    	def minimum_version(self):
    		"""String minimum version the plugin was tested on"""
    		return core.BNPluginGetMinimimVersions(self.handle)

    	@property
    	def name(self):
    		"""String name of the plugin"""
    		return core.BNPluginGetName(self.handle)

    	@property
    	def plugin_types(self):
    		"""List of PluginType enumeration objects indicating the plugin type(s)"""
    		result = []
    		count = ctypes.c_ulonglong(0)
    		plugintypes = core.BNPluginGetPluginTypes(self.handle, count)
    		for i in range(count.value):
    			result.append(PluginType(plugintypes[i]))
    		core.BNFreePluginTypes(plugintypes)
    		return result

    	@property
    	def url(self):
    		"""String url of the plugin's git repository"""
    		return core.BNPluginGetUrl(self.handle)

    	@property
    	def version(self):
    		"""String version of the plugin"""
    		return core.BNPluginGetVersion(self.handle)

    	@property
    	def update_status(self):
    		"""PluginUpdateStatus enumeration indicating if the plugin is up to date or not"""
    		return PluginUpdateStatus(core.BNPluginGetPluginUpdateStatus(self.handle))

    class Repository(object):
    	"""
    	``Repository`` is a read-only class. Use RepositoryManager to Enable/Disable/Install/Uninstall plugins.
    	"""
    	def __init__(self, handle):
    		raise Exception("Repository temporarily disabled!")
    		self.handle = core.handle_of_type(handle, core.BNRepository)

    	def __del__(self):
    		core.BNFreeRepository(self.handle)

    	def __repr__(self):
    		return "<{} - {}/{}>".format(self.path, self.remote_reference, self.local_reference)

    	def __getitem__(self, plugin_path):
    		for plugin in self.plugins:
    			if plugin_path == plugin.path:
    				return plugin
    		raise KeyError()

    	@property
    	def url(self):
    		"""String url of the git repository where the plugin repository's are stored"""
    		return core.BNRepositoryGetUrl(self.handle)

    	@property
    	def path(self):
    		"""String local path to store the given plugin repository"""
    		return core.BNRepositoryGetRepoPath(self.handle)

    	@property
    	def full_path(self):
    		"""String full path the repository"""
    		return core.BNRepositoryGetPluginsPath(self.handle)

    	@property
    	def local_reference(self):
    		"""String for the local git reference (ie 'master')"""
    		return core.BNRepositoryGetLocalReference(self.handle)

    	@property
    	def remote_reference(self):
    		"""String for the remote git reference (ie 'origin')"""
    		return core.BNRepositoryGetRemoteReference(self.handle)

    	@property
    	def plugins(self):
    		"""List of RepoPlugin objects contained within this repository"""
    		pluginlist = []
    		count = ctypes.c_ulonglong(0)
    		result = core.BNRepositoryGetPlugins(self.handle, count)
    		for i in range(count.value):
    			pluginlist.append(RepoPlugin(handle=result[i]))
    		core.BNFreeRepositoryPluginList(result, count.value)
    		del result
    		return pluginlist

    	@property
    	def initialized(self):
    		"""Boolean True when the repository has been initialized"""
    		return core.BNRepositoryIsInitialized(self.handle)

    class RepositoryManager(object):
    	"""
    	``RepositoryManager`` Keeps track of all the repositories and keeps the enabled_plugins.json file coherent with
    	the plugins that are installed/unstalled enabled/disabled
    	"""
    	def __init__(self, handle=None):
    		raise Exception("RepositoryManager temporarily disabled!")
    		self.handle = core.BNGetRepositoryManager()

    	def __getitem__(self, repo_path):
    		for repo in self.repositories:
    			if repo_path == repo.path:
    				return repo
    		raise KeyError()

    	def check_for_updates(self):
    		"""Check for updates for all managed Repository objects"""
    		return core.BNRepositoryManagerCheckForUpdates(self.handle)

    	@property
    	def repositories(self):
    		"""List of Repository objects being managed"""
    		result = []
    		count = ctypes.c_ulonglong(0)
    		repos = core.BNRepositoryManagerGetRepositories(self.handle, count)
    		for i in range(count.value):
    			result.append(Repository(handle=repos[i]))
    		core.BNFreeRepositoryManagerRepositoriesList(repos)
    		return result

    	@property
    	def plugins(self):
    		"""List of all RepoPlugins in each repository"""
    		plgs = {}
    		for repo in self.repositories:
    			plgs[repo.path] = repo.plugins
    		return plgs

    	@property
    	def default_repository(self):
    		"""Gets the default Repository"""
    		binaryninja._init_plugins()
    		return Repository(handle=core.BNRepositoryManagerGetDefaultRepository(self.handle))

    	def enable_plugin(self, plugin, install=True, repo=None):
    		"""
    		``enable_plugin`` Enables the installed plugin 'plugin', optionally installing the plugin if `install` is set to
    		True (default), and optionally using the non-default repository.

    		:param str name: Name of the plugin to enable
    		:param Boolean install: Optionally install the repo, defaults to True.
    		:param str repo: Optional, specify a repository other than the default repository.
    		:return: Boolean value True if the plugin was successfully enabled, False otherwise
    		:rtype: Boolean
    		:Example:

    			>>> mgr = RepositoryManager()
    			>>> mgr.enable_plugin('binaryninja-bookmarks')
    			True
    			>>>
    		"""
    		if install:
    			if not self.install_plugin(plugin, repo):
    				return False

    		if repo is None:
    			repo = self.default_repository
    		repopath = repo
    		pluginpath = plugin
    		if not isinstance(repo, str):
    			repopath = repo.path
    		if not isinstance(plugin, str):
    			pluginpath = plugin.path
    		return core.BNRepositoryManagerEnablePlugin(self.handle, repopath, pluginpath)

    	def disable_plugin(self, plugin, repo=None):
    		"""
    		``disable_plugin`` Disable the specified plugin, pluginpath

    		:param Repository or str repo: Repository containing the plugin to disable
    		:param RepoPlugin or str plugin: RepoPlugin to disable
    		:return: Boolean value True if the plugin was successfully disabled, False otherwise
    		:rtype: Boolean
    		:Example:

    			>>> mgr = RepositoryManager()
    			>>> mgr.disable_plugin('binaryninja-bookmarks')
    			True
    			>>>
    		"""
    		if repo is None:
    			repo = self.default_repository
    		repopath = repo
    		pluginpath = plugin
    		if not isinstance(repo, str):
    			repopath = repo.path
    		if not isinstance(plugin, str):
    			pluginpath = plugin.path
    		return core.BNRepositoryManagerDisablePlugin(self.handle, repopath, pluginpath)

    	def install_plugin(self, plugin, repo=None):
    		"""
    		``install_plugin`` install the specified plugin, pluginpath

    		:param Repository or str repo: Repository containing the plugin to install
    		:param RepoPlugin or str plugin: RepoPlugin to install
    		:return: Boolean value True if the plugin was successfully installed, False otherwise
    		:rtype: Boolean
    		:Example:

    			>>> mgr = RepositoryManager()
    			>>> mgr.install_plugin('binaryninja-bookmarks')
    			True
    			>>>
    		"""
    		if repo is None:
    			repo = self.default_repository
    		repopath = repo
    		pluginpath = plugin
    		if not isinstance(repo, str):
    			repopath = repo.path
    		if not isinstance(plugin, str):
    			pluginpath = plugin.path
    		return core.BNRepositoryManagerInstallPlugin(self.handle, repopath, pluginpath)

    	def uninstall_plugin(self, plugin, repo=None):
    		"""
    		``uninstall_plugin`` uninstall the specified plugin, pluginpath

    		:param Repository or str repo: Repository containing the plugin to uninstall
    		:param RepoPlugin or str plugin: RepoPlugin to uninstall
    		:return: Boolean value True if the plugin was successfully uninstalled, False otherwise
    		:rtype: Boolean
    		:Example:

    			>>> mgr = RepositoryManager()
    			>>> mgr.uninstall_plugin('binaryninja-bookmarks')
    			True
    			>>>
    		"""
    		if repo is None:
    			repo = self.default_repository
    		repopath = repo
    		pluginpath = plugin
    		if not isinstance(repo, str):
    			repopath = repo.path
    		if not isinstance(plugin, str):
    			pluginpath = plugin.path
    		return core.BNRepositoryManagerUninstallPlugin(self.handle, repopath, pluginpath)

    	def update_plugin(self, plugin, repo=None):
    		"""
    		``update_plugin`` update the specified plugin, pluginpath

    		:param Repository or str repo: Repository containing the plugin to update
    		:param RepoPlugin or str plugin: RepoPlugin to update
    		:return: Boolean value True if the plugin was successfully updated, False otherwise
    		:rtype: Boolean
    		:Example:

    			>>> mgr = RepositoryManager()
    			>>> mgr.update_plugin('binaryninja-bookmarks')
    			True
    			>>>
    		"""
    		if repo is None:
    			repo = self.default_repository
    		repopath = repo
    		pluginpath = plugin
    		if not isinstance(repo, str):
    			repopath = repo.path
    		if not isinstance(plugin, str):
    			pluginpath = plugin.path
    		return core.BNRepositoryManagerUpdatePlugin(self.handle, repopath, pluginpath)

    	def add_repository(self, url=None, repopath=None, localreference="master", remotereference="origin"):
    		"""
    		``add_repository`` adds a new plugin repository for the manager to track.

    		:param str url: Url to the git repository where the plugins are stored.
    		:param str repopath: path to where the repository will be stored on disk locally
    		:param str localreference: Optional reference to the local tracking branch typically "master"
    		:param str remotereference: Optional reference to the remote tracking branch typically "origin"
    		:return: Boolean value True if the repository was successfully added, False otherwise.
    		:rtype: Boolean
    		:Example:

    			>>> mgr = RepositoryManager()
    			>>> mgr.add_repository(url="https://github.com/vector35/community-plugins.git",
    			                       repopath="myrepo",
    			                       localreference="master", remotereference="origin")
    			True
    			>>>
    		"""
    		if not (isinstance(url, str) and isinstance(repopath, str) and
    			isinstance(localreference, str) and isinstance(remotereference, str)):
    			raise ValueError("Parameter is incorrect type")

    		return core.BNRepositoryManagerAddRepository(self.handle, url, repopath, localreference, remotereference)