#####################################################################
#                                                                   #
#  THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE   #
# LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet )  #
# FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk )      #
# WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
# IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
# OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE.        #
#                                                                   #
#      ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS       #
# (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK.  #
# YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
# THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER    #
# VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG   #
# WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ).         #
#                                                                   #
# THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE   #
# NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
# THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT    #
# SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
# THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT.       #
#                                                                   #
# THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL  #
# FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE   #
# THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD   #
# TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT.  #
#                                                                   #
#####################################################################

import os
import time
import urllib.request
import threading
import json

from subprocess import *
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Pango
from gi.repository import GdkPixbuf
from PIL import Image, ImageSequence

from flbry import markdown
from flbry import data_view
from flbry import ui
from flbry import fetch
from flbry import claim_search
from flbry import comments

def resolve(w, win, url):
    
    # This function will draw a widget of rsolved url


    #####################################################
    #              RESOLVING PART ( NO GTK )            #
    #####################################################


    out = fetch.lbrynet("resolve",
                        {"urls":[url]}
    )
    out = out[url]


    # Saving the resolved thing into the win for later use
    win.resolved = out
    
    # out = check_output(["flbry/lbrynet",
    #                      "resolve", url])
    # raw_data = out.decode("utf-8")
    
    # # Now we want to parse the json

    # try:
    #     out = json.loads(out)
    #     out = out[url]
    # except:
    #     print("Resolve Failed")
    #     return False
    try:
        is_channel = "value_type" in out and out["value_type"] == "channel"
        percentage = downloaded(out["claim_id"])
    except:
        # If this fails we activate search
        return ["search", False, False, w, win, url]
    
    # make sure the url path starts with lbry://
    if not url.startswith("lbry://"):
        url = "lbry://"+url
        win.url.set_text(url)

    return [out, is_channel, percentage, w, win, url]

def render_resolve(data):

    out, is_channel, percentage, w, win,  url = data


    # It could be failed
    if out == "search":
        return ui.load(win, claim_search.find, claim_search.render, win, win.url.get_text())

    
    
    #####################################################
    #              DRAWING PART ( GTK )                 #
    #####################################################
    try:
        price = out["value"]["fee"]["amount"]
    except:
        price = 0
    try:
        currency = out["value"]["fee"]["currency"]
    except:
        currency = "LBC"
    
        
    
    box = Gtk.HBox()
    outbox = Gtk.VBox()

    #### THUMBNAIL ###

    thumb = ""
    try:
        thumb = out["value"]["thumbnail"]["url"]
        thumb_url = ui.image_save_name(thumb)
        def thumb_open(w):
            os.system("xdg-open "+thumb_url)

        thumb_button = Gtk.Button()
        thumb_button.set_tooltip_text(thumb)
        if is_channel:
            thumb_image = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumb, 150, "", True)
        else:
            thumb_image = ui.load(win, ui.net_image_calculation, ui.net_image_render, thumb, 400,  "", True)
        thumb_button.add(thumb_image)
        thumb_button.set_relief(Gtk.ReliefStyle.NONE)
        thumb_button.connect("clicked", thumb_open)

        box.pack_start(thumb_button, False, False, False)
    except:
        pass
        
    outbox.pack_start(box, False, False, False)

    from_right_to_thumbnail = Gtk.VBox()
    box.pack_end(from_right_to_thumbnail, True, True, False)

    # If channel load banner
    if is_channel:
        if "cover" in out["value"]:
            channel_banner = ui.load(win, ui.net_image_calculation, ui.net_image_render, out["value"]["cover"]["url"], False, "", True)
            channel_scroll = Gtk.ScrolledWindow()
            channel_scroll.set_size_request(400,150)
            channel_scroll.add_with_viewport(channel_banner)
            from_right_to_thumbnail.pack_start(channel_scroll, False, False, False)
        else:
            try:
                channel_scroll.destroy()
            except:
                pass
            
    
    #### NAME / CHANNEL ####

    the_packing_box = from_right_to_thumbnail
    if is_channel:
        the_packing_box = outbox
    
    name_channel_box = Gtk.VBox()
    the_packing_box.pack_start(name_channel_box, False, False, False)

    # Channel

    if "signing_channel" in out:
        name_channel_box.pack_start(ui.go_to_channel(win, out["signing_channel"]),False,False,False)
    
    # name it self
    title = out["name"]
    try:
        title = out["value"]["title"]
    except:
        pass
    title_label = Gtk.Label()
    title_label.set_line_wrap_mode( Gtk.WrapMode.WORD )
    title_label.set_line_wrap(True)
    title_label.set_markup('<span size="x-large">  '+title+'</span> ')
    title_label.set_selectable(True)
    title_label.set_css_name("")
    name_channel_box.pack_start(title_label, False, False, False)

    ################# TOOL BAR ##################


    toolbox = Gtk.HBox()
    the_packing_box.pack_start(toolbox, False, False,False)

    if not is_channel:

        def download_action(w):
            start_downloading(url)
        
        download_button = Gtk.Button()
        download_button.connect("clicked", download_action)
        download_button.set_relief(Gtk.ReliefStyle.NONE)
        download_box = Gtk.HBox()
        download_button.add(download_box)
        download_icon = ui.icon(win, "download")
        download_box.pack_start(download_icon, False, False, False)
        #filesize
        labeltext = "Download"
        if price:
            labeltext = "Buy for "+str(price)+" "+currency
        try:
            filesize = out["value"]["source"]["size"]
            labeltext = labeltext + " ("+csize(filesize)+")"
        except:
            filesize = 0
        
        download_box.pack_start(Gtk.Label(labeltext), False, False, False)
        toolbox.pack_start(download_button, False,False,False)

        download_bar = Gtk.ProgressBar()
        the_packing_box.pack_start(download_bar, False, False,False)

        def delete_action(w):
            filename = get_downloaded_file(out["claim_id"])
            delete_file(out["claim_id"])

            try:
                os.remove(filename)
            except:
                pass
            
        delete_button = Gtk.Button()
        delete_button.connect("clicked", delete_action)
        delete_button.set_relief(Gtk.ReliefStyle.NONE)
        delete_box = Gtk.HBox()
        delete_button.add(delete_box)
        delete_icon = ui.icon(win,"delete")
        delete_box.pack_start(delete_icon, False, False, False)
        delete_box.pack_start(Gtk.Label("Delete"), False, False, False)
        toolbox.pack_start(delete_button, False,False,False)

        def launch_action(w):
            Popen(["xdg-open", get_downloaded_file(out["claim_id"])])

        launch_button = Gtk.Button()
        launch_button.connect("clicked", launch_action)
        launch_button.set_relief(Gtk.ReliefStyle.NONE)
        launch_box = Gtk.HBox()
        launch_button.add(launch_box)
        launch_icon = ui.icon(win, "launch")
        launch_box.pack_start(launch_icon, False, False, False)
        launch_box.pack_start(Gtk.Label("Launch"), False, False, False)
        toolbox.pack_start(launch_button, False,False,False)

        win.check_download_daemon_keep_alive = True
        
        t = threading.Thread(target=downloading_check_thread, args=(win, out["claim_id"],download_button, delete_button, launch_button, download_bar))
        t.setDaemon(True)
        t.start()
        def kill_daemon(w):
            win.check_download_daemon_keep_alive = False
        download_button.connect("destroy", kill_daemon)
    
    ########### BOTTOM NOTEBOOK ##############

    notebook = Gtk.Notebook()
    outbox.pack_start(notebook, True, True,False)

    # If article read article
    try:
        if out["value"]["source"]["media_type"] == "text/markdown" and price == 0:

            # We download it first
            
            playout = fetch.lbrynet("get", {"uri":url, "save_file":True})

            # playout = check_output(["flbry/lbrynet",
            #                             "get", url])
            # # Parsing the Json
            # playout = json.loads(playout)

            md_text = open(playout['download_path'])
            
            md_text = md_text.read()

            
            

            # Markdown covenreted
            md_scrl = Gtk.ScrolledWindow()
            md_view = Gtk.TextView()
            md_view.set_wrap_mode(Gtk.WrapMode.WORD )
            md_buffer = md_view.get_buffer()
            md_view.set_editable(False)
            md_scrl.add(md_view)
            md_buffer.set_text(md_text)
            markdown.convert(win, md_view)
            detailsbox = Gtk.HBox()
            detailsbox.pack_start(ui.icon(win, "text-plain"), False, False, False)
            detailsbox.pack_start(Gtk.Label("  Read Article  "), True, True, True)
            detailsbox.show_all()
            notebook.append_page(md_scrl, detailsbox)

            # Markdown source
            md_scrl = Gtk.ScrolledWindow()
            md_view = Gtk.TextView()
            #md_view.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.2,0.2,0.2, 1))
            #md_view.override_color(Gtk.StateType.NORMAL, Gdk.RGBA(0.9,0.9,0.9, 1))
            md_view.override_font(Pango.FontDescription("Monospace"))
            md_view.set_wrap_mode(Gtk.WrapMode.WORD )
            md_buffer = md_view.get_buffer()
            md_view.set_editable(False)
            md_scrl.add(md_view)
            md_buffer.set_text(md_text)
            detailsbox = Gtk.HBox()
            detailsbox.pack_start(ui.icon(win, "text-markdown"), False, False, False)
            detailsbox.pack_start(Gtk.Label("  Source of Article  "), True, True, True)
            detailsbox.show_all()
            notebook.append_page(md_scrl, detailsbox)


            
    except Exception as e:
        print("FUCKING ERROR")
        print(e)
        print()

    # Channel Uploads / Publications
    
    if is_channel:

        uploads_box = ui.load(win, claim_search.find, claim_search.render, win, "", [out["claim_id"]], )
        detailsbox = Gtk.HBox()
        detailsbox.pack_start(ui.icon(win, "folder-remote"), False, False, False)
        detailsbox.pack_start(Gtk.Label("  Publications  "), True, True, True)
        detailsbox.show_all()
        notebook.append_page(uploads_box, detailsbox)
    
    
    ##### DESCRIPTION ####
    
    try:
        description_scrl = Gtk.ScrolledWindow()
        description_scrl.set_size_request(500,200)
        description_field = Gtk.TextView()
        description_field.set_wrap_mode(Gtk.WrapMode.WORD )
        description_field.set_editable(False)
        description_buffer = description_field.get_buffer()
        description_buffer.set_text(out["value"]["description"])
        markdown.convert(win, description_field)

        description_box = Gtk.VBox()
        detailsbox = Gtk.HBox()
        detailsbox.pack_start(ui.icon(win, "text-plain"), False, False, False)
        detailsbox.pack_start(Gtk.Label("  Description  "), True, True, True)
        detailsbox.show_all()
        notebook.append_page(description_scrl, detailsbox)
        
        description_box.pack_start(description_scrl, False, False, False)
        description_scrl.add(description_field)
    except:
        pass

    ######## COMMENTS ########


    
    com_box = Gtk.VBox()
    com_scrl = Gtk.ScrolledWindow()
    comments_widget = ui.load(win, comments.list_comments, comments.render_comments, win, out["claim_id"], )
    com_scrl.add(comments_widget)
    detailsbox = Gtk.HBox()
    detailsbox.pack_start(ui.icon(win, "message-new"), False, False, False)
    detailsbox.pack_start(Gtk.Label("  Comments  "), True, True, True)
    detailsbox.show_all()
    com_box.pack_start(comments.comment_input(win), False, False, 0)
    com_box.pack_start(com_scrl, True, True, 0)
    notebook.append_page(com_box, detailsbox)

    
    ##### Details  #######

    # Almost like Raw Data but shows only the important stuff

    
    details = {"LBRY URL: ":url,
               "Price: ":str(price)+" "+str(currency)}
    try:
        details["Claim ID: "] = out["claim_id"]
    except:
        pass

    try:
        details["Upload Bid: "] = out["amount"]+" LBC"
    except:
        pass

    try:
        details["Support: "] = out["meta"]["support_amount"]+" LBC"
    except:
        pass
    
    
    try:
        details["Filename: "] = out["value"]["source"]["name"]
    except:
        pass
    try:
        details["File Size:"] = csize(filesize)
    except:
        pass
    try:
        details["License: "] = out["value"]["license"]
    except:
        pass

    try:
        details["Released at: "] = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(out["value"]["release_time"])))
    except:
        pass
    
    det_scrl = Gtk.ScrolledWindow()
    det_view = data_view.data_widget(details)
    det_scrl.add(det_view)
    detailsbox = Gtk.HBox()
    detailsbox.pack_start(ui.icon(win, "emblem-information"), False, False, False)
    detailsbox.pack_start(Gtk.Label("  Details  "), True, True, True)
    detailsbox.show_all()
    notebook.append_page(det_scrl, detailsbox)
    
    ##### Raw Data #######

    raw_scrl = Gtk.ScrolledWindow()
    raw_view = data_view.data_widget(out)
    raw_scrl.add(raw_view)
    detailsbox = Gtk.HBox()
    detailsbox.pack_start(ui.icon(win, "emblem-warning"), False, False, False)
    detailsbox.pack_start(Gtk.Label("  Extra Details  "), True, True, True)
    detailsbox.show_all()
    notebook.append_page(raw_scrl, detailsbox)


    
    
    #outbox.show_all()
    return outbox



def downloaded(claim_id):

    # Returns a fraction ( from 0 to 1 ) of the download
    # percentage. If it's a 0, we can use it to display
    # the download button.

    #out = check_output(["flbry/lbrynet",
    #                     "file", "list", "--claim_id="+claim_id])
    out = fetch.lbrynet("file_list", {"claim_id":claim_id})
    #print(out, '\n\n')
    
    try:
        #out = json.loads(out)
        out = out["items"][0]
        
        if out["status"] == "finished":
            return 1
        else:
            return out["written_bytes"] / out["total_bytes"]
        
    except:
        return 0

def get_downloaded_file(claim_id):

    #out = check_output(["flbry/lbrynet",
    #                     "file", "list", "--claim_id="+claim_id])
    out = fetch.lbrynet("file_list", {"claim_id":claim_id})

    try:
        #out = json.loads(out)
        out = out["items"][0]

        return out["download_path"]
    except:
        return ""

def delete_file(claim_id):
    #check_output(["flbry/lbrynet",
    #                     "file", "delete", "--claim_id="+claim_id])
    fetch.lbrynet("file_delete", {"claim_id":claim_id})
def start_downloading(url):
    out = fetch.lbrynet("get", {"uri":url, "save_file":True})
    
def downloading_check_thread(win, claim_id,
                             download_button,
                             delete_button,
                             launch_button,
                             progress_bar):

    # This is a thread that will toggle buttons on/off
    # based on a curretly downloading file.

    def update(fraction):
        if not fraction: # if it's 0
            download_button.set_visible(True)
            delete_button.set_visible(False)
            launch_button.set_visible(False)
            progress_bar.set_visible(False)
        else:
            download_button.set_visible(False)
            delete_button.set_visible(True)
            launch_button.set_visible(True)
            progress_bar.set_visible(True)
            progress_bar.set_fraction(fraction)
        if fraction == 1:
            progress_bar.set_visible(False)
    
    while True:
        
        fraction = downloaded(claim_id)
        GLib.idle_add(update, fraction)
        time.sleep(2) # The new algorithm is too fast LOL

        if not win.check_download_daemon_keep_alive:
            return
def csize(x):

    x = float(x)

    l = ["B","KB", "MB", "GB", "TB"]

    for i in range(5):
        if x > 1024:
            x = x / 1024
        else:
            return str(round(x, 2))+" "+l[i]
    return str(round(x, 2))+" "+l[i]
