#include <gtk/gtk.h>
#include <gtk/gtkx.h>
#include <locale.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <libintl.h>
#include <getopt.h>
#include <libintl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <libublsettings.h>
#include <libublsettings-gtk3.h>
#include <libublsettingsui-gtk3.h>
#ifdef WEBKIT_INCLUDE
    #include <webkit2/webkit2.h>
#endif 
#include "ubl-strings.h"

#define _(String) gettext(String)

#define glade_path "/com/ublinux/ui/ubl-settings-repomanager.glade"
#define glade_path_repo_add "/com/ublinux/ui/ubl-settings-repomanager-add.glade"
#define glade_path_storage_config "/com/ublinux/ui/ubl-settings-repomanager-configuration.glade"
#define glade_path_repo_block "/com/ublinux/ui/ubl-settings-repomanager-repo-block.glade"
#define glade_path_filechooser "/com/ublinux/ui/ubl-settings-repomanager-filechooser.glade"
#define glade_path_key "/com/ublinux/ui/ubl-settings-repomanager-key.glade"
#define glade_path_sign "/com/ublinux/ui/ubl-settings-repomanager-sign.glade"
#define banner_path "/com/ublinux/images/ubl-settings-repomanager-banner.png"
#define CssPath "/com/ublinux/css/ubl-settings-repomanager.css"
#define config_path yon_char_unite(yon_ubl_user_get_home_directory(),"/.config/",LocaleName,"/",LocaleName,".conf",NULL)
#define icon_path "com.ublinux.ubl-settings-repomanager"

#define folder_no_edits "com.ublinux.libublsettingsui-gtk3.folder-symbolic"
#define document_no_edits "com.ublinux.libublsettingsui-gtk3.key-symbolic"
#define document_deleted "com.ublinux.ubl-settings-repomanager.key-red-symbolic"
#define document_added "com.ublinux.ubl-settings-repomanager.key-green-symbolic"

#define add_icon_name "com.ublinux.libublsettingsui-gtk3.increase-symbolic"
#define move_icon "com.ublinux.libublsettingsui-gtk3.arrows-left-right-symbolic"
#define remove_icon "com.ublinux.libublsettingsui-gtk3.trash-symbolic"

#define LocalePath "/usr/share/locale" 
#define LocaleName "ubl-settings-repomanager"

#define lib_key_icon_name "com.ublinux.libublsettingsui-gtk3.key-symbolic"
#define key_icon_name "com.ublinux.ubl-settings-repomanager.document-sign-symbolic"
#define key_error_icon_name "com.ublinux.ubl-settings-repomanager.document-sign-error-symbolic"

#define package_signature_icon_name "com.ublinux.ubl-settings-repomanager.chain-symbolic"
#define package_signature_error_icon_name "com.ublinux.ubl-settings-repomanager.chain-error-symbolic"

#define warning_icon_name "com.ublinux.ubl-settings-repomanager.warning-symbolic"

#define package_linked_icon "com.ublinux.ubl-settings-repomanager.package-linked-symbolic"
#define package_unlinked_icon "com.ublinux.ubl-settings-repomanager.package-unlinked-symbolic"

#define key_encription_path "resource:///com/ublinux/csv/crypt-key.csv"
typedef char* string;

#define create_storage_command(path,name,sign) yon_char_unite("repo-manager add --only-db --force-replace -r '",name,"' -d '", path,"'",!yon_char_is_empty(sign)?" -S ":NULL,sign,NULL)
#define add_packages_command(path,arch,name,packages_path) yon_char_unite("repo-manager add -f -r '",name,"' -a '",arch,"' -d '", path,"' ",packages_path,NULL)
#define add_packages_sign_command(path,arch,name,repo_sign,pack_sign,packages_path) yon_char_unite("repo-manager add -f -r '",name,"' -a '",arch,"' -d '", path,"' -S ",repo_sign," -s ",pack_sign," ",packages_path,NULL)
#define add_packages_package_sign_command(path,arch,name,sign,packages_path) yon_char_unite("repo-manager add -f -r '",name,"' -a '",arch,"' -d '", path,"' -s '",sign,"' ",packages_path,NULL)
#define add_packages_repo_sign_command(path,arch,name,sign,packages_path) yon_char_unite("repo-manager add -f -r '",name,"' -a '",arch,"' -d '", path,"' -S '",sign,"' ",packages_path,NULL)
#define remove_packages_command(path,arch,name,packages_path,sign) yon_char_unite("repo-manager remove -r '",name,"' -a '",arch,"' -d '", path,"' ",sign?"-S ":"",sign?sign:"",sign?" ":"",packages_path,NULL)

#define repo_sign_command(storage_path, repo, sign) yon_char_unite("repo-manager add -f -r '",repo,"' -d '", storage_path, "' -S ", sign,NULL)

#define yon_move_packages_command(storage,arch, source_repo, target_repo, packages,sign) yon_char_unite("repo-manager move -d '",storage,"' -a '",arch,"' -R '",source_repo,"' -r '",target_repo,"' ",sign?"-S ":"",sign?sign:"",sign?" ":"", packages,NULL)
#define yon_delete_packages_command(storage,arch, target_repo, packages) yon_char_unite("repo-manager remove -d '",storage,"' -a '",arch,"' -r '",target_repo,"' ", packages,NULL)
#define yon_delete_packages_sign_command(storage,arch, target_repo,sign, packages) yon_char_unite("repo-manager remove -d '",storage,"' -a '",arch,"' -r '",target_repo,"' -S '",sign,"' ", packages,NULL)

#define ubl_settings_update_command "ubl-settings-update"

#define yon_get_packages_command(path,repo,arch) yon_char_unite("repo-manager show --quiet -d '",path,"' -a ",arch," -r '",repo,"' | sed -E 's/(\\S+)(\\s+)(\\S+)/\\1;\\3/g'",NULL)

#define yon_repo_get_signature(path, repo) yon_char_unite("repo-manager verify --quiet -d ",path," -r '",repo,"' . 2>&1 | cut -d: -f2 |sed -E 's/   *//g'",NULL)
#define yon_repo_get_signature_status(path,repo,arch, package) yon_char_unite("repo-manager verify --quiet -d ",path," -r '",repo,"' ",package,NULL)
#define yon_repo_get_signature_status_arch(path,repo,arch) yon_char_unite("repo-manager verify --quiet -d ",path," -r '",repo,"' .",NULL)
#define yon_repo_get_signature_status_packages(path,repo,arch) yon_char_unite("repo-manager verify --quiet -d ",path," -r '",repo,"'",NULL)

#define yon_get_package_name_from_filename(path) yon_char_unite("[[ '",path,"' =~ (.*/)*(.*)-[^-]+-[^-]+-[^-]+\\.pkg\\.tar\\.[^.-]+ ]] && echo ${BASH_REMATCH[2]}",NULL)

#define yon_get_filename_from_package_name(target) yon_char_unite("find ${PATH} -maxdepth 1 -regextype egrep -regex \".*/",target,"-[^-]+-[^-]+-[^-]+\\.pkg\\.tar\\.[^-.]*\"",NULL)

#define get_gpg_keys_command "gpg --list-keys | awk ' \
/^pub/ { \
    key=\"\"; \
    getline; \
    if (match($0, /[0-9A-F]{8,}/)) \
        key=substr($0, RSTART, RLENGTH); \
} \
/^uid/ { \
    $1=\"\"; sub(/^ +/, \"\"); \
    if (match($0, /<[^>]+>/)) { \
        email = substr($0, RSTART+1, RLENGTH-2); \
        print $0 \";\" email; \
    } else { \
        print $0 \";\" key; \
    } \
}'"


#define yon_sign_package(storage,repo,arch,target,sign) yon_char_unite("repo-manager add -r ''",repo,"'' -a '",arch,"' -d '", storage,"' -s '",sign,"' ",target,NULL)
// #define yon_sign_repo(storage,repo,sign) yon_char_unite("repo-manager add --only-db --force-replace  -f -r ''",repo,"'' -d '", storage,"' --sign-repo='",sign,"'",NULL)

#define get_package_info_command(filename) yon_char_unite("pacman -Q --info --file '",filename,"' 2>/dev/null|sed -e 's/  */ /g' -e 's/ : /: /g' -e 's/\\n/ /g' -e 's/^ /\\t/g'",NULL)

#define get_storage_info_lab el(path, repos, archs) yon_char_unite("<b>",STORAGE_PATH_LABEL,":</b> ", path,"\n\n<b>", REPOS_LABEL,":</b> ",repos,"\n\n<b>",ARCHS_LABEL,":</b> ",archs,NULL)

#define get_repo_info_label(repos, archs,sign) yon_char_unite("<b>",REPO_NAME_LABEL,":</b> ",repos,"\n\n<b>",ARCHS_LABEL,":</b> ",archs,"\n\n",sign,NULL)

#define get_arch_info_label(repos, archs) yon_char_unite("<b>",REPO_NAME_LABEL,":</b> ",repos,"\n\n<b>",ARCHS_LABEL,":</b> ",archs,NULL)

#define yon_generate_key_command(type,name,email,strength,comment,expire,password) yon_char_unite("gpg --batch --gen-key <<EOF\n\
%echo Generating a basic OpenPGP key\n\
Key-Type: ",type,"\n\
Key-Length: 1024\n\
Subkey-Type: ELG-E\n\
Subkey-Length: ",strength,"\n\
Name-Real: '",name,"'\n",\
!yon_char_is_empty(comment)?"Name-Comment: ":"",!yon_char_is_empty(comment)?comment:"",!yon_char_is_empty(comment)?"\n":"","\
Name-Email: '",email,"'\n\
Expire-Date: ",!yon_char_is_empty(expire)?expire:"0","\n\
Passphrase: '",password,"'\n\
%commit\n\
%echo done\n\
EOF\n\n",NULL)

#define yon_generate_key_no_password_command(type,name,email,strength,comment,expire,password) yon_char_unite("gpg --batch --gen-key <<EOF\n\
%echo Generating a basic OpenPGP key\n\
Key-Type: ",type,"\n\
Key-Length: 1024\n\
Subkey-Type: ELG-E\n\
Subkey-Length: ",strength,"\n\
Name-Real: ",name,"\n",\
!yon_char_is_empty(comment)?"Name-Comment: ":"",!yon_char_is_empty(comment)?comment:"",!yon_char_is_empty(comment)?"\n":"","\
Name-Email: '",email,"'\n\
Expire-Date: ",!yon_char_is_empty(expire)?expire:"0","\n\
%no-protection\n\
%commit\n\
%echo done\n\
EOF\n\n",NULL)

#define STORAGE_search_macro "STORAGE["
#define STORAGE(target) yon_char_unite("STORAGE[",target,"]",NULL)
#define STORAGE_command "echo \"\""


typedef char* string; 

__attribute__((unused)) static \
string version_application;

typedef struct {
   template_config_fields
    GtkTreeStore *tree_store;
    int lock_root;
    config_str signs;
    int signs_size;
    char *last_selection;
    GHashTable *storages;
} config;

extern config main_config;

typedef struct {
    template_window_fields
    GtkWidget *UpdateButton;
    GtkWidget *DBConfigurationButton;
    GtkWidget *AddButton;
    GtkWidget *MoveButton;
    GtkWidget *RemoveButton;
    GtkWidget *CloudButton;
    GtkWidget *ReposTree;
    GtkWidget *RepoFileTree;
    GtkWidget *SearchEntry;
    GtkWidget *RepoPackagesTree;
    GtkWidget *InformationLabel;
    GtkWidget *KeyMenuItem;
    GtkTreeStore *RepoList;
    GtkListStore *RepoFileList;
    GtkListStore *RepoPackagesList;
} main_window;

typedef struct {
    GtkWidget *MainWindow;
    GtkWidget *StatusBox;
    GtkWidget *HeadLabel;
    GtkWidget *PackagesToAddLabel;
    GtkWidget *CancelButton;
    GtkWidget *AddButton;
    GtkWidget *MoveSourceFrame;
    GtkWidget *MoveSourceStorageEntry;
    GtkWidget *MoveSourceRepoEntry;
    GtkWidget *MoveSourceArchEntry;
    GtkWidget *MoveTargetFrame;
    GtkWidget *MoveTargetStorageEntry;
    GtkWidget *MoveTargetRepoEntry;
    GtkWidget *MoveTargetArchEntry;
    GtkWidget *StorageCombo;
    GtkWidget *RepositoryCombo;
    GtkWidget *ArchitectureCombo;
    GtkWidget *PackageSignCombo;
    GtkWidget *RepoSignCombo;
    GtkWidget *AddPackageButton;
    GtkWidget *RemoveButton;
    GtkWidget *PackagesTree;
    GtkWidget *InfoLabel;
    GtkWidget *TestTree;
    GtkListStore *PackagesList;
    GtkTreeStore *tree_copy;
    GtkTreeModel *storage_filter;
    GtkTreeModel *repo_filter;
    GtkTreeModel *arch_filter;
    GtkTreeModel *package_filter;

    char *selected_path;

    dictionary *added_packages_archs;
    dictionary *removed_packages_archs;
} repo_add_window;

typedef struct {
    GtkWidget *MainWindow;
    GtkWidget *HeadLabel;
    GtkWidget *CancelButton;
    GtkWidget *AcceptButton;
    GtkWidget *StatusBox;
    GtkWidget *StoragePathCombo;
    GtkWidget *OpenExistingButton;
    GtkWidget *CreateNewButton;
    GtkWidget *DisableButton;
    GtkWidget *DeleteButton;
    GtkWidget *ReposBox;
    GtkWidget *AddEntry;
    GtkWidget *AddRepoButton;
    GtkWidget *TestTree;
    GHashTable *storages_copy;
    int delete_storages_size;
    config_str delete_storages;
} storage_config_window;

typedef struct {
    GtkWidget *Window;
    GtkWidget *StatusBox;
    GtkWidget *HeadLabel;
    GtkWidget *NameEntry;
    GtkWidget *EmailEntry;
    GtkWidget *CommentsEntry;
    GtkWidget *EncryptionCombo;
    GtkWidget *KeyStrengthSpin;
    GtkWidget *ExpireCombo;
    GtkWidget *ExpireEntry;
    GtkWidget *ExpireButton;
    GtkWidget *CancelButton;
    GtkWidget *AddButton;
    GtkWidget *PasswordEntry;
    GtkWidget *PasswordButton;
    unsigned long expire_time;
} key_creation_window;

typedef struct {
    GtkWidget *Window;
    GtkWidget *StatusBox;
    GtkWidget *HeadLabel;
    GtkWidget *CancelButton;
    GtkWidget *SignButton;
    GtkWidget *SignCombo;
} sign_window;

typedef struct {
    GtkWidget *MainBox;
    GtkWidget *RepoNameEntry;
    GtkWidget *SignatureCombo;
    GtkWidget *RepoRemoveButton;
    
    char *storage_path;
    char *repo_name;
    char *sign_request;

} storage_repo_block;

typedef struct {
    char *name;
    char *path;
    GHashTable *repos;
    int sign_status;
} storage_struct;

typedef struct {
    char *name;
    char *storage_path;
    GHashTable *archs;
    char *path;
    char *sign_status;
} repo_struct;

typedef struct {
    char *name;
    char *storage_path;
    GHashTable *packages;
    char *repo_name;
    char *path;
} arch_struct;

struct repo_tree_build {
    main_window *widgets;
    GtkTreeIter storageiter;
    GtkTreeIter repoiter;
    GtkTreeIter architer;
};

enum YON_REPO_DATA_TYPE {
    YON_REPO_DATA_STORAGE,
    YON_REPO_DATA_REPO,
    YON_REPO_DATA_ARCH
};

main_window *setup_window();

void yon_storage_config_update(storage_config_window *window);

void yon_storage_config_update(storage_config_window *window);

void yon_sign_list_update();

void on_key_clicked(GtkWidget *self, main_window *widgets);

storage_repo_block *yon_storage_repo_block_new();

void yon_combo_box_text_set_signs(GtkComboBoxText *target);

void on_storage_changed(GtkWidget *self, storage_config_window *window);

config_str yon_tree_store_get_all_at_level(GtkTreeStore *target,GtkTreeIter *parent, int *size, int column, int level);

void on_remove_arch_selection_changed(GtkWidget *self, repo_add_window *window);

void on_repo_add(GtkWidget *self, storage_config_window *window);

void on_main_tree_selection_changed(GtkWidget *self, main_window *widgets);

void yon_config_load_update(main_window *widgets);
void yon_add_window_set_selected(main_window *widgets, repo_add_window *window);
repo_add_window *yon_repo_add_window_new();
void on_repo_sign_load(GtkWidget *self, repo_add_window *window);
void on_add_packages_accept(GtkWidget *self, dictionary *dict);
void on_add_window_tree_changed(GtkWidget *self, repo_add_window *window);
void on_remove_package(GtkWidget *self, repo_add_window *window);
void on_add_package(GtkWidget *self, repo_add_window *window);
void on_add_accept(GtkWidget *self, dictionary *dict);
void on_move_arch_changed(GtkWidget *self, repo_add_window *window);
void on_move_repo_changed(GtkWidget *self, repo_add_window *window);
void on_move_storage_changed(GtkWidget *self, repo_add_window *window);
void on_add_target_package_selection_changed(GtkWidget *target,repo_add_window *window);
void on_arch_chosen(GtkWidget *target,repo_add_window *window);
void on_add_target_arch_selection_changed(GtkWidget *target,repo_add_window *window);
void on_add_target_repo_selection_changed(GtkWidget *target,repo_add_window *window);
config_str yon_tree_store_get_all_at_level(GtkTreeStore *target,GtkTreeIter *parent, int *size, int column, int level);
gboolean yon_storage_filter_func(GtkTreeModel *model,GtkTreeIter *iter,gpointer data);
void on_file_chooser_file_activate(GtkWidget *self, dictionary *dict);
gboolean yon_storage_filter_func(GtkTreeModel *model,GtkTreeIter  *iter,gpointer data);
void on_storage_configure_clicked(GtkWidget *self, main_window *widgets);
storage_config_window *yon_storage_config_window_new();
void on_storage_changed(GtkWidget *self, storage_config_window *window);
void on_storage_accept(GtkWidget *self, dictionary *dict);
void on_repo_add(GtkWidget *self, storage_config_window *window);
void on_storage_remove(GtkWidget *self, storage_config_window *window);
void on_storage_disable(GtkWidget *self, storage_config_window *window);
void on_storage_create(GtkWidget *self, storage_config_window *window);
void on_storage_open(GtkWidget *self, storage_config_window *window);
void on_repo_signed();
void on_repo_removed(GtkWidget *self,storage_repo_block *block);
void on_repo_sign_changed(GtkWidget *self, storage_repo_block *block);
void on_add_clicked(GtkWidget *self, main_window *widgets);
void on_rmb_move(GtkWidget *self, main_window *widgets);
repo_add_window *on_move_clicked(GtkWidget *self, main_window *widgets);
repo_add_window *on_remove_clicked(GtkWidget *self, main_window *widgets);
void on_folder_changed(GtkFileChooser *self, dictionary *dict);
void on_move_remove_package(GtkWidget *self, repo_add_window *window);
void on_move_add_accept(GtkWidget *self, dictionary *dict);
void on_ubl_settings_update_launch();
void on_package_selection_changed(GtkWidget *self, main_window *widgets);
void on_tree_view_activate(GtkWidget *self, GtkTreePath* path, GtkTreeViewColumn* column, main_window *widgets);
void on_repo_item_activate(GtkWidget *self, GtkTreePath *path, GtkTreeViewColumn *column, main_window *widgets);
void on_update_clicked(GtkWidget *self, main_window *widgets);
storage_struct *yon_storage_struct_new();
repo_struct *yon_repo_struct_new();
arch_struct *yon_arch_struct_new();
char *yon_path_get_name(char *path);
void yon_storage_fill_repos(storage_struct *storage, config_str repos, int repos_size);
void yon_repo_fill_archs(repo_struct *repo);
void yon_arch_fill_packages(arch_struct *arch);
void yon_repo_tree_build_storage(char *key, storage_struct *storage, main_window *widgets);
void yon_repo_tree_build_repo(char *key, repo_struct *repo, struct repo_tree_build *data);
void yon_repo_tree_build_arch(char *key, arch_struct *arch, struct repo_tree_build *data);
void yon_interface_update(main_window *widgets);
void yon_sublist_packages_build(char *key, char *file, main_window *widgets);
// void yon_sublist_build(char *key, storage_struct *storage, main_window *widgets);
void yon_storage_repo_block_destroy(storage_repo_block *block);
void yon_storage_hash_table_clone(GHashTable *source_hash, GHashTable *target_hash);
void yon_storage_sturct_free_full(storage_struct *storage);
void yon_storage_edit_accept(storage_struct *target_struct, storage_struct *source_struct);
void yon_storage_struct_remove(storage_struct *storage,repo_struct *repo);
void yon_storage_struct_add_repo(storage_struct *storage,repo_struct *repo);
void yon_repo_struct_sign(storage_struct *storage, repo_struct *repo,const char *sign);
void yon_storage_remove(char *key, storage_struct *storage,void*);
config_str yon_arch_struct_get_packages(arch_struct *arch, int *size);
void yon_move_window_set_selected(main_window *widgets, repo_add_window *window);
void on_move_add_package(GtkWidget *self, repo_add_window *window);
void on_packages_sign(GtkWidget *self, main_window *widgets);
void yon_add_window_add_chosen_packages(main_window *widgets, repo_add_window *window);
void yon_storage_open_add_repos(char *storage_path, storage_config_window *window);
void yon_package_info_set_markup(config_str parsed, int size);
void on_remove_window_tree_changed(GtkWidget *self, repo_add_window *window);
void on_move_window_tree_changed(GtkWidget *self, repo_add_window *window);
void on_move_check_fill(GtkWidget *self, repo_add_window *window);
void yon_repo_creation_set_sensitive_from_combo_box(GtkComboBox *toggle, GtkWidget *target);
int yon_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data);
gboolean yon_pwquality_check(const char *password);
char *yon_pwquality_error_check(const char *password);