#!/usr/bin/env python3
###
#This class returns a login_grid whith a standarized login form
###
import os,sys,socket
import threading
import netifaces 
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('PangoCairo', '1.0')
import json
import cairo
#import commands
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject, GLib, PangoCairo, Pango
try:
	import xmlrpclib as n4d
except ImportError:
	raise ImportError("xmlrpc not available. Disabling server queries")
import time
import gettext

gettext.textdomain('edupals.ui.common')
_ = gettext.gettext
GObject.threads_init()

class N4dGtkLogin(Gtk.Box):
	__gtype_name__='n4dgtklogin'

	def __init__(self,*args,**kwds):
		super(Gtk.Box,self).__init__(*args,**kwds)
		self.dbg=True
		self.msgCount=0
		self.vertical=False
		if 'orientation' in kwds.keys():
			if kwds['orientation']==Gtk.Orientation.VERTICAL:
				self.vertical=True
		self.sw_n4d=True
		if hasattr(sys,'last_value'):
		#If there's any error at this point it only could be an ImportError caused by xmlrpc
			self.sw_n4d=False
		self.css_classes={'#GtkEntry':'{font-family: Roboto;border:0px;border-bottom:1px grey solid;margin-top:0px;padding-top:0px;}'}
		self.style_provider=Gtk.CssProvider()
		Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),self.style_provider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
		self.default_spacing=12
		self.username_placeholder=_("Username")
		self.server_placeholder=_("Server IP (Default value : server)")
		self.default_server='server'
		self.login_banner_default="llx-avatar"
		self.info_banner=None
		self.allowed_groups=[]
		#internal boxes
		self.form_box=Gtk.Box(spacing=0,orientation=Gtk.Orientation.VERTICAL)
		self.info_box=Gtk.Box(spacing=self.default_spacing,orientation=Gtk.Orientation.VERTICAL)
		self.main_grid=Gtk.Grid()
		self.render_form()
	#def __init__

	def _debug(self,msg):
		if self.dbg:
			print("%s",msg)

	def set_allowed_groups(self,groups):
		self.allowed_groups=groups
	#def set_allowed_groups

	def set_mw_proportion_ratio(self,left_panel,right_panel):
		for child in self.main_grid.get_children():
			self.main_grid.remove(child)
		if self.vertical:
			self.main_grid.attach(self.form_box,0,0,1,left_panel)
			self.main_grid.attach(self.info_box,0,0+left_panel,1,right_panel)
			self.main_grid.set_margin_bottom(12)
		else:
			self.main_grid.attach(self.info_box,0,0,left_panel,1)
			self.main_grid.attach(self.form_box,0+left_panel,0,right_panel,1)
	#def set_mw_proportion_ratio
	
	def set_mw_background(self,image=None,cover=False,from_color='#ffffff',to_color='silver',gradient='linear'):
		mw_background=self._set_background(image,cover,from_color,to_color,gradient)
		self.css_classes['#mw']='{'+mw_background+';;}'
		self._set_css()
	#def set_mw_background

	def set_login_background(self,image=None,cover=False,from_color='#ffffff',to_color='@silver',gradient='linear'):
		form_background=self._set_background(image,cover,from_color,to_color,gradient)
		self.css_classes['#main']='{'+form_background+';;}'
		self._set_css()
	#def set_login_background

	def set_info_background(self,image=None,cover=False,from_color='#ffffff',to_color='@silver',gradient='linear'):
		info_background=self._set_background(image,cover,from_color,to_color,gradient)
		self.css_classes['#info']='{'+info_background+';;}'
		self._set_css()
	#def set_info_background

	def _set_css(self):
		css=''
		for css_class,style in self.css_classes.items():
			css=css+css_class+' '+style
		css_style=eval('b"""'+css+'"""')
		self.style_provider.load_from_data(css_style)
	#def _set_css

	def _set_background(self,image=None,cover=False,from_color='#ffffff',to_color='silver',gradient='linear'):
		bg=''
		if image and os.path.isfile(image):
			if cover:
				bg='background-image:url("'+image+'"); background-repeat:no-repeat; background-size:100% 100%'
			else:
				bg='background-image:url("'+image+'"); background-repeat:no-repeat;'
		elif image:
			self._debug("%s not found. Searching..."%image)
			#try to locate the image in the default theme
			icon_theme=Gtk.IconTheme.get_default()
			icon_sizes=icon_theme.get_icon_sizes(image)
			if icon_sizes:
				max_size=max(icon_sizes)
				icon=icon_theme.lookup_icon(image,max_size,0)
				icon_path=icon.get_filename()
				bg='background-image:url("'+icon_path+'"); background-repeat:no-repeat; background-size:100% 100%'

		else:
			if gradient=='linear':
				bg='background-image:-gtk-gradient (linear, left top, left bottom, from ('+from_color+'),  to ('+to_color+'))'
			elif gradient=='radial':
				bg='background-image:-gtk-gradient (radial, center center,0,center center,1, from ('+from_color+'),  to ('+to_color+'))'
		return bg
	#def _set_background

	def set_default_username(self,username):
		self.username_placeholder=username
		self._set_text_for_entry(self.txt_username,username)
	#def set_default_username

	def set_default_server(self,server):
		self.server_placeholder=server
		self.default_server=server
		if self.txt_server:
			self._set_text_for_entry(self.txt_server,server)
	#def set_default_server
	
	def _set_text_for_entry(self,widget,text):
		widget.set_placeholder_text(text)
	#def _set_text_for_entry

	def _lookup_user_face(self):
		sw_ok=False
		if os.path.isfile(os.path.expanduser('~/.face')):
			sw_ok=True
			self.set_login_banner(os.path.expanduser('~/.face'))
		return sw_ok
	#def _lookup_user_face

	def set_login_banner(self,image=None):
		avatar=None
		if image:
			avatar=self._get_image(image)
			if avatar==None:
				avatar=self._get_image("avatar-default")
			self.login_banner.set_from_pixbuf(avatar)
	#def set_banner

	def set_info_banner(self,banner,x=72,y=72):
		self.info_banner.set_from_pixbuf(self._get_image(banner))
	#def set_info_banner

	def _get_image(self,image,x=72,y=72):
		pixbuf=None
		icon_theme=Gtk.IconTheme.get_default()
		if icon_theme.has_icon(image):
			pixbuf=icon_theme.load_icon(image,x,0)
		else:
			if os.path.isfile(image):
				pixbuf=GdkPixbuf.Pixbuf.new_from_file_at_scale(image,x,y,True)
		return pixbuf
	#def _get_image
	
	def set_info_text(self,title,subtitle,text):
		sw_ok=True
		info_msg=''
		self.lbl_info_msg.set_width_chars(25)
		self.lbl_info_msg.set_max_width_chars(25)
		try:
			msg="<b><big>"+title+"</big></b>"
			info_msg=msg+'\n'+subtitle+'\n'+text
		except Exception as e:
			sw_ok=False
			print(e)
		self.lbl_info_msg.set_markup(info_msg)
		self.lbl_info_msg.show()
		return sw_ok
	#def set_info_text

	def hide_server_entry(self):
		self.txt_server.props.no_show_all=True
		self.txt_server.hide()
		return True
	#def hide_server_entry

	def show_info(self,msg):
		if self.msgCount<10:
			self.msgCount+=1
		elif self.rev_info.get_reveal_child():
			self.rev_info.set_transition_type(Gtk.RevealerTransitionType.SLIDE_UP)
			self.rev_info.set_reveal_child(False)
			self.msgCount=0
			return False
		self.rev_info.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN)
		self.rev_info.set_reveal_child(True)
		return True
    #def show_info

	def hide_info_box(self):
		self.info_box.props.no_show_all=True
		self.info_box.hide()
	#def hide_info_box

	def get_action_area(self):
		return self.info_box
	#def get_action_area

	def render_form(self):
		self.scr=Gtk.ScrolledWindow()
		self.scr.set_policy(Gtk.PolicyType.AUTOMATIC,Gtk.PolicyType.AUTOMATIC)
		self.scr.set_propagate_natural_height(True)
		self.scr.set_propagate_natural_width(True)
		sb=self.scr.get_vscrollbar()
		sb.hide()
		self.main_grid.set_hexpand(True)
		self.main_grid.set_vexpand(True)

		self.main_grid.set_column_homogeneous(True)
		self.main_grid.set_row_homogeneous(True)
		self._render_login_form()
		self._render_info_form()
		if self.vertical:
			self.main_grid.attach(self.form_box,1,1,1,1)
			self.main_grid.attach(self.info_box,1,3,1,1)
			self.main_grid.set_margin_bottom(12)
			self.scr.set_valign(Gtk.Align.START)
			self.pack_start(self.scr,False,False,0)
		else:
			self.main_grid.attach(self.info_box,1,1,2,1)
			self.main_grid.attach(self.form_box,3,1,1,1)
			self.pack_start(self.scr,True,True,0)
		self.scr.add(self.main_grid)
		self.set_name("mw")
		self.form_box.set_name("main")
	#def render_form

	def _render_login_form(self):
		self.txt_username=Gtk.Entry()
		self.txt_username.set_name("GtkEntry")
		self.login_banner=Gtk.Image()
		if not self._lookup_user_face():
			self.set_login_banner(self.login_banner_default)
		self.login_banner.set_margin_bottom(self.default_spacing)
		self.set_default_username(self.username_placeholder)
		self.txt_username.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,"system-users")
		self.txt_password=Gtk.Entry()
		self.txt_password.set_name("GtkEntry")
		self.txt_password.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,"changes-prevent")
		self._set_text_for_entry(self.txt_password,_("Password"))
		self.txt_server=Gtk.Entry()
		self.txt_server.set_name("GtkEntry")
#		self.set_default_server(self.server_placeholder)
		self.txt_server.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,"network-wired")
		self.btn_sign=Gtk.Button(stock=Gtk.STOCK_OK)
		self.btn_sign.connect('clicked',self._validate)
		self.btn_sign.set_halign(Gtk.Align.CENTER)
		login_grid=Gtk.Grid()
		login_grid.set_column_spacing(6)
		login_grid.set_row_spacing(6)
		login_grid.set_margin_left(self.default_spacing)
		login_grid.set_margin_right(self.default_spacing)
		login_grid.set_margin_top(self.default_spacing)
		login_grid.set_margin_bottom(self.default_spacing)
		self.spinner=Gtk.Spinner()
		self.spinner.set_name("spinner")
		if self.vertical:
			position=Gtk.PositionType.RIGHT
		else:
			position=Gtk.PositionType.BOTTOM

		login_grid.attach(self.spinner,0,1,2,5)
		login_grid.attach(self.txt_username,0,1,1,1)
		self._set_widget_default_props(self.txt_username,_("Username"))
		self.txt_username.connect('activate',self._validate)
		login_grid.attach_next_to(self.txt_password,self.txt_username,position,1,1)
		self._set_widget_default_props(self.txt_password,_("Password"))
		self.txt_password.set_visibility(False)
		self.txt_password.props.caps_lock_warning=True
		self.txt_password.connect('activate',self._validate)
		self._set_widget_default_props(self.txt_server,_("Master Server IP"))
		if self.vertical:
			self.txt_server.set_halign(Gtk.Align.FILL)
			self.txt_server.set_hexpand(True)
			login_grid.attach_next_to(self.txt_server,self.txt_username,Gtk.PositionType.BOTTOM,2,1)
		else:
			login_grid.attach_next_to(self.txt_server,self.txt_password,Gtk.PositionType.BOTTOM,1,1)
		self.txt_server.connect('activate',self._validate)
		self.btn_sign.set_margin_top(self.default_spacing)
		login_grid.attach_next_to(self.btn_sign,self.txt_server,Gtk.PositionType.BOTTOM,3,1)
		login_grid.attach(self.login_banner,0,0,3,1)
		self.rev_info=Gtk.Revealer()
		self.rev_info.set_transition_duration(1000)
		self.rev_info.set_valign(Gtk.Align.START)
		self.rev_info.set_name("revealer")
		if not '#revealer' in self.css_classes.keys():
				self.css_classes['#revealer']='{background-color:red;color:white;padding:3px;}'
		self.lbl_error=Gtk.Label(_("Login failed"))
		self.lbl_error.set_name("revealer")
		self.rev_info.add(self.lbl_error)
		login_grid.attach(self.rev_info,0,0,3,1)
		if self.vertical:
			login_grid.props.valign=Gtk.Align.END
			login_grid.set_margin_bottom(0)
			self.btn_sign.set_margin_bottom(0)
		else:
			login_grid.props.valign=Gtk.Align.CENTER
		login_grid.props.halign=Gtk.Align.CENTER
		self.form_box.pack_start(login_grid,True,True,0)
	#def _render_login_form

	def _render_info_form(self):
		self.info_box.set_homogeneous(False)
		self.info_banner=Gtk.Image()
		info_detail_box=Gtk.Box(spacing=self.default_spacing,orientation=Gtk.Orientation.VERTICAL)
		info_detail_box.pack_start(self.info_banner,False,False,0)
		self.lbl_info_msg=Gtk.Label()
		self.lbl_info_msg.set_use_markup(True)
		self.lbl_info_msg.set_line_wrap(True)
		info_detail_box.pack_start(self.lbl_info_msg,True,True,0)
		if not '#label' in self.css_classes.keys():
			self.css_classes['#label']='{background-color:rgba(200,200,200,0.8);;}'
		self.lbl_info_msg.set_name("label")
		self.lbl_info_msg.set_no_show_all(True)
		self.lbl_info_msg.hide()
		self.info_box.set_name("info")
		if self.vertical:
			self.lbl_info_msg.set_justify(Gtk.Justification.CENTER)
			self.lbl_info_msg.set_margin_left(12)
			self.lbl_info_msg.set_margin_right(12)
			info_detail_box.props.valign=Gtk.Align.START
		else:
			info_detail_box.props.valign=Gtk.Align.CENTER
			info_detail_box.props.halign=Gtk.Align.CENTER
		if self.vertical:
			self.info_box.pack_start(info_detail_box,False,False,0)
		else:
			self.info_box.pack_start(info_detail_box,True,True,0)
		self._set_css()
	#def _render_info_form

	def set_label_background(self,r,g,b,a):
		self.css_classes['#label']='{background-color:rgba(%s,%s,%s,%s);;}'%(r,g,b,a)

	def _set_widget_default_props(self,widget,tooltip=None):
		widget.set_valign(True)
		widget.set_halign(True)
		widget.set_tooltip_text(tooltip)
	#def _set_widget_default_props

	def _status_info_hide(self,*args):
		self.rev_info.hide()
	#def _info_hide

	def _validate(self,widget=None):
		height=self.get_allocation().height

		self.scr.set_min_content_height(height)
		self.scr.set_propagate_natural_height(False)
		self.scr.set_propagate_natural_width(False)
		self.scr.set_max_content_height(height+1)
		user=self.txt_username.get_text()
		pwd=self.txt_password.get_text()
		server=self.txt_server.get_text()
		server_ip=''
		if not server:
			server=self.default_server
		try:
			server_ip=socket.gethostbyname(server)
		except:
			server='localhost'
		#Check if localhost is server
		for iface in netifaces.interfaces():
			for key,netinfo in netifaces.ifaddresses(iface).items():
				for info in netinfo:
					if info['addr']==server_ip:
						server='localhost'
						break

		self.form_box.set_sensitive(False)
		self.spinner.start()
		self.th=threading.Thread(target=self._t_validate,args=[user,pwd,server])
		self.th.start()
	#def _validate
	
	def _t_validate(self,user,pwd,server):
		Gdk.threads_enter()
		ret=[False]
		msg=_("Login failed")
		if self.sw_n4d:
			try:
				self.n4dclient=self._n4d_connect(server)
				ret=self.n4dclient.validate_user(user,pwd)
			except socket.error as e:
				msg=_("Unknown host")
				ret=[False,str(e)]

		if (isinstance(ret,bool)):
			pass
		elif not ret[0]:
			pass
		elif self.allowed_groups and not set(self.allowed_groups).intersection(ret[1]):
			#Check user groups
			msg=_("User not allowed")
		else:
			msg=""
		GLib.idle_add(self._validate_result,msg,user,pwd,server)
		Gdk.threads_leave()
		return(False)
	#def _t_validate
	
	def _validate_result(self,msg,user,pwd,server):
		self.spinner.stop()
		self.form_box.set_sensitive(True)
		self.th.join()
		if msg:
			GLib.timeout_add(500,self.show_info,(msg))
			self.txt_server.props.no_show_all=False
			self.txt_server.show()
		else:
			self.after_validate(user,pwd,server)
		return False

	def after_validation_goto(self,func,data=None):
		self.after_validate=func
	#def after_validation_func

	def _n4d_connect(self,server):
		try:
			socket.gethostbyname(server)
		except:
			#It could be an ip
			try:
				socket.inet_aton(server)
			except Exception as e:
				print(e)
				raise
		c = n4d.ServerProxy("https://"+server+":9779")
		return c
	#def _n4d_connect
