import obspython as obs
import urllib.request
import urllib.error
from io import StringIO
from html.parser import HTMLParser
from bs4 import BeautifulSoup as BS
import os
import json
import textwrap 

# ------------------------------------------------------------
hotkey_n_id = obs.OBS_INVALID_HOTKEY_ID
hotkey_p_id = obs.OBS_INVALID_HOTKEY_ID
hotkey_r_id = obs.OBS_INVALID_HOTKEY_ID

def  sourceShowing():
	global source_name
	source = obs.obs_get_source_by_name(source_name)
	showing = False
	if source != None:
		showing = obs.obs_source_showing(source)
	obs.obs_source_release(source)	
	return showing


def sourceActive():
	global source_name
	source = obs.obs_get_source_by_name(source_name)
	active = False
	if source != None:
		active = obs.obs_source_active(source)
	obs.obs_source_release(source)	
	return active
	
# text_fade_dir = 1 to fade out and 2 to fade in
def timer_callback():
	global text_fade_enabled
	global text_fade_speed
	global text_opacity
	global text_fade_dir
	global isLoaded
	if text_fade_dir > 0:
		if text_fade_dir == 1:	
			if text_opacity > text_fade_speed:
				text_opacity = text_opacity - text_fade_speed
			else:
				text_fade_dir = 0  ## stop fading
				text_opacity = 0  ## set to 0%
				show_scripture()
		else:
			if text_opacity < 100 - text_fade_speed:
				text_opacity = text_opacity + text_fade_speed
			else:
				text_fade_dir = 0  ## stop fading
				text_opacity = 100 ##set to 100%

		source = obs.obs_get_source_by_name(source_name)
		if source is not None:
			settings = obs.obs_data_create()
			obs.obs_data_set_int(settings, "opacity", text_opacity)  ## Set new text opacity
			obs.obs_source_update(source, settings)
			obs.obs_data_release(settings)
		obs.obs_source_release(source)
	return

class MLStripper(HTMLParser):
    def __init__(self):
        super().__init__()
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
	

def refresh_display():	
	global chapter 
	global start_verse 
	global end_verse 
	global source_name 
	global scripture 
	global book 
	global bibleNdx
	global script_settings
	global display_list
	global dlines
	global dwidth
	global showIndex
	global groups
	global script_props	
	
	if dwidth == 0:
		return
	display_text = ""
	wrapper = textwrap.TextWrapper(width=dwidth) 
	text = scripture.replace("*","\n")
	display_list = wrapper.wrap(text = text)
	
	i = 0
	groups = 1
	for line in display_list:
		display_text = display_text + line + "\n"
		i += 1
		if i == dlines:
			display_text=display_text + "~\n"
			groups += 1
			i = 0
	while i < dlines:
		display_text=display_text + "-\n"
		display_list.append("")
		i = i + 1;
	obs.obs_data_set_string(script_settings, "display_text", display_text)

	show_scripture()

def set_title():
	global titleText
	global script_settings
	global chapter
	global start_verse
	global end_verse
	global extra
	global bibleNdx

	titleText = bibleNdx[book]["book"] + "%20" + str(chapter) + ":" + str(start_verse)
	if end_verse > start_verse :
		titleText += "-" + str(end_verse) 
	titleText += extra
	return titleText.replace("%20"," ")
	
def refresh_text():
	global chapter 
	global start_verse 
	global end_verse 
	global source_name 
	global scripture 
	global book 
	global bibleNdx
	global script_settings
	global showIndex
	global extra
	global titleText

	bible = obs.obs_data_get_string(script_settings, "bible")	
	
	set_title()
	url = "http://bible.oremus.org/?version=" + bible + "&vnum=NO&fnote=NO&show_ref=NO&headings=NO&passage=" + titleText 
	url = url.replace(" ","%20")
	print ("URL: " + url)
	try:
		with urllib.request.urlopen(url) as response:
			data = response.read()
			text = data.decode('utf-8')
			soup = BS(text, 'html.parser')

			for tag in soup.select('span[class*="vnumVis"]'):
				tag.clear()
				
			for tag in soup.select('sup[class*="vnumVis"]'):
				tag.clear()
				
			for tag in soup.select('sup[class="fnote"]'):
				tag.clear()					
				
			for tag in soup.select('h2'):
				tag.clear()					
				
			texts = soup.find("div", attrs={"class": "bibletext"})
			textb = strip_tags(str(texts))
			text = textb.replace("\\n", "")
			text = textb.replace("\"", "")				
			text = " ".join(text.split())
			scripture = text
			obs.obs_data_set_string(script_settings, "scripture", text)

	except urllib.error.URLError as err:
		obs.script_log(obs.LOG_WARNING, "Error opening URL '" + url + "': " + err.reason)
		obs.remove_current_callback()
			
	refresh_display()

	
def show_scripture():
	global showIndex
	global display_list
	global dlines
	global dwidth
	global text_fade_enabled
	global text_fade_speed
	global text_opacity
	global text_fade_dir	
	global titleText
	global script_settings
	global groups
	display_text = ""
	if showIndex < groups:
		for i in range(0, dlines):
			display_text = display_text + display_list[i + (showIndex * dlines)] 
			if i < dlines-1:
				display_text += "\n"
	text_settings = obs.obs_data_create()
	obs.obs_data_set_string(text_settings, "text", display_text)  ## Set new text string
	obs.obs_data_set_int(text_settings, "opacity", 0)  ## Set zero text opacity
	source = obs.obs_get_source_by_name(source_name)
	obs.obs_source_update(source, text_settings)
	obs.obs_data_set_string(text_settings, "text", display_text)  ## Set new text string	
	obs.obs_source_release(source)
	obs.obs_data_release(text_settings)
	
	text_settings = obs.obs_data_create()	
	text = ""
	if titleText != None:
		text = titleText.replace("%20"," ")
		obs.obs_data_set_string(text_settings, "text",text )  ## Set new text string
		title = obs.obs_data_get_string(script_settings, "Tsource") 
		source = obs.obs_get_source_by_name(title)
		obs.obs_source_update(source, text_settings)
		obs.obs_data_set_string(text_settings, "text", display_text)  ## Set new text string	
	obs.obs_data_release(text_settings)		
	obs.obs_source_release(source)

	text_fade_dir = 2;
	
def fade_scripture(): 
	global text_opacity
	global showIndex
	global text_fade_dir	
	if text_fade_enabled:
		if text_opacity == 100: 
			text_opacity = 99
			text_fade_dir = 1  ## fade out
			return
		if text_opacity == 0:
			text_opacity = 1
			text_fade_dir = 2  ## fade in
	show_scripture()

def refresh_pressed(props, prop):
	global script_settings
	refresh_text()
	return True
	
def home_scripture(pressed):
	global source_name
	if not pressed or not sourceShowing(): return 
	global showIndex
	showIndex = 0
	show_scripture()
	return True

def next_scripture(pressed):
	global source_name
	if not pressed or not sourceShowing(): return 
	global showIndex
	if showIndex < groups:
		showIndex += 1 
	fade_scripture()
	return True	
		
def prev_scripture(pressed):
	global source_name
	if not pressed or not sourceShowing(): return 
	global showIndex
	if showIndex > 0:
		showIndex -= 1 	
	fade_scripture()
	return True	
	
def home_pressed(props, prop):
	home_scripture(True)
	return False

def shownext_pressed(props, prop):
	next_scripture(True)
	return False
	
def showprev_pressed(props, prop):
	prev_scripture(True)
	return False
	
def script_load(settings):
	global showIndex
	global chapter 
	global start_verse 
	global end_verse 
	global source_name 
	global scripture 
	global book 
	global bibleNdx
	global script_settings
	global text_fade_enabled
	global text_fade_speed
	global text_opacity
	global extra
	global text_fade_dir	
	global dlines
	global dwidth	
	global isActive
	global isLoaded
	global titleText
	
	global hotkey_n_id
	global hotkey_p_id
	global hotkey_r_id
	
	isLoaded = False
	scripture = ""
	isActive = True
	script_settings = settings
	extra = ""
	titleText = ""
	text_fade_dir = 0
	text_opacity = 100
	dlines = obs.obs_data_get_int(script_settings, "lines")	
	dwidth = obs.obs_data_get_int(script_settings, "width")	
	if dwidth == 0:
		dwidth = 10
	if dlines == 0:
		dlines = 5
	text_fade_enabled = obs.obs_data_get_bool(settings, "text_fade_enabled")   ## 	Fade Enable (WZ)
	text_fade_speed = obs.obs_data_get_int(settings, "text_fade_speed")   ## 	Fade Speed (WZ)		
	with open( os.path.dirname(os.path.abspath(__file__)) + "\\bible.json", "r") as read_file:
		bibleNdx = json.load(read_file)
	showIndex = 0
	chapter = obs.obs_data_get_int(script_settings, "chapter")
	start_verse = obs.obs_data_get_int(script_settings, "sverse")
	end_verse = obs.obs_data_get_int(script_settings, "everse")
	extra = obs.obs_data_get_string(script_settings, "extra")
	source_name = obs.obs_data_get_string(script_settings, "source")
	#scripture = obs.obs_data_get_string(settings, "scripture_text")
	length = len(bibleNdx)
	booktext = obs.obs_data_get_string(settings, "book")	
	for book in range(length):
		if bibleNdx[book]["book"] == booktext:
			break	
			
	hotkey_n_id = obs.obs_hotkey_register_frontend("scripture_next_hotkey", "Next Scripture Section", next_scripture)
	hotkey_save_array = obs.obs_data_get_array(settings, "scripture_next_hotkey")
	obs.obs_hotkey_load(hotkey_n_id, hotkey_save_array)
	obs.obs_data_array_release(hotkey_save_array)

	hotkey_p_id = obs.obs_hotkey_register_frontend("scripture_prev_hotkey", "Previous Scripture Section", prev_scripture)		
	hotkey_save_array = obs.obs_data_get_array(settings, "scripture_prev_hotkey")
	obs.obs_hotkey_load(hotkey_p_id, hotkey_save_array)
	obs.obs_data_array_release(hotkey_save_array)
	
	hotkey_r_id = obs.obs_hotkey_register_frontend("scripture_reset_hotkey", "Reset Scripture", home_scripture)		
	hotkey_save_array = obs.obs_data_get_array(settings, "scripture_reset_hotkey")
	obs.obs_hotkey_load(hotkey_r_id, hotkey_save_array)
	obs.obs_data_array_release(hotkey_save_array)
	refresh_text()
	obs.timer_add(timer_callback, 80)	## Setup callback for text fade effect	

def script_save(settings):
	global hotkey_n_id
	global hotkey_p_id
	global hotkey_r_id
	
	hotkey_save_array = obs.obs_hotkey_save(hotkey_n_id)
	obs.obs_data_set_array(settings, "scripture_next_hotkey", hotkey_save_array)
	obs.obs_data_array_release(hotkey_save_array)
	
	hotkey_save_array = obs.obs_hotkey_save(hotkey_p_id)
	obs.obs_data_set_array(settings, "scripture_prev_hotkey", hotkey_save_array)
	obs.obs_data_array_release(hotkey_save_array)
	
	hotkey_save_array = obs.obs_hotkey_save(hotkey_r_id)
	obs.obs_data_set_array(settings, "scripture_reset_hotkey", hotkey_save_array)
	obs.obs_data_array_release(hotkey_save_array)

def script_description():
	return "Loads scripture from Oremus.org to paged Text Object. <br> (W. Zaggle 2020)"
	
def script_update(settings):
	global text_fade_enabled
	global text_fade_speed
	global source_name
	global scripture
	scripture = obs.obs_data_get_string(script_settings, "scripture")
	text_fade_enabled = obs.obs_data_get_bool(settings, "text_fade_enabled")   ## 	Fade Enable (WZ)
	text_fade_speed = obs.obs_data_get_int(settings, "text_fade_speed")   ## 	Fade Speed (WZ)	
	source_name = obs.obs_data_get_string(script_settings, "source")
	refresh_display()	
	return True

def script_defaults(settings):

	return
	
def book_changed(props, prop, settings):
	global chapter 
	global start_verse 
	global end_verse 
	global book 
	global script_settings

	length = len(bibleNdx)

	booktext = obs.obs_data_get_string(script_settings, "book")	
	for book in range(length):
		if bibleNdx[book]["book"] == booktext:
			break
		
	length = len(bibleNdx[book]["chapters"])
	p = obs.obs_properties_get(props,"chapter")
	obs.obs_property_int_set_limits(p,1,length,1)
	chapter = obs.obs_data_get_int(script_settings, "chapter")	
	if chapter > length:
		chapter = length
	if chapter < 1:
		chapter = 1
	obs.obs_data_set_int(script_settings,"chapter",chapter)
	start_verse = obs.obs_data_get_int(script_settings, "sverse")
	length = int(bibleNdx[book]["chapters"][chapter-1]["verses"])
	p = obs.obs_properties_get(props,"sverse")
	obs.obs_property_int_set_limits(p,1,length,1)
	if start_verse > length:
		start_verse = length
	if start_verse < 1:
		start_verse = 1
	end_verse = obs.obs_data_get_int(script_settings, "everse")	
	obs.obs_data_set_int(script_settings,"sverse",start_verse)
	
	p = obs.obs_properties_get(props,"everse")
	obs.obs_property_int_set_limits(p,start_verse,int(bibleNdx[book]["chapters"][chapter-1]["verses"]),1)	
	if end_verse > length :
		end_verse = length	
	if end_verse < start_verse :
		end_verse = start_verse	

	obs.obs_data_set_int(script_settings,"everse",end_verse)
	global load_prop
	obs.obs_property_set_description(load_prop, "Load: "+ set_title())	
	return True
	
def chapter_changed(props, prop, settings):
	global chapter 
	global start_verse 
	global end_verse 
	global book 
	global script_settings

	chapter = obs.obs_data_get_int(script_settings, "chapter")
	length = int(bibleNdx[book]["chapters"][chapter-1]["verses"])
	p = obs.obs_properties_get(props,"sverse")
	obs.obs_property_int_set_limits(p,1,length,1)	
	if start_verse > length:
		start_verse = length
	obs.obs_data_set_int(script_settings,"sverse",start_verse)	
	
	p = obs.obs_properties_get(props,"everse")
	obs.obs_property_int_set_limits(p,start_verse,length,1)	
	if end_verse > length :
		end_verse = length		
	if end_verse < start_verse:
		end_verse = start_verse
	obs.obs_data_set_int(script_settings,"everse",end_verse)
	global load_prop
	obs.obs_property_set_description(load_prop, "Load: "+ set_title())		

	return True
	
def start_changed(props, prop, settings):
	global chapter 
	global start_verse 
	global end_verse 
	global book 
	global script_settings

	start_verse = obs.obs_data_get_int(script_settings, "sverse")
	end_verse = obs.obs_data_get_int(script_settings, "everse")

	length = int(bibleNdx[book]["chapters"][chapter-1]["verses"])
	p = obs.obs_properties_get(props,"everse")
	obs.obs_property_int_set_limits(p,start_verse,length,1)	
	if end_verse > length :
		end_verse = length		
	if end_verse < start_verse:
		end_verse = start_verse

	obs.obs_data_set_int(script_settings,"everse",end_verse)
	global load_prop
	obs.obs_property_set_description(load_prop, "Load: "+ set_title())	
	return True
	
def end_changed(props, prop, settings):
	global start_verse 
	global end_verse 
	global book 
	global script_settings
	end_verse = obs.obs_data_get_int(script_settings, "everse")
	global load_prop
	obs.obs_property_set_description(load_prop, "Load: "+ set_title())	
	return True	
	
def extra_changed(props, prop, settings):
	global script_settings
	global extra
	extra = obs.obs_data_get_string(script_settings, "extra")
	global load_prop
	obs.obs_property_set_description(load_prop, "Load: "+ set_title())		
	return True		
	
def scripture_changed(props, prop, settings):
	global script_settings
	global groups
	obs.obs_property_set_description(prop, "Display (" + str(groups) + "):")	
	refresh_display()	
	return True			

def lines_changed(props, prop, settings):
	global dlines
	dlines = obs.obs_data_get_int(script_settings, "lines")	
	refresh_display()
	global groups
	global display_prop
	obs.obs_property_set_description(display_prop, "Display (" + str(groups) + "):")		
	return True		
	
def width_changed(props, prop, settings):
	global dwidth
	dwidth = obs.obs_data_get_int(script_settings, "width")
	refresh_display()
	global groups
	global display_prop
	obs.obs_property_set_description(display_prop, "Display (" + str(groups) + "):")	
	return True	
	
def script_properties():
	global display_prop
	global load_prop
	props = obs.obs_properties_create()
	p0 = obs.obs_properties_add_list(props, "bible", "Bible:", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING)
	obs.obs_property_list_add_string(p0, "NRSVAE", "NRSVAE")
	obs.obs_property_list_add_string(p0, "NRSV", "NRSV")
	obs.obs_property_list_add_string(p0, "AV", "AV")
	p1 = obs.obs_properties_add_list(props, "book", "Book:", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING)
	length = len(bibleNdx)
	for i in range(length):
		name = bibleNdx[i]["book"]
		obs.obs_property_list_add_string(p1, name, name)
	obs.obs_property_set_modified_callback(p1, book_changed)
	p2 = obs.obs_properties_add_int(props,"chapter","Chapter:",1,len(bibleNdx[book]["chapters"]),1)
	obs.obs_property_set_modified_callback(p2, chapter_changed)
	p3 = obs.obs_properties_add_int(props,"sverse","Start Verse:",1,int(bibleNdx[book]["chapters"][chapter-1]["verses"]),1)
	obs.obs_property_set_modified_callback(p3, start_changed)
	p4 = obs.obs_properties_add_int(props,"everse","End Verse:",1,int(bibleNdx[book]["chapters"][chapter-1]["verses"]),1)
	obs.obs_property_set_modified_callback(p4, end_changed)
	p3 = obs.obs_properties_add_text(props,"extra", "Extra:", obs.OBS_TEXT_DEFAULT)
	obs.obs_property_set_modified_callback(p3, extra_changed)
	load_prop = obs.obs_properties_add_button(props, "button", "Load Scripture", refresh_pressed)
	p6 = obs.obs_properties_add_list(props, "source", "Text Source", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)
	p6B = obs.obs_properties_add_list(props, "Tsource", "Title Source", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)	
	sources = obs.obs_enum_sources()
	if sources is not None:
		n = list()
		for source in sources:
			source_id = obs.obs_source_get_unversioned_id(source)
			if source_id == "text_gdiplus" or source_id == "text_ft2_source":
				n.append(obs.obs_source_get_name(source))
		n.sort()
		for name in n:
				obs.obs_property_list_add_string(p6, name, name)
				obs.obs_property_list_add_string(p6B, name, name)				
		obs.source_list_release(sources)
		
	obs.obs_properties_add_bool(props, "text_fade_enabled", "Fade text Out/In for next page")	## Fade Enable (WZ)
	obs.obs_properties_add_int_slider(props, "text_fade_speed", "Fade Speed", 1, 20, 1)
	obs.obs_properties_add_text(props, "scripture", "Scripture:", obs.OBS_TEXT_MULTILINE)	

	display_prop = obs.obs_properties_add_text(props, "display_text", "Display:", obs.OBS_TEXT_MULTILINE)
	obs.obs_property_set_modified_callback(display_prop, scripture_changed)	
	p7 = obs.obs_properties_add_int(props,"width","Width (Chars):",10,200,1)
	obs.obs_property_set_modified_callback(p7, width_changed)
	p8 = obs.obs_properties_add_int(props,"lines","Length (Lines):",1,20,1)
	obs.obs_property_set_modified_callback(p8, lines_changed)
	obs.obs_properties_add_button(props, "dohome", "Reset", home_pressed)
	obs.obs_properties_add_button(props, "showprev", "Show Prev", showprev_pressed)	
	obs.obs_properties_add_button(props, "shownext", "Show Next", shownext_pressed)

	
	return props
	

	