# coding: utf-8
#
# Load the Libraries.
#
require 'sinatra'				# http://www.sinatrarb.com/
require 'ruby-handlebars' 	# https://github.com/vincent-psarga/ruby-handlebars
require 'kramdown' 			# http://kramdown.gettalong.org
require 'slim' 				# http://slim-lang.com/
require 'gmail'       # https://github.com/dcparker/ruby-gmail
require 'json'
require 'date'

#
# Setup the Handlebars engine.
#
$hbs = Handlebars::Handlebars.new

#
# HandleBars Helper: 		date
#
# Description: 				This helper returns the current date
# 									based on the format given.
#
$hbs.register_helper('date') {|context, format|
	now = Date.today
	now.strftime(format)
}

#
# HandleBars Helper: 		cdate
#
# Description: 				This helper returns the given date
# 									based on the format given.
#
$hbs.register_helper('cdate') {|context, date, format|
	day = Date.parse(date)
	day.strftime(format)
}

#
# HandleBars Helper: 		save
#
# Description: 				This helper expects a
# 									"<name>|<value>" where the name
# 									is saved with the value for future
# 									expansions. It also returns the
# 									value directly.
#
$hbs.register_helper('save') {|context, name, text|
	#
	# If the text parameter isn't there, then it is the goPress
	# format all combined into the name. Split it out. The parameters
	# are not String objects. Therefore, they need converted first.
	#
	name = String.try_convert(name)
	if name.count("|") > 0
		parts = name.split('|')
		name = parts[0]
		text = parts[1]
	end

	#
	# Register the new helper.
	#
	$hbs.register_helper(name) {|context, value|
		text
	}

	#
	# Return the text.
	#
	text
}

#
# Load Server Data.
#
$parts = {}
$parts = JSON.parse(File.read './server.json')
$styleDir = Dir.getwd + '/themes/styling/' + $parts['CurrentStyling']
$layoutDir = Dir.getwd + '/themes/layouts/' + $parts['CurrentLayout']

#
# Load the layouts and styles defaults.
#
$parts["layout"] = File.read $layoutDir + '/template.html'
$parts["404"] = File.read $styleDir + '/404.html'
$parts["footer"] = File.read $styleDir + '/footer.html'
$parts["header"] = File.read $styleDir + '/header.html'
$parts["sidebar"] = File.read $styleDir + '/sidebar.html'

#
# Load all the page parts in the parts directory.
#
Dir.entries($parts["Sitebase"] + '/parts/').select {|f|
	if !File.directory? f
		$parts[File.basename(f, ".*")] = File.read $parts["Sitebase"] + '/parts/' + f
	end
}

#
# Setup server defaults:
#
port = $parts["ServerAddress"].split(":")[2]
set :port, port

#
# Define the routes for the CMS.
#
get '/' do
  page "main"
end

get '/favicon.ico', :provides => 'ico' do
	File.read "#{$parts['Sitebase']}/images/favicon.ico"
end

get '/stylesheets.css', :provides => 'css'  do
	File.read "#{$parts["Sitebase"]}/css/final/final.css"
end

get '/scripts.js', :provides => 'js'  do
	File.read "#{$parts["Sitebase"]}/js/final/final.js"
end

get '/images/:image', :provides => 'image' do
	File.read "#{$parts['Sitebase']}/images/#{parms['image']}"
end

get '/posts/blogs/:blog' do
	post 'blogs', params['blog'], 'index'
end

get '/posts/blogs/:blog/:post' do
	post 'blogs', params['blog'], params['post']
end

get '/posts/news/:news' do
	post 'news', params['news'], 'index'
end

get '/posts/news/:news/:post' do
	post 'news', params['news'], params['post']
end

get '/:page' do
	page params['page']
end

post '/api/message' do
  #
  # Get the parameters from the form.
  #
  name = params[:Name]
  email = params[:Email]
  message = params[:Message]

  #
  # Create and send the email.
  #
  Gmail.new('<your email address>', '<your password>') do |gmail|
    gmail.deliver do
      to "<your email address>"
      from email
      subject "Message from " + name
      text_part do
        body message
      end
    end
  end
  #
  # Send the user to the message sent page.
  #
  page 'messagesent'
end

#
# Various functions used in the making of the server:
#

#
# Function:    page
#
# Description: This function is for processing a page
# 					in the CMS.
#
# Inputs:
# 				pg 	The page name to lookup
#
def page(pg)
	processPage $parts["layout"], "#{$parts["Sitebase"]}/pages/#{pg}"
end

#
# Function:    post
#
# Description: This function is for processing a post type
# 					page in the CMS. All blog and news pages are
#    				post type pages.
#
# Inputs:
# 					type 		The type of the post
# 					cat 		The category of the post (blog, news)
# 					post 		The actual page of the post
#
def post(type, cat, post)
	processPage $parts["layout"], "#{$parts["Sitebase"]}/posts/#{type}/#{cat}/#{post}"
end

#
# Function:    figurePage
#
# Description: This function is to figure out the page
# 					type (ie: markdown, HTML, jade, etc), read
# 					the contents, and translate it to HTML.
#
# Inputs:
# 					page 		The address of the page without it's
# 								extension.
#
def figurePage(page)
	result = ""

	if File.exist? page + ".html"
		#
		# It's an HTML file.
		#
		result = File.read page + ".html"
	elsif File.exist? page + ".md"
		#
		# It's a markdown file.
		#
		result = Kramdown::Document.new(File.read page + ".md").to_html

		#
		# Fix the fancy quotes from Kramdown. It kills
		# the Handlebars parser.
		#
		result.gsub!("“","\"")
		result.gsub!("”","\"")
	elsif File.exist? page + ".amber"
		#
		# It's a jade file. Slim doesn't support
		# macros. Therefore, not as powerful as straight jade.
		# Also, we have to render any Handlebars first
		# since the Slim engine dies on them.
		#
		File.write("./tmp.txt",$hbs.compile(File.read page + ".amber").call($parts))
		result = Slim::Template.new("./tmp.txt").render()
	else
		#
		# Doesn't exist. Give the 404 page.
		#
		result = $parts["404"]
	end

	#
	# Return the results.
	#
	return result
end

#
# Function:    processPage
#
# Description: The function processes a page by getting
# 					it's contents, combining with all the page
# 					parts using Handlebars, and processing the
# 					shortcodes.
#
# Inputs:
# 					layout 		The layout structure for the page
# 					page 			The complete path to the desired
# 									page without it's extension.
#
def processPage(layout, page)
	#
	# Get the page contents and name.
	#
	$parts["content"] = figurePage page
	$parts["PageName"] = File.basename page

	#
	# Run the page through Handlebars engine.
	#
	begin
		pageHB = $hbs.compile(layout).call($parts)
	rescue
		pageHB = "<h1 class='Error'>Render Error</h1>"
	end

	#
	# Run the page through the shortcodes processor.
	#
	pageSH = processShortCodes pageHB

	#
	# Run the page through the Handlebar engine again.
	#
	begin
		pageFinal = $hbs.compile(pageSH).call($parts)
	rescue
		pageFinal = "<h1 class='Error'>Render Error</h1>" + pageSH
	end

	#
	# Return the results.
	#
	return pageFinal
end

#
# Function:    processShortCodes
#
# Description: This function takes the page and processes
# 					all of the shortcodes in the page.
#
# Inputs:
# 					page 		The contents of the page to process.
#
def processShortCodes(page)
	#
	# Initialize the result variable for returning.
	#
	result = ""

	#
	# Find the first shortcode
	#
	scregFind = /\-\[([^\]]*)\]\-/
	match1 = scregFind.match(page)
	if match1 != nil
		#
		# We found one! get the text before it
		# into the result variable and initialize
		# the name, param, and contents variables.
		#
		name = ""
		param = ""
		contents = ""
		nameLine = match1[1]
		loc1 = scregFind =~ page
		result = page[0, loc1]

		#
		# Separate out the nameLine into a shortcode
		# name and parameters.
		#
		match2 = /(\w+)(.*)*/.match(nameLine)
		if match2.length == 2
			#
			# Just a name was found.
			#
			name = match2[1]
		else
			#
			# A name and parameter were found.
			#
			name = match2[1]
			param = match2[2]
		end

		#
		# Find the closing shortcode
		#
		rest = page[loc1+match1[0].length, page.length]
		regEnd = Regexp.new("\\-\\[\\/#{name}\\]\\-")
		match3 = regEnd.match(rest)
		if match3 != nil
			#
			# Get the contents the tags enclose.
			#
			loc2 = regEnd =~ rest
			contents = rest[0, loc2]

			#
			# Search the contents for shortcodes.
			#
			contents = processShortCodes(contents)

			#
			# If the shortcode exists, run it and include
			# the results. Otherwise, add the contents to
			# the result.
			#
			if $shortcodes.include?(name)
				result += $shortcodes[name].call(param, contents)
			else
				result += contents
			end

			#
			# process the shortcodes in the rest of the
			# page.
			#
			rest = rest[loc2 + match3[0].length, page.length]
			result += processShortCodes(rest)
		else
			#
			# There wasn't a closure. Therefore, just send the page
			# back.
			#
			result = page
		end
	else
		#
		# No shortcodes. Just return the page.
		#
		result = page
	end

	return result
end

#
# Data Structure: 		$shortcodes
#
# Description: 			This data structure contains all
# 								the valid shortcodes names and the
# 								function. All shortcodes should
# 								receive the arguments and the
# 								that the shortcode encompasses.
#
$shortcodes = {
	"box" => lambda { |args, contents|
		return("<div class='box'>#{contents}</div>")
	},
   'Column1'=> lambda { |args, contents|
		return("<div class='col1'>#{contents}</div>")
   },
   'Column2' => lambda { |args, contents|
      return("<div class='col2'>#{contents}</div>")
   },
   'Column1of3' => lambda { |args, contents|
      return("<div class='col1of3'>#{contents}</div>")
   },
   'Column2of3' => lambda { |args, contents|
      return("<div class='col2of3'>#{contents}</div>")
   },
   'Column3of3' => lambda { |args, contents|
      return("<div class='col3of3'>#{contents}</div>")
   },
   'php' => lambda { |args, contents|
      return("<div class='showcode'><pre type='syntaxhighlighter' class='brush: php'>#{contents}</pre></div>")
   },
   'js' => lambda { |args, contents|
      return("<div class='showcode'><pre type='syntaxhighlighter' class='brush: javascript'>#{contents}</pre></div>")
   },
	"html" => lambda { |args, contents|
		return("<div class='showcode'><pre type='syntaxhighlighter' class='brush: html'>#{contents}</pre></div>")
	},
   'css' => lambda {|args, contents|
      return("<div class='showcode'><pre type='syntaxhighlighter' class='brush: css'>#{contents}</pre></div>")
   }
}
