"""Automatically setup docs for a project

Call from command line:

	bench setup-docs app path

"""

import os, json, frappe, shutil, re
from frappe.website.context import get_context
from frappe.utils import markdown

class setup_docs(object):
	def __init__(self, app):
		"""Generate source templates for models reference and module API
			and templates at `templates/autodoc`
		"""
		self.app = app

		frappe.local.flags.web_pages_folders = ['docs',]
		frappe.local.flags.web_pages_apps = [self.app,]

		self.hooks = frappe.get_hooks(app_name = self.app)
		self.app_title = self.hooks.get("app_title")[0]
		self.setup_app_context()

	def setup_app_context(self):
		self.docs_config = frappe.get_module(self.app + ".config.docs")
		version = self.hooks.get("app_version")[0]
		self.app_context =  {
			"app": frappe._dict({
				"name": self.app,
				"title": self.app_title,
				"description": self.hooks.get("app_description")[0],
				"version": version,
				"publisher": self.hooks.get("app_publisher")[0],
				"icon": self.hooks.get("app_icon")[0],
				"email": self.hooks.get("app_email")[0],
				"headline": self.docs_config.headline,
				"sub_heading": self.docs_config.sub_heading,
				"source_link": self.docs_config.source_link,
				"hide_install": getattr(self.docs_config, "hide_install", False),
				"docs_base_url": self.docs_config.docs_base_url,
				"long_description": markdown(getattr(self.docs_config, "long_description", "")),
				"license": self.hooks.get("app_license")[0],
				"branch": getattr(self.docs_config, "branch", None) or "develop",
				"style": getattr(self.docs_config, "style", "")
			}),
			"metatags": {
				"description": self.hooks.get("app_description")[0],
			},
			"get_doctype_app": frappe.get_doctype_app
		}

	def build(self, docs_version):
		"""Build templates for docs models and Python API"""
		self.docs_path = frappe.get_app_path(self.app, "docs")
		self.path = os.path.join(self.docs_path, docs_version)
		self.app_context["app"]["docs_version"] = docs_version

		self.app_title = self.hooks.get("app_title")[0]
		self.app_path = frappe.get_app_path(self.app)

		print "Deleting current..."
		shutil.rmtree(self.path, ignore_errors = True)
		os.makedirs(self.path)

		self.make_home_pages()

		for basepath, folders, files in os.walk(self.app_path):

			# make module home page
			if "/doctype/" not in basepath and "doctype" in folders:
				module = os.path.basename(basepath)

				module_folder = os.path.join(self.models_base_path, module)

				self.make_folder(module_folder,
					template = "templates/autodoc/module_home.html",
					context = {"name": module})
				self.update_index_txt(module_folder)

			# make for model files
			if "/doctype/" in basepath:
				parts = basepath.split("/")
				#print parts
				module, doctype = parts[-3], parts[-1]

				if doctype not in ("doctype", "boilerplate"):
					self.write_model_file(basepath, module, doctype)

			# standard python module
			if self.is_py_module(basepath, folders, files):
				self.write_modules(basepath, folders, files)

		self.build_user_docs()

	def make_home_pages(self):
		"""Make standard home pages for docs, developer docs, api and models
			from templates"""
		# make dev home page
		with open(os.path.join(self.docs_path, "index.html"), "w") as home:
			home.write(frappe.render_template("templates/autodoc/docs_home.html",
			self.app_context))

		# make dev home page
		with open(os.path.join(self.path, "index.html"), "w") as home:
			home.write(frappe.render_template("templates/autodoc/dev_home.html",
			self.app_context))

		# make folders
		self.models_base_path = os.path.join(self.path, "models")
		self.make_folder(self.models_base_path,
			template = "templates/autodoc/models_home.html")

		self.api_base_path = os.path.join(self.path, "api")
		self.make_folder(self.api_base_path,
			template = "templates/autodoc/api_home.html")

		# make /user
		user_path = os.path.join(self.docs_path, "user")
		if not os.path.exists(user_path):
			os.makedirs(user_path)

		# make /assets/img
		img_path = os.path.join(self.docs_path, "assets", "img")
		if not os.path.exists(img_path):
			os.makedirs(img_path)

	def build_user_docs(self):
		"""Build templates for user docs pages, if missing."""
		#user_docs_path = os.path.join(self.docs_path, "user")

		# license
		with open(os.path.join(self.app_path, "..", "license.txt"), "r") as license_file:
			self.app_context["license_text"] = markdown(license_file.read())
			html = frappe.render_template("templates/autodoc/license.html",
				context = self.app_context)

		with open(os.path.join(self.docs_path, "license.html"), "w") as license_file:
			license_file.write(html.encode("utf-8"))

		# contents
		shutil.copy(os.path.join(frappe.get_app_path("frappe", "templates", "autodoc",
			"contents.html")), os.path.join(self.docs_path, "contents.html"))

		shutil.copy(os.path.join(frappe.get_app_path("frappe", "templates", "autodoc",
			"contents.py")), os.path.join(self.docs_path, "contents.py"))

		# install
		html = frappe.render_template("templates/autodoc/install.md",
			context = self.app_context)

		with open(os.path.join(self.docs_path, "install.md"), "w") as f:
			f.write(html)

		self.update_index_txt(self.docs_path)

	def make_docs(self, target, local = False):
		self.target = target
		self.local = local

		if self.local:
			self.docs_base_url = ""
		else:
			self.docs_base_url = self.docs_config.docs_base_url

		# add for processing static files (full-index)
		frappe.local.docs_base_url = self.docs_base_url

		# write in target path
		self.write_files()

		# copy assets/js, assets/css, assets/img
		self.copy_assets()

	def is_py_module(self, basepath, folders, files):
		return "__init__.py" in files \
			and (not "/doctype" in basepath) \
			and (not "/patches" in basepath) \
			and (not "/change_log" in basepath) \
			and (not "/report" in basepath) \
			and (not "/page" in basepath) \
			and (not "/templates" in basepath) \
			and (not "/tests" in basepath) \
			and (not "/docs" in basepath)

	def write_modules(self, basepath, folders, files):
		module_folder = os.path.join(self.api_base_path, os.path.relpath(basepath, self.app_path))
		self.make_folder(module_folder)

		for f in files:
			if f.endswith(".py"):
				module_name = os.path.relpath(os.path.join(basepath, f),
					self.app_path)[:-3].replace("/", ".").replace(".__init__", "")

				module_doc_path = os.path.join(module_folder,
					self.app + "." + module_name + ".html")

				self.make_folder(basepath)

				if not os.path.exists(module_doc_path):
					print "Writing " + module_doc_path
					with open(module_doc_path, "w") as f:
						context = {"name": self.app + "." + module_name}
						context.update(self.app_context)
						f.write(frappe.render_template("templates/autodoc/pymodule.html",
							context))

		self.update_index_txt(module_folder)

	def make_folder(self, path, template=None, context=None):
		if not template:
			template = "templates/autodoc/package_index.html"

		if not os.path.exists(path):
			os.makedirs(path)

			index_txt_path = os.path.join(path, "index.txt")
			print "Writing " + index_txt_path
			with open(index_txt_path, "w") as f:
				f.write("")

			index_html_path = os.path.join(path, "index.html")
			if not context:
				name = os.path.basename(path)
				if name==".":
					name = self.app
				context = {
					"title": name
				}
			context.update(self.app_context)
			print "Writing " + index_html_path
			with open(index_html_path, "w") as f:
				f.write(frappe.render_template(template, context))

	def update_index_txt(self, path):
		index_txt_path = os.path.join(path, "index.txt")
		pages = filter(lambda d: ((d.endswith(".html") or d.endswith(".md")) and d not in ("index.html",)) \
			or os.path.isdir(os.path.join(path, d)), os.listdir(path))
		pages = [d.rsplit(".", 1)[0] for d in pages]

		index_parts = []
		if os.path.exists(index_txt_path):
			with open(index_txt_path, "r") as f:
				index_parts = filter(None, f.read().splitlines())

		if not set(pages).issubset(set(index_parts)):
			print "Updating " + index_txt_path
			with open(index_txt_path, "w") as f:
				f.write("\n".join(pages))

	def write_model_file(self, basepath, module, doctype):
		model_path = os.path.join(self.models_base_path, module, doctype + ".html")

		if not os.path.exists(model_path):
			model_json_path = os.path.join(basepath, doctype + ".json")
			if os.path.exists(model_json_path):
				with open(model_json_path, "r") as j:
					doctype_real_name = json.loads(j.read()).get("name")

				print "Writing " + model_path

				with open(model_path, "w") as f:
					context = {"doctype": doctype_real_name}
					context.update(self.app_context)
					f.write(frappe.render_template("templates/autodoc/doctype.html",
						context).encode("utf-8"))

	def write_files(self):
		"""render templates and write files to target folder"""
		frappe.local.flags.home_page = "index"

		from frappe.website.router import get_pages, make_toc
		pages = get_pages()

		# clear the user, current folder in target
		shutil.rmtree(os.path.join(self.target, "user"), ignore_errors=True)
		shutil.rmtree(os.path.join(self.target, "current"), ignore_errors=True)

		cnt = 0
		for path, context in pages.iteritems():
			print "Writing {0}".format(path)

			# set this for get_context / website libs
			frappe.local.path = path

			context.update({
				"page_links_with_extn": True,
				"relative_links": True,
				"docs_base_url": self.docs_base_url,
				"url_prefix": self.docs_base_url,
			})

			context.update(self.app_context)

			context = get_context(path, context)

			if context.basename:
				target_path_fragment = context.route + '.html'
			else:
				# index.html
				target_path_fragment = context.route + '/index.html'

			target_filename = os.path.join(self.target, target_path_fragment.strip('/'))

			context.brand_html = context.top_bar_items = context.favicon = None

			self.docs_config.get_context(context)

			if not context.brand_html:
				if context.docs_icon:
					context.brand_html = '<i class="{0}"></i> {1}'.format(context.docs_icon, context.app.title)
				else:
					context.brand_html = context.app.title

			if not context.top_bar_items:
				context.top_bar_items = [
					# {"label": "Contents", "url": self.docs_base_url + "/contents.html", "right": 1},
					{"label": "User Guide", "url": self.docs_base_url + "/user", "right": 1},
					{"label": "Developer Docs", "url": self.docs_base_url + "/current", "right": 1},
				]

			context.top_bar_items = [{"label": '<i class="octicon octicon-search"></i>', "url": "#",
				"right": 1}] + context.top_bar_items

			context.parents = []
			parent_route = os.path.dirname(context.route)
			if pages[parent_route]:
				context.parents = [pages[parent_route]]

			if not context.favicon:
				context.favicon = "/assets/img/favicon.ico"

			context.only_static = True
			context.base_template_path = "templates/autodoc/base_template.html"

			if '<code>' in context.source:
				context.source = re.sub('\<code\>(.*)\</code\>', '<code>{% raw %}\g<1>{% endraw %}</code>', context.source)

			html = frappe.render_template(context.source, context)

			html = make_toc(context, html)

			if not "<!-- autodoc -->" in html:
				html = html.replace('<!-- edit-link -->',
					edit_link.format(\
						source_link = self.docs_config.source_link,
						app_name = self.app,
						branch = context.app.branch,
						target = context.template))

			if not os.path.exists(os.path.dirname(target_filename)):
				os.makedirs(os.path.dirname(target_filename))

			with open(target_filename, "w") as htmlfile:
				htmlfile.write(html.encode("utf-8"))

				cnt += 1

		print "Wrote {0} files".format(cnt)


	def copy_assets(self):
		"""Copy jquery, bootstrap and other assets to files"""

		print "Copying assets..."
		assets_path = os.path.join(self.target, "assets")

		# copy assets from docs
		source_assets = frappe.get_app_path(self.app, "docs", "assets")
		if os.path.exists(source_assets):
			for basepath, folders, files in os.walk(source_assets):
				target_basepath = os.path.join(assets_path, os.path.relpath(basepath, source_assets))

				# make the base folder
				if not os.path.exists(target_basepath):
					os.makedirs(target_basepath)

				# copy all files in the current folder
				for f in files:
					shutil.copy(os.path.join(basepath, f), os.path.join(target_basepath, f))

		# make missing folders
		for fname in ("js", "css", "img"):
			path = os.path.join(assets_path, fname)
			if not os.path.exists(path):
				os.makedirs(path)

		copy_files = {
			"js/lib/jquery/jquery.min.js": "js/jquery.min.js",
			"js/lib/bootstrap.min.js": "js/bootstrap.min.js",
			"js/lib/highlight.pack.js": "js/highlight.pack.js",
			"js/docs.js": "js/docs.js",
			"css/bootstrap.css": "css/bootstrap.css",
			"css/font-awesome.css": "css/font-awesome.css",
			"css/docs.css": "css/docs.css",
			"css/hljs.css": "css/hljs.css",
			"css/font": "css/font",
			"css/octicons": "css/octicons",
			# always overwrite octicons.css to fix the path
			"css/octicons/octicons.css": "css/octicons/octicons.css",
			"images/frappe-bird-grey.svg": "img/frappe-bird-grey.svg",
			"images/background.png": "img/background.png",
			"images/smiley.png": "img/smiley.png",
			"images/up.png": "img/up.png"
		}

		for source, target in copy_files.iteritems():
			source_path = frappe.get_app_path("frappe", "public", source)
			if os.path.isdir(source_path):
				if not os.path.exists(os.path.join(assets_path, target)):
					shutil.copytree(source_path, os.path.join(assets_path, target))
			else:
				shutil.copy(source_path, os.path.join(assets_path, target))

		# fix path for font-files, background
		files = (
			os.path.join(assets_path, "css", "octicons", "octicons.css"),
			os.path.join(assets_path, "css", "font-awesome.css"),
			os.path.join(assets_path, "css", "docs.css"),
		)

		for path in files:
			with open(path, "r") as css_file:
				text = css_file.read()
			with open(path, "w") as css_file:
				if "docs.css" in path:
					css_file.write(text.replace("/assets/img/",
						self.docs_base_url + '/assets/img/'))
				else:
					css_file.write(text.replace("/assets/frappe/", self.docs_base_url + '/assets/'))


edit_link = '''
<div class="page-container">
	<div class="page-content">
	<div class="edit-container text-center">
		<i class="icon icon-smile"></i>
		<a class="text-muted edit" href="{source_link}/blob/{branch}/{app_name}/{target}">
			Improve this page
		</a>
	</div>
	</div>
</div>'''
