import ugfx, badge, network, gc, time, urequests, appglue

import uos
import utime

# SHA2017 Badge installer
# V3 Edwin Eefting (psy0rz)
# V2 Thomas Roos
# V1 Niek Blankers

def draw_progress(msg):
    """draw progress data (downloading/errors etc). will also flush"""
    ugfx.area(148,110,148,18, ugfx.BLACK)
    ugfx.string_box(148,110,148,18, str(msg), "Roboto_Regular12", ugfx.WHITE, ugfx.justifyLeft)
    ugfx.flush()
    return

def draw_status(msg):
    """update status text (e.g. 'ready') will not flush. uses same area as draw progress"""
    ugfx.area(148,110,148,18, ugfx.WHITE)
    ugfx.string_box(148,110,148,18, str(msg), "Roboto_Regular12", ugfx.BLACK, ugfx.justifyLeft)
    return


def connectWiFi():
    """makes sure wifi is connected, otherwise raises exception"""
    nw = network.WLAN(network.STA_IF)
    if not nw.isconnected():
        nw.active(True)
        ssid = badge.nvs_get_str('badge', 'wifi.ssid', 'SHA2017-insecure')
        password = badge.nvs_get_str('badge', 'wifi.password')
        nw.connect(ssid, password) if password else nw.connect(ssid)

        draw_progress("Connecting...")

        timeout = 150
        while not nw.isconnected():
            time.sleep(0.1)
            timeout = timeout - 1
            if (timeout<1):
                raise("Timeout!")
                nw.active(True)


def show_description(active):
    if active:
        global text
        if options.selected_index()>0 and options.selected_index()<len(packages):
            text.text(packages[options.selected_index()]["description"])
            ugfx.flush()

def select_category(active):
	if active:
		global categories
		global options
		index = options.selected_index()
		if categories[index]["eggs"] > 0:
			category = categories[index]["slug"]
			list_apps(category)

def list_apps(slug, refresh=False):
	global options
	global text
	global packages

	if refresh:
		cache_clear()
    
	ugfx.input_attach(ugfx.JOY_UP, 0)
	ugfx.input_attach(ugfx.JOY_DOWN, 0)
	ugfx.input_attach(ugfx.BTN_A, 0)
	ugfx.input_attach(ugfx.BTN_B, 0)
	ugfx.input_attach(ugfx.BTN_START, 0)

	while options.count() > 0:
		options.remove_item(0)


	try:
		age=None
		url="https://badge.sha2017.org/eggs/category/%s/json" % slug
		packages = cache_get_json(url)
		age = cache_get_age(url)
	except Exception as e:
		draw_progress(e)
  		packages = []

	for package in packages:
		options.add_item("%s rev. %s" % (package["name"], package["revision"]))

	if packages:
		options.selected_index(0)

	ugfx.input_attach(ugfx.JOY_UP, show_description)
	ugfx.input_attach(ugfx.JOY_DOWN, show_description)
	ugfx.input_attach(ugfx.BTN_A, install_app)
	ugfx.input_attach(ugfx.BTN_B, lambda pushed: list_categories() if pushed else False)
	ugfx.input_attach(ugfx.BTN_START, lambda pushed: appglue.start_app('') if pushed else False)
	ugfx.input_attach(ugfx.BTN_SELECT, lambda pushed: list_apps(slug,True) if pushed else False)

    #no download error?
	if age!=None:
		show_description(True)
		if age:
			draw_status("cached: "+str(age)+"m (SEL=refresh)")
		else:
			draw_status("Ready")
	
	ugfx.flush(ugfx.LUT_NORMAL)
	gc.collect()

def start_categories(pushed):
	if pushed:
		list_categories()

def start_app(pushed):
	if pushed:
		global selected_app
		appglue.start_app(selected_app)

def install_app(active):
	if active:
		global options
		global text
		global packages
		global selected_app

		connectWiFi()
        
		index = options.selected_index()

		ugfx.input_attach(ugfx.JOY_UP, 0)
		ugfx.input_attach(ugfx.JOY_DOWN, 0)
		ugfx.input_attach(ugfx.BTN_A, 0)
		ugfx.input_attach(ugfx.BTN_B, 0)
		ugfx.input_attach(ugfx.BTN_START, 0)

		ugfx.clear(ugfx.BLACK)
		ugfx.string(40,25,"Installing:","Roboto_BlackItalic24",ugfx.WHITE)
		ugfx.string(100,55, packages[index]["name"],"PermanentMarker22",ugfx.WHITE)
		ugfx.flush()

		import woezel
		selected_app =  packages[index]["slug"]
		woezel.install(selected_app)

		ugfx.clear(ugfx.WHITE)
		ugfx.string(40,25,"Installed:","Roboto_BlackItalic24",ugfx.BLACK)
		ugfx.string(100,55, packages[index]["name"],"PermanentMarker22",ugfx.BLACK)
		ugfx.string(0, 115, "[ A: START | B: BACK ]", "Roboto_Regular12", ugfx.BLACK)

		ugfx.input_attach(ugfx.BTN_A, start_app)
		ugfx.input_attach(ugfx.BTN_B, start_categories)
		ugfx.input_attach(ugfx.BTN_START, lambda pushed: appglue.start_app("") if pushed else False)

		ugfx.flush(ugfx.LUT_NORMAL)
		gc.collect()

def list_categories(refresh=False):
	global options
	global text
	global categories

	if refresh:
		cache_clear()      
    
	ugfx.input_init()
	try:
		url="https://badge.sha2017.org/eggs/categories/json"
		age=None
		categories=cache_get_json(url)
		age=cache_get_age(url)
	except Exception as e:
		draw_progress(e)
		categories=[]

	options = ugfx.List(0,0,int(ugfx.width()/2),ugfx.height())
	text = ugfx.Textbox(int(ugfx.width()/2),0, int(ugfx.width()/2), ugfx.height())

	ugfx.input_attach(ugfx.JOY_UP, lambda pushed: ugfx.flush() if pushed else False)
	ugfx.input_attach(ugfx.JOY_DOWN, lambda pushed: ugfx.flush() if pushed else False)
	ugfx.input_attach(ugfx.BTN_A, select_category)
	ugfx.input_attach(ugfx.BTN_B, lambda pushed: appglue.start_app("launcher", False) if pushed else False)
	ugfx.input_attach(ugfx.BTN_START, lambda pushed: appglue.start_app("") if pushed else False)
	ugfx.input_attach(ugfx.BTN_SELECT, lambda pushed: list_categories(True) if pushed else False)

	#ugfx.clear(ugfx.WHITE)
	#ugfx.flush()

	while options.count() > 0:
		options.remove_item(0)
	for category in categories:
		options.add_item("%s (%d) >" % (category["name"], category["eggs"]))
	options.selected_index(0)

	text.text("Install or update eggs from the hatchery here\n\n\n\n")
	ugfx.string_box(148,0,148,26, "Hatchery", "Roboto_BlackItalic24", ugfx.BLACK, ugfx.justifyCenter)
	ugfx.line(148, 64, 296, 64, ugfx.BLACK)
	ugfx.string_box(148,64,148,18, " A  : Open category", "Roboto_Regular12", ugfx.BLACK, ugfx.justifyLeft)
	ugfx.string_box(148,78,148,18, " B  : Return to home", "Roboto_Regular12", ugfx.BLACK, ugfx.justifyLeft)
	ugfx.string_box(148,92,148,18, " SEL: Refresh", "Roboto_Regular12", ugfx.BLACK, ugfx.justifyLeft)
	ugfx.line(148, 110, 296, 110, ugfx.BLACK)
	#ugfx.string_box(148,110,148,18, " badge.sha2017.org", "Roboto_Regular12", ugfx.BLACK, ugfx.justifyLeft)
    
	if age!=None:
		if age:
			draw_status("cached: "+str(age)+"m (SEL=refresh)")
		else:
			draw_status("Ready")
                  
	ugfx.flush(ugfx.LUT_NORMAL)
	gc.collect()

def cache_get_filename(url):
  global installer_cache_dir
  return(installer_cache_dir+"/"+url.replace("/","_").replace(":","_"))
  
    
def cache_get(url, refresh=False):
  """get cached contents of url, or redownloads if it doenst exist, of if refresh=True
  
  """
  global installer_cache_dir
  global installer_cache
  
  url_filename=cache_get_filename(url)

  #use cache
  if refresh==False:
    try:
      with open(url_filename,'r') as fh:
        return(fh.read())
    except:
      refresh=True
      
  #reload/dont use cache    
  if refresh:
    connectWiFi()
    
    draw_progress('Downloading...')
    
    url_fh=urequests.get(url)
    content=url_fh.content
    url_fh.close()

    #cache content
    with open(url_filename,'w') as fh:
      fh.write(content)

    #uos.stat seemed buggy, so just store time..
    with open(url_filename+".time",'w') as fh:
      fh.write(str(utime.time()))
      
    return(content)

def cache_get_age(url):
  """get age of cached data in minutes"""

  #note: uos.stat seemed buggy
  url_filename=cache_get_filename(url)+".time"
  with open(url_filename,'r') as fh:
    delta=utime.time()-int(fh.read())
    return(int(delta/60))
  
  

def cache_get_json(url, refresh=False):
  """uses cache_get and parses json data"""
  import ujson
  return ujson.loads(cache_get(url, refresh))

 
def cache_prepare():
  global installer_cache_dir

  installer_cache_dir="/media/installer_cache"
  try:
    #we dont have os.path.exists, so just yolooo
    uos.mkdir(installer_cache_dir)
  except:
    pass

  
def cache_clear():
  global installer_cache_dir
  draw_progress("Clearing cache..")
  for file in uos.listdir(installer_cache_dir):
    uos.remove(installer_cache_dir+"/"+file)
    
    
  

try:
    cache_prepare()

    ugfx.clear(ugfx.WHITE)
    ugfx.set_lut(ugfx.LUT_FASTER)

    list_categories()

except Exception as e: 
    #probably a bug:
    draw_progress(str(e))
    #dont re-raise it in the hope the user can continue
    #raise e