from kivy.properties import StringProperty, NumericProperty, ListProperty
from kivy.factory import Factory
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.image import AsyncImage

from .baseWidget import ScrollWidget,  getDataHandler, VBox

from .utils import CSize
import re

SOURCE_DELIMITER = "\n```\n"
VIDEO_LEAD = "![@"
AUDIO_LEAD = "![#"
IMAGE_LEAD = "!["
LINK_LEAD = "["
REFER_SPLIT = "]("
REFER_END = ")"

class MarkdownParser(object):
	mdkeys = [
		SOURCE_DELIMITER,
		VIDEO_LEAD,
		AUDIO_LEAD,
		IMAGE_LEAD
	]
	def __init__(self, mdtext):
		self.mdtext = mdtext
		self.result = []
		self.text = ""
		self.mdkey_handler = {
			SOURCE_DELIMITER:self.handle_source_delimiter,
			VIDEO_LEAD:self.lead_video,
			AUDIO_LEAD:self.lead_audio,
			IMAGE_LEAD:self.lead_image,
		}

	def handle_source_delimiter(self):
		x = self.mdtext.split(SOURCE_DELIMITER,1)
		mk_p = MarkdownParser(x[0])
		d = {
			'source':x[0]
		}
		self.result.append(d) 
		if len(x) > 1:
			self.mdtext = x[1]

	def lead_video(self):
		print('lead_video() ..., mdtext=', self.mdtext)
		x = self.mdtext.split(REFER_SPLIT,1)
		if len(x)<2:
			print('lead_video() return here', REFER_SPLIT, '---', self.mdtext)
			self.text = f'{VIDEO_LEAD}'
			return
		title = x[0]
		y = x[1].split(REFER_END,1)
		if len(y) < 2:
			print('lead_video() return here', REFER_END)
			self.text = f'{VIDEO_LEAD}'
			return
		url = y[0]
		self.mdtext = y[1]
		d = {
			"video":{
				"title":title,
				"url":url
			}
		}
		self.result.append(d)

	def lead_audio(self):
		x = self.mdtext.split(REFER_SPLIT,1)
		if len(x)<2:
			self.text = f'{AUDIO_LEAD}'
			return
		title = x[0]
		y = x[1].split(REFER_END,1)
		if len(y) < 2:
			self.text = f'{AUDIO_LEAD}'
			return
		url = y[0]
		d = {
			"audio":{
				"title":"title",
				"url":url
			}
		}
		self.result.append(d)
		l = len(title) + 2 + len(url)
		self.mdtext = self.mdtext[l:]

	def lead_image(self):
		x = self.mdtext.split(REFER_SPLIT,1)
		if len(x)<2:
			self.text = f'{IMAGE_LEAD}'
			return
		title = x[0]
		y = x[1].split(REFER_END,1)
		if len(y) < 2:
			self.text = f'{IMAGE_LEAD}'
			return
		url = y[0]
		d = {
			"image":{
				"title":"title",
				"url":url
			}
		}
		self.result.append(d)
		self.mdtext = y[1]

	def check_key(self,t):
		for k in self.mdkeys:
			if t.startswith(k):
				return k
		return None

	def parse(self):
		"""
		parser parse mdtext, recognize bbtext, source, img, audio, video
		part text
		"""
		while len(self.mdtext) > 0:
			k = self.check_key(self.mdtext)
			if k is None:
				self.text = f'{self.text}{self.mdtext[0]}'
				self.mdtext = self.mdtext[1:]
			else:
				if len(self.text) > 0:
					self.result.append({'pure_md':self.text})
					self.text = ""
				self.mdtext = self.mdtext[len(k):]
				self.mdkey_handler[k]()
		if len(self.text) > 0:
			self.result.append({'pure_md':self.text})
			self.text = ''
		return self.result	

class MDImage(AsyncImage):
	parent_width=NumericProperty(None)
	image_size = ListProperty(None)	
	def __init__(self, **kw):
		super().__init__(**kw)
		self.image_size = None
		self.parent_width = None
		self.bind(texture=self.image_loaded)

	def image_loaded(self,o, *args):
		self.image_size = self.texture.size

	def resize(self, *args):
		ps = [0,0,0,0]
		if hasattr(self.parent, 'padding'):
			ps = self.parent.padding
		self.parent_width = self.parent.width - ps[0] - ps[2]
		if self.texture:
			if self.texture.size:
				self.image_size = self.texture.size
		Logger.info('MDImage:resize called, %d', self.parent_width)

	def on_image_size(self, *args):
		if self.parent_width:
			Logger.info('MDImage:on_iage_size %d, %s', self.parent_width,
						self.image_size)
			self.set_image_size()

	def on_parent_width(self, *args):
		if self.image_size:
			Logger.info('MDImage:on_oarent_width %d, %s', self.parent_width,
						self.image_size)
			self.set_image_size()

	def set_image_size(self):
		self.width = self.parent_width
		self.height = self.parent_width * self.image_size[1] \
							/ self.image_size[0]
		Logger.info('MDImage:set_image_size %d, %d', self.width,
					self.height)
	
class MarkdownBody(VBox):
	def __init__(self, md_obj=None, padding=[10,10,10,10], **kw):
		self.part_handlers = {
			"pure_md":self.build_pure_md,
			"source":self.build_source,
			"image":self.build_image,
			"video":self.build_video,
			"audio":self.build_audio
		}
		self.md_obj = md_obj
		super().__init__(**kw)
		self.padding=padding
		self.size_hint = None,None
		self.bind(parent=self.resize)
		self.resize()
	
	def show_mdtext(self, mdtext):
		mdp = MarkdownParser(mdtext)
		parts = mdp.parse()
		self.clear_widgets()
		for p in parts:
			for k,v in p.items():
				f = self.part_handlers.get(k)
				f(v)

	def resize(self, *args):
		Logger.info('MDBody:resize called')
		if self.parent:
			ps = [0,0,0,0]
			if hasattr(self.parent, 'padding'):
				ps = self.parent.padding
			h = 0
			for c in self.children:
				if hasattr(c, 'resize'):
					c.resize()
				h += c.height
			self.width = self.parent.width - ps[0] - ps[2]
			self.height = h
		else:
			Logger.info('resize:parent is null')

	def build_source(self,source_desc):
		w = MarkdownBody(md_obj=self.md_obj,
				csscls=self.md_obj.second_css, size_hint_y=None)
		w.show_mdtext(source_desc)
		self.add_widget(w)
		w.resize()

	def build_pure_md(self, mdtext):
		for l in mdtext.split('\n'):
			self.parse_line(l)

	def build_image(self,img_desc):
		w = MDImage(source=img_desc['url'],
					allow_stretch=True,
					size_hint_y=None,
					height=CSize(10),
					keep_ratio=True
			)
		
		self.add_widget(w)
		w.resize()

	def build_video(self, video_desc):
		w = Factory.NewVideo(source=video_desc['url'],
				keep_ratio=True,
				play=False,
				allow_stretch = True,
				size_hint_y=None
		)
		w.height=self.width * 10 / 16
		w.state = 'pause'
		def f1(x):
			x.state = 'play'
		def f2(x):
			x.state = 'stop'

		w.bind(on_enter_focus=f1)
		w.bind(on_leave_focus=f2)
		self.add_widget(w)
		w.resize()
		
	def build_audio(self, audio_desc):
		w = Factory.APlayer(source=audio_desc.url)
		w.bind(minimum_height=w.setter('height'))
		self.add_widget(w)

	def parse_title(self, txt, level):
		w = Factory.Blocks().widgetBuild({
			"widgettype":f"Title{level}",
			"options":{
				"text":txt,
				"size_hint_x":None,
				"width":self.width,
				"size_hint_y":None,
				"markup":True,
				"wrap":True,
				"halign":"left",
				"valign":"middle"
			}
		})
		if not w:
			return
		w1,h1 = w.get_wraped_size()
		clen = CSize(1)
		if h1 is None or h1 < clen:
			h1 = clen
		w.height = h1
		w.bind(on_ref_press=self.md_obj.open_new_md)
		self.add_widget(w)
		
	def parse_line(self, l):
		if l.startswith('###### '):
			t = self.mktext_bbtext(l[7:])
			return self.parse_title(t,6)
		if l.startswith('##### '):
			t = self.mktext2bbtext(l[6:])
			return self.parse_title(t,5)
		if l.startswith('#### '):
			t = self.mktext2bbtext(l[5:])
			return self.parse_title(t,4)
		if l.startswith('### '):
			t = self.mktext2bbtext(l[4:])
			return self.parse_title(t,3)
		if l.startswith('## '):
			t = self.mktext2bbtext(l[3:])
			return self.parse_title(t,2)
		if l.startswith('# '):
			t = self.mktext2bbtext(l[2:])
			return self.parse_title(t,1)
		t = self.mktext2bbtext(l)
		print('markdown.py, t=', t, 'len(t)=', len(t), 'type(t)=', type(t))
		w = Factory.Blocks().widgetBuild({
			"widgettype":"Text",
			"options":{
				"text":t,
				"wrap":True,
				"size_hint_x":None,
				"width":self.width,
				"markup":True,
				"valign":"middle",
				"halign":"left"
			}
		})
		if not w:
			return
		_,h = w.get_wraped_size()
		clen = CSize(1)
		if h is None or h < clen:
			h = clen
		w.height = h
		w.bind(on_ref_press=self.md_obj.open_new_md)
		self.add_widget(w)

	def mktext2bbtext(self,mdtext):
		"""
		convert markdown text to bbtag text kivy Label Widget recognized

		markdown syntax
		*XXX*		Italic
		**XXX**		Bold
		***XXX***	Bold + Italic
		> XXXX		reference
		>> XXXX		reference inside reference
		[-*] XXX	list
		[0-9]*[.] XXX	list
		***			split line
		---			split line
		___			split line
		[.*](url)	link
		![.*](url)	image
		[![.*](url)](url)		image link
		!v[.*](url)	video*		plan to extend
		!a[.*](url) audio*		plan to extend

		bb tag syntax
		[b]XXX[/b]	Bold
		[i]XXX[/i]	Italic
		[sub]XXX[/sub]
		"""
		mdtext = re.sub('\*\*\*(.*)\*\*\*', \
					lambda x: '[b][i]'+x.group(1)+'[/i][/b]', \
					mdtext)

		mdtext = re.sub('\*\*(.*)\*\*', \
					lambda x: '[b]'+x.group(1)+'[/b]', \
					mdtext)

		mdtext = re.sub('\*(.*)\*', \
					lambda x: '[i]'+x.group(1)+'[/i]', \
					mdtext)

		mdtext = re.sub('([^!]?)\[(.*)\]\((.*)\)', \
					lambda x:x.group(1) + '[ref='+x.group(3)+'][color=00ff00]'+ x.group(2)+'[/color][/ref]', \
					mdtext)

		return mdtext


class Markdown(ScrollView):
	"""
# Markdown
MArkdown widget using to render a markdown file and show it
description file format
{
	"widgettype":"Markdown",
	"options":{
		"source": the markdown file
		"first_css":,
		"second_css":
		other options
	}
}
	"""
	source = StringProperty(None)
	first_css = StringProperty("default")
	second_css = StringProperty("default")
	def __init__(self, **kw):
		super(Markdown, self).__init__(**kw)
		self.root_body = MarkdownBody(md_obj=self,
					csscls=self.first_css,
					size_hint_y=None
		)
		self.root_body.bind(minimum_height=self.root_body.setter('height'))
		self.add_widget(self.root_body)
		self.source = kw.get('source')
		if self.source:
			self.load_text()
		self.bind(size=self.root_body.resize)
		self.bind(source=self.load_text)

	def check_parent_window(self, *args):
		pw = self.get_parent_window()
		rw = self.get_root_window()
		print('MD: pw=%s,rw=%s', pw,rw)

	def stop_media(self, *args):
		self.root_body.stp_video()

	def load_text(self, *args):
		h = getDataHandler(self.source)
		h.bind(on_success=self.update)
		h.bind(on_error=self.show_error)
		h.handle()

	def show_error(self, o, e):
		print('load_text(), error=',e)

	def update(self, o, text):
		print('text=',text, type(text))
		if not text:
			return
		text = ''.join(text.split('\r'))
		self.root_body.show_mdtext(text)
		self.scroll_y = 1

	def open_new_md(self, o, value):
		print(value,'is it a link')
		self.source = value
