import os
import shutil
import sys
import re
import markdown
import StringIO
import argparse

class Pjen:
	def __init__(self, path=None):
		"""
		By default, the path will be set to a directory called 'website' in the current working directory, 
		or the path can be specified using the 'path' keyword parameter.
		"""

		self.path = path if path else "website"


	def _create_file_structure(self, overwrite=False):
		"""
		Creates the following file structure:

		[website or the specified name]
		  |-> templates
			  |-> group1
		  |-> generated
  	      |-> images
		  |-> scss
	      |-> css
	      |-> scripts

	    If overwrite=True then any existing file structe will be overwritten.  
		"""

		#if overwrite=True then remove the existing files
		if overwrite:
			shutil.rmtree(self.path)

		project_folders = (
			os.path.join("templates","group1"),
			"generated",
			"images",
			"scss",
			"css",
			"scripts"
        )

		for folder in project_folders:
			os.makedirs(os.path.join(self.path, folder))

		print("Created project in {}".format(self.path))

	def create_project(self):
		"""
		Creates the initial file structure for a project. If a directory already exists
		at the location then it will ask if the user wants to overwrite the existing directory. 
		"""

		#check if a project already exists, if so give option to overwrite
		if os.path.exists(self.path):

			print("A directory already exists at: {}".format(self.path))
			response = raw_input("Do you want to overwrite? (y/n)\n")

			if response == "y" or response == "Y":
				print("Overwriting existing file structure")
				self._create_file_structure(overwrite=True)

			elif response == "n" or response == "N":
				print("Exiting...")
				sys.exit()

			else:
				print("Not a valid input. Exiting...")

		#if there is no existing project then create one.
		else:
			self._create_file_structure()

	def _count_indent(self, string, tab_equiv=4):
		"""
		Returns the number of leading spaces before a tag. 
		A tab is counted as 'tab_equiv' spaces.
		"""

		#find the index of the first occurance of a character
		count = re.search("\{\{(.*?)\}\}", string).span()[0]

		#count the number of tabs before the first occurance of a character
		n_tabs = len(re.findall("\t", string[:count+1]))

		#account for the tabs in the count - recall they have already been counted as 1
		count += (tab_equiv-1) * n_tabs

		return count

	def _sanatise_file_list(self, file_list, fname=None):
		"""
		Removes blacklisted files from the input list 'file_list'. In addition, a file with 
		the name 'fname' can also be removed.
		"""

		blacklist = (".DS_Store", fname)

		for item in blacklist:
			try:
				file_list.remove(item)
			except ValueError:
				pass

	def _extract_tag_data(self, f, md=False):
		"""
		Extracts the tag data from the file object 'f'. The flag 'md' specifies whether the file is a markdown file.
		If 'md' is True then the contents get converted from markdown to html

		Returns a dictionary of tag data. The keys are the names of the tags and the corresponding values are lists of lines,
		with empty lines removed from the start and end of the lists.

		For example:

		For a file that contains the following (note: period/full-stop represents a whitespace character):

			.\n
			.\n
			<div>
			....<p>This is a paragraph</p>
			</div>
			.\n
		

		the dictionary returned will be as follows:

		{ "html" : ["<div>", "....<p>This is a paragraph</p>", "</div>"] }

		"""

		#dictionary to store data
		tag_data = {}

		#flag to determine the section of the input file
		tag = None

		#if it's a markdown file, convert it to html - create a file object so readlines() can be used
		if md:
			f = StringIO.StringIO(markdown.markdown(f.read()))

		#iterate through the input file and set the appropriate flag
		for line in f.readlines():

			#look for tags
			match = re.search("\{\{(.*?)\}\}", line)

			#if there is a tag
			if match:

				#extract the tag name
				tag = re.search("(\w)+", match.group(0)).group(0)

				#add an empty string to the dictionary
				tag_data[tag] = ""

			#not a tag line
			else:

				#ensure that the line is actually within a tag's section
				if tag:
					tag_data[tag] += line

		#split the data for each tag into a list of lines
		for key in tag_data.keys():
			tag_data[key] = [line + "\n" for line in tag_data[key].split("\n")]

			#remove any empty lines from the start and end of the tag data
			while tag_data[key][0].isspace() is True:
				del tag_data[key][0]

			while tag_data[key][-1].isspace() is True:
				del tag_data[key][-1]

		return tag_data

	def generate(self):
		"""
		Iterates through all the directories in the 'templates' directory, inserting all the 
		inputs into each template. 
		"""

		#get a list of template groups
		static_groups = os.listdir(os.path.join(self.path, "templates"))
		self._sanatise_file_list(static_groups)

		print("Found {} group(s)".format(len(static_groups)))

		#iterate through each of the template groups
		for group in static_groups:

			#get each of the files in the group
			files = os.listdir(os.path.join(self.path, "templates", group))
			
			#remove the template and hidden file
			self._sanatise_file_list(files, "template.html")

			#open the template 
			with open(os.path.join(self.path, "templates", group, "template.html"), "r") as template:

				#iterate though the files that need to be generated
				for f in files:

					#the new static page will have the same filename as the template_input, but will be a html file
					fname = os.path.splitext(f)[0] + ".html"

					#create a new static page 
					with open(os.path.join(self.path, "generated", fname), "w") as page:

						print("Generating file: {}".format(fname))

						#open up the input
						with open(os.path.join(self.path, "templates", group, f), "r") as template_input:

							#check if a markdown file
							_, extension = os.path.splitext(f)

							#set the flag to specify whether it's a markdown file
							is_md = True if extension == ".md" else False

							#extract the tag data from the template input
							tag_data = self._extract_tag_data(template_input, is_md)

							#reset the file pointer as the template can be read many times
							template.seek(0)

							#iterate through the template file
							for line in template.readlines():

								#look for tags
								match = re.search("\{\{(.*?)\}\}", line)

								#if there is a tag
								if match:

									#extract the tag name
									tag = re.search("(\w)+", match.group(0)).group(0)

									#check if there is data to be inserted for this tag
									if tag in tag_data.keys():

										indent = self._count_indent(line)

										for insert_line in tag_data[tag]:

											page.write(" "*indent + insert_line)

								#otherwise copy the template text
								else:
									page.write(line)

if __name__ == "__main__":

	#need to parse command line arguments
	parser = argparse.ArgumentParser(description = "Pjen - Static website generator")

	#add the arguments
	parser.add_argument('-p','--path', help="Sets the project path")	
	parser.add_argument('-c','--create', action="store_true", help="Create project file structure")
	parser.add_argument('-g','--generate', action="store_true", help="Generate static pages")
    
	args = parser.parse_args()

	#ensure that conflicting arguments have not been supplied
	if args.create and args.generate:
		print("Error: Cannot use --create and --generate at the same time")

	#if no commands were supplied then print the help
	elif not (args.create or args.generate):
		parser.print_help()

	else:

		p = Pjen(args.path)

		if args.generate:
			p.generate()

		elif args.create:
			p.create_project()

