#####################################################################
#                                                                   #
#  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.  #
#                                                                   #
#####################################################################

# This file will handle all kinds of settings

import os
import json
from gi.repository import Gtk

from flbry import ui
from flbry import fetch
from flbry import odysee
from flbry import comments

def get_settings_folder(flbry="flbry/"):

    try:
        data_dir = os.environ["XDG_DATA_HOME"] + "/" + flbry # Reducted back since it
                                                             # broke too much and made
                                                             # my settings appear in
                                                             # $XDG_DATA_HOME folder
                                                             # inside the repository.
                                                             # WTF !!!
    except:
        data_dir = os.path.expanduser("~/.local/share/"+flbry)

    try:
        os.makedirs(data_dir)
    except:
        pass

    return data_dir

def load():
    with open(get_settings_folder()+'config.json') as json_file: 
            data = json.load(json_file) 
    return data
            
def save(data):
    with open(get_settings_folder()+'config.json', 'w') as fp:
            json.dump(data, fp, sort_keys=True, indent=4)

def make_sure_file_exists():

    # Let's make some defaults

    defaults = {
        "GTK_icon_theme":"System Theme",
        "notifications":True,
        "auth_token": "",
        "autoconnect": False,
        "comment_api": "https://comments.odysee.com/api/v2",
        "lbrynet_binary": "flbry/lbrynet",
        "librarian_instance": "https://librarian.bcow.xyz/",
        "promote_fast_lbry_in_comments":False,
        "promote_fast_lbry_text": comments.BUTTON_GTK_PROMOTE_TEXT,
        "filter_tags":["mature", "sex", "porn"],
        "lock_password":"",
        "live_stream_player":"vlc",
        "default_tab":"following",
        "comments_auto_resize":True

    }

    # List of old and unneded or changed setting names. For example if
    # there was a spelling error in the key. This list exists for
    # backward compatibility, so users could update to the new version
    # without seeing two separate settings all of a sudden.
    
    old_and_bad = [
        "promote_fast_lbry_in_commnets"   # issue #26 | Bad spelling
    ]
    
    # Let's write the Default theme.
    try:
        with open(get_settings_folder()+"config.json") as f:
            data = json.load(f)

        # Adding missing    
        for i in defaults:
            if i not in data:
                data[i] = defaults[i]

        # Removing bad
        for i in old_and_bad:
            if i in data:
                del data[i]
                
    except Exception as e:
        data = defaults
        
    with open(get_settings_folder()+'config.json', 'w') as fp:
        json.dump(data, fp, sort_keys=True, indent=4)
    
def dialogue(w, win):

    # This is the settings dialogue

    # Configuring the window
    
    dialogWindow = Gtk.Dialog("FastLBRY - Settings",
            buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                     Gtk.STOCK_OK, Gtk.ResponseType.OK),
        )

    dialogWindow.set_size_request(500,500)
    dialogWindow.set_default_icon_from_file("icon.png")
    mainbox = dialogWindow.get_content_area()

    # We will break the settings into tabs of various importances.

    notebook = Gtk.Notebook()
    notebook.set_scrollable(True)
    mainbox.pack_start(notebook, 1,1,0)
    

    #######################################################################
    #                                                                     #
    #                            ACCOUNT / WALLET                         #
    #                                                                     #
    #######################################################################

    scrl = Gtk.ScrolledWindow()
    box = Gtk.VBox()
    scrl.add(box)
    
    notebook.append_page(scrl, Gtk.Label("Account / Wallet"))

    # We need to get a list of all of the wallets on this system

    try:
        wallets = fetch.lbrynet("wallet_list")["items"]
    except:
        wallets = []

    for n, wallet in enumerate(wallets):

        wallet_frame = Gtk.Expander(label=wallet.get("name", ""))
        if n == 0:
            wallet_frame.set_expanded(True)
        box.pack_start(wallet_frame, 0,0,5)

        wbox = Gtk.VBox()
        wallet_frame.add(wbox)

        # Odysee sync button

        def sync_do(w):
            response = odysee.sync_ui(win)
            if "auth_token" in response:
                auth_e.set_text(response["auth_token"])
                win.settings["auth_token"] = response["auth_token"]
                save(win.settings)
        
        odysee_sync = Gtk.Button()
        odysee_sync.connect("clicked", sync_do)
        odysee_sync.set_relief(Gtk.ReliefStyle.NONE)
        obox = Gtk.HBox()
        odysee_sync.add(obox)
        obox.pack_start(Gtk.Image.new_from_file("icons/odysee.png"),0,0,2)
        obox.pack_start(Gtk.Label("Sync with Odysee"), 1,1,2)
        wbox.pack_start(odysee_sync, 0,0,2)

    box.pack_start(Gtk.HSeparator(), 0,0,10)

    # Auth Token

    auth_box = Gtk.HBox()
    box.pack_start(auth_box, 0,0,5)

    auth_box.pack_start(Gtk.Label("Authentication Token : "),0,0,5)
    auth_w, auth_e = ui.password_entry(win)
    auth_box.pack_start(auth_w, 1,1,5)
    auth_e.set_text(win.settings["auth_token"])

    # Lock Password

    lock_box = Gtk.HBox()
    box.pack_start(lock_box, 0,0,5)

    lock_box.pack_start(Gtk.Label("Wallet Lock Password : "),0,0,5)
    lock_w, lock_e = ui.password_entry(win)
    lock_box.pack_start(lock_w, 1,1,5)
    lock_e.set_text(win.settings["lock_password"])

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

    scrl = Gtk.ScrolledWindow()
    box = Gtk.VBox()
    scrl.add(box)
    
    notebook.append_page(scrl, Gtk.Label("Comments"))

    # Here will be the settings for comments.

    promote_box = Gtk.HBox()
    box.pack_start(promote_box, 0,0,5)

    promote_box.pack_start(Gtk.Label("Promote FastLBRY in Comments: "), 0,0,5)
    def hide_promotion_options(w, u):
        promote_options_box.set_visible(w.get_active())
    promote_fastlbry_switch = Gtk.Switch()
    promote_fastlbry_switch.connect("notify::active", hide_promotion_options)
    promote_fastlbry_switch.set_active(win.settings["promote_fast_lbry_in_comments"])
    promote_box.pack_end(promote_fastlbry_switch, 0,0,0)

    # Options of the promotion will come next. They are in a separate box, so I could
    # hide it when promotion is not activated.
    
    promote_options_box = Gtk.VBox()
    box.pack_start(promote_options_box, 0,0,5)


    type_of_promotion = Gtk.ComboBoxText()
    type_of_promotion.append_text( "Image Link" )
    type_of_promotion.append_text( "Text Link" )
    type_of_promotion.append_text( "Custom" )

    ptype_box = Gtk.HBox()
    promote_options_box.pack_start(ptype_box, 0,0,5)
    ptype_box.pack_start(Gtk.Label("Promotion Type: "), 0,0,5)
    ptype_box.pack_end(type_of_promotion, 0,0,5)

    def hide_custom_promotion(w):
        if type_of_promotion.get_active_text() == "Custom":
            pcustom_box.set_visible(True)
        else:
            pcustom_box.set_visible(False)
    type_of_promotion.connect("changed", hide_custom_promotion)
    
    pcustom_box = Gtk.HBox()    
    promote_options_box.pack_start(pcustom_box, 0,0,5)
    pcustom_box.pack_start(Gtk.Label("Custom Promotion: "), 0,0,5)

    pcustom_entry = Gtk.Entry()
    pcustom_entry.set_text(win.settings["promote_fast_lbry_text"])
    pcustom_box.pack_end(pcustom_entry, 0,0,5)

    box.pack_start(Gtk.HSeparator(), 0,0,5)

    # Scaling of comments

    cscalebox = Gtk.HBox()
    cscalebox.pack_start(Gtk.Label(" Force-fit comments to window: "), 0,0,5)
    commentScaleSwitch = Gtk.Switch()
    commentScaleSwitch.set_active(win.settings["comments_auto_resize"])
    cscalebox.pack_end(commentScaleSwitch, 0,0,5)

    box.pack_start(cscalebox, 0,0,5)


    box.pack_start(Gtk.HSeparator(), 0,0,5)
    
    
    # Advanced commment settings

    advanced_frame = Gtk.Expander(label="Advanced Settings")
    box.pack_start(advanced_frame, 0,0,5)

    adbox = Gtk.VBox()
    advanced_frame.add(adbox)

    # Comment API setting. For talking on different comment servers.
    commentAPIbox = Gtk.HBox()
    adbox.pack_start(commentAPIbox, 0,0,5)

    commentAPIbox.pack_start(Gtk.Label(" Comment server: ") , 0,0,5)
    commentserverentry = Gtk.Entry()
    commentserverentry.set_text(win.settings["comment_api"])
    commentAPIbox.pack_start(commentserverentry , 1,1,5)
    
    
    #######################################################################
    #                                                                     #
    #                              ALL THE REST                           #
    #                                                                     #
    #######################################################################
    

    
    scrl = Gtk.ScrolledWindow()
    box = Gtk.VBox()
    scrl.add(box)
    
    notebook.append_page(scrl, Gtk.Label("Other Settings"))
    




    
    #######################################################################
    #                                                                     #
    #                            THEMES SELECTOR                          #
    #                                                                     #
    #######################################################################

    
    themes_setting = Gtk.ComboBoxText()
    #themes_setting.set_relief(Gtk.ReliefStyle.NONE)

    # Themes are laoded from folder in ./icons

    select = 0

    icon_themes = os.listdir(os.getcwd()+"/icons")
    icon_themes.append("System Theme")
    for n, theme in enumerate(icon_themes):
        themes_setting.append_text( theme )
        if win.settings["GTK_icon_theme"] == theme:
            select = n

    
            
    themes_setting.set_active(select)

    theme_box = Gtk.HBox()
    theme_box.pack_start(Gtk.Label("  Icon Theme (requires restart) :  "), False, False, False)
    theme_box.pack_end(themes_setting, False, False, False)
    box.pack_start(theme_box, False, False, 5)

    # The default tab. Whether it's following or suggestions
    
    default_tab = Gtk.ComboBoxText()
    default_tab.append_text("Following")
    default_tab.append_text("Suggestions")
    if win.settings["default_tab"] == "suggest":
        default_tab.set_active(1)
    else:
        default_tab.set_active(0)
    
    default_box = Gtk.HBox()
    default_box.pack_start(Gtk.Label("  Default Tab :  "), False, False, False)
    default_box.pack_end(default_tab, False, False, False)
    box.pack_start(default_box, False, False, 5)
    

    #######################################################################
    #                                                                     #
    #                            ALL THE REST                             #
    #                                                                     #
    #######################################################################

    # The settings are SHARED WITH FASTLBRY TERMINAL
    # The rest of settings are generated by what's available in the config
    # file. So there could be problems. First we need exclude list.

    exclude = ["GTK_icon_theme", # We have a separate thing for it
               "save_history",   # Used by FastLBRY terminal, not interesting here
               "theme",          # The theme of FastLBRY terminal
               "default_tip",
               "default_opener",
               "dev_mode",
               "default_editor",
               "music_player",
               "player",
               "markdown_reader",# In FastLBRY GTK we have a UI implementation for this
               "graph_force_ASCII", # Another FastLBRY Termianl only function
               "ignore_width_forcing", # FASTLBRY TERMINAL AGAIN
               "channel",        # This one is set in the top pannel of the window
               "default_tab",    # Implemented separately
               "auth_token",     #  [ Account / Wallet ] Tab
               "lock_password",  #  [ Account / Wallet ] Tab
               "promote_fast_lbry_in_comments", # [ Comments ] Tab
               "promote_fast_lbry_text",        # [ Comments ] Tab
               "comment_api",                   # [ Comments ] Tab
               "comments_auto_resize"           # [ Comments ] Tab
               
    ]

    parts = {}
    
    for name in win.settings:
        if name in exclude:
            continue

        parts[name] = {}
        parts[name]["box"] = Gtk.HBox()

        # I want to beutify the name for the setting
        pname = name.replace("_", " ")
        pname = "  "+pname[0].upper()+pname[1:].lower()+"  "
        parts[name]["box"].pack_start(Gtk.Label(pname), False, False, False)

        # Then depending on the type of the data itself, we are going to give
        # inputs

        if type(win.settings[name]) == str:
            parts[name]["entry"]  = Gtk.Entry()
            parts[name]["entry"].set_text(win.settings[name])
            parts[name]["entry"].set_size_request(300,40)
            parts[name]["box"].pack_end(parts[name]["entry"], False, False, False)
            
        elif type(win.settings[name]) == bool:
            parts[name]["entry"]  = Gtk.Switch()
            parts[name]["entry"].set_active(win.settings[name])
            parts[name]["box"].pack_end(parts[name]["entry"], False, False, False)

        elif type(win.settings[name]) == float:
            parts[name]["number"] = Gtk.Adjustment(win.settings[name],
                                                   lower=0.0001,
                                                   upper=1000000000,
                                                   step_increment=0.1) 
            parts[name]["entry"]  = Gtk.SpinButton(adjustment=parts[name]["number"],
                                                   digits=4)
            parts[name]["box"].pack_end(parts[name]["entry"], False, False, False)
        elif type(win.settings[name]) == int:
            parts[name]["number"] = Gtk.Adjustment(win.settings[name],
                                                   lower=0,
                                                   upper=1000000000,
                                                   step_increment=4) 
            parts[name]["entry"]  = Gtk.SpinButton(adjustment=parts[name]["number"])
            parts[name]["box"].pack_end(parts[name]["entry"], False, False, False)
        elif type(win.settings[name]) == list:
            parts[name]["box"] = Gtk.VBox()
            parts[name]["box"].pack_start(Gtk.Label(pname), False, False, False)
            parts[name]["box"].pack_start( ui.tags_editor(win, win.settings[name]), True, True, 0)
            
        box.pack_start(parts[name]["box"], False, False, 5)
    
    # notifications_box = Gtk.HBox()
    # notifications_setting = Gtk.Switch()
    # notifications_setting.set_active(win.settings["notifications"])
    # notifications_box.pack_start(Gtk.Label("  Notifications:  "), False, False, False)
    # notifications_box.pack_end(notifications_setting, False, False, False)
    # box.pack_start(notifications_box, False, False, False)
    



    #######################################################################
    #                                                                     #
    #                      RUNING THE SETTINGS DIALOG                     #
    #                                                                     #
    #######################################################################
    
    # Running the dialog
    mainbox.show_all()

    # Hide element
    if not win.settings["promote_fast_lbry_in_comments"]:
        promote_options_box.set_visible(False)

    if win.settings["promote_fast_lbry_text"] == comments.BUTTON_GTK_PROMOTE:
        type_of_promotion.set_active(0)
    elif win.settings["promote_fast_lbry_text"] == comments.BUTTON_GTK_PROMOTE_TEXT:
        type_of_promotion.set_active(1)
    else:
        type_of_promotion.set_active(2)
        
    response = dialogWindow.run()

    if response == Gtk.ResponseType.OK:

        # Here I want to overwrite all the setting to ones
        # chosen by the user.

        win.settings["GTK_icon_theme"] = themes_setting.get_active_text()
        win.settings["auth_token"] = auth_e.get_text()
        win.settings["lock_password"] = lock_e.get_text()
        win.settings["comment_api"] = commentserverentry.get_text()
        win.settings["comments_auto_resize"] = commentScaleSwitch.get_active()

        win.settings["promote_fast_lbry_in_comments"] = promote_fastlbry_switch.get_active()
        if type_of_promotion.get_active_text() == "Image Link":
            win.settings["promote_fast_lbry_text"] = comments.BUTTON_GTK_PROMOTE
        elif type_of_promotion.get_active_text() == "Text Link":
            win.settings["promote_fast_lbry_text"] = comments.BUTTON_GTK_PROMOTE_TEXT
        else:
            win.settings["promote_fast_lbry_text"] = pcustom_entry.get_text()
        
        if default_tab.get_active_text() == "Suggestions":
            win.settings["default_tab"] = "suggest"
        else:
            win.settings["default_tab"] = "following"
        
        for name in win.settings:
            if name in exclude:
                continue

            value = win.settings[name]
            
            if type(win.settings[name]) == str:
                value = parts[name]["entry"].get_text()
            elif type(win.settings[name]) == bool:
                value = parts[name]["entry"].get_active()
            elif type(win.settings[name]) == int:
                value = int(parts[name]["entry"].get_value())
            elif type(win.settings[name]) == float:
                value = parts[name]["entry"].get_value()

            win.settings[name] = value
            
        # Then we save everything.
        save(win.settings)

        if not win.settings["lock_password"]:
            win.lock_button.set_sensitive(False)
        else:
            win.lock_button.set_sensitive(True)
            
    dialogWindow.destroy()
