/******************************* LICENCE **************************************
* Any code in this file may be redistributed or modified under the terms of
* the GNU General Public Licence as published by the Free Software 
* Foundation; version 2 of the licence.
****************************** END LICENCE ***********************************/

/******************************************************************************
* Author:
* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php
*
* Contributors:
* 
******************************************************************************/

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <libintl.h>
#include <errno.h>

#include "isomaster.h"

#define MAX_RANDOM_BASE_NAME_LEN 26
#define RANDOM_STR_NAME_LEN 6

/* files that I created in the temp dir for editing */
TempFileCreated* GBLtempFilesList = NULL;

extern bool GBLisoPaneActive;
extern GtkWidget* GBLisoTreeView;
extern char* GBLisoCurrentDir;
extern VolInfo GBLvolInfo;
extern GtkWidget* GBLmainWindow;
extern bool GBLisoChangesProbable;
extern GtkWidget* GBLeditorFld;
extern GtkWidget* GBLviewerFld;
extern GtkWidget* GBLtempDirFld;

/******************************************************************************
* addToTempFilesList()
* Appends a node to the front of the list.
* */
void addToTempFilesList(const char* pathAndName)
{
    TempFileCreated* newNode;
    
    newNode = malloc(sizeof(TempFileCreated));
    if(newNode == NULL)
        fatalError("newNode = malloc(sizeof(TempFileCreated)) failed\n");
    
    newNode->pathAndName = malloc(strlen(pathAndName) + 1);
    if(newNode == NULL)
        fatalError("newNode.pathAndName = malloc(strlen(pathAndName) + 1) failed\n");
    
    strcpy(newNode->pathAndName, pathAndName);
    
    newNode->next = GBLtempFilesList;
    
    GBLtempFilesList = newNode;
}

void deleteTempFiles(void)
{
    TempFileCreated* next;
    
    while(GBLtempFilesList != NULL)
    {
        next = GBLtempFilesList->next;
        
        unlink(GBLtempFilesList->pathAndName);
        
        free(GBLtempFilesList->pathAndName);
        free(GBLtempFilesList);
        
        GBLtempFilesList = next;
    }
}

void editSelectedBtnCbk(GtkMenuItem *menuitem, gpointer data)
{
    GtkTreeSelection* selection;
    
    /* do nothing if no image open */
    if(!GBLisoPaneActive)
        return;
    
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(GBLisoTreeView));
    
    /* there's just one row selected but this is the easiest way to do it */
    gtk_tree_selection_selected_foreach(selection, editSelectedRowCbk, NULL);
    
    /* can't put this in the callback because gtk complains */
    refreshIsoView();
}

void editSelectedRowCbk(GtkTreeModel* model, GtkTreePath* path,
                        GtkTreeIter* iterator, gpointer data)
{
    int fileType;
    char* itemName;
    char* randomizedItemName;
    char* pathAndNameOnFs; /* to extract to and add from */
    char* pathAndNameOnIso; /* to delete from iso */
    int rc;
    GtkWidget* warningDialog;
    
    gtk_tree_model_get(model, iterator, COLUMN_HIDDEN_TYPE, &fileType,
                                        COLUMN_FILENAME, &itemName, -1);
    
    if(fileType != FILE_TYPE_REGULAR)
    {
        warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("'%s' is not a regular file"),
                                               itemName);
        gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
        gtk_dialog_run(GTK_DIALOG(warningDialog));
        gtk_widget_destroy(warningDialog);
        
        g_free(itemName);
        return;
    }
    
    /* create full path and name for the file on the iso */
    pathAndNameOnIso = malloc(strlen(GBLisoCurrentDir) + strlen(itemName) + 1);
    if(pathAndNameOnIso == NULL)
        fatalError("malloc(strlen(GBLisoCurrentDir) + strlen(itemName) + 1) failed");
    strcpy(pathAndNameOnIso, GBLisoCurrentDir);
    strcat(pathAndNameOnIso, itemName);
    
    /* create full path and name for the extracted file */
    randomizedItemName = makeRandomFilename(itemName);
    pathAndNameOnFs = malloc(strlen(gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld))) + 
                             strlen(randomizedItemName) + 2);
    if(pathAndNameOnFs == NULL)
        fatalError("malloc(strlen(gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld))) + "
                   "strlen(randomizedItemName) + 2) failed");
    strcpy(pathAndNameOnFs, gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld)));
    strcat(pathAndNameOnFs, "/"); /* doesn't hurt even if not needed */
    strcat(pathAndNameOnFs, randomizedItemName);
    
    /* disable warnings, so user isn't confused with 'continue' buttons */
    bool(*savedWarningCbk)(const char*) = GBLvolInfo.warningCbk;
    GBLvolInfo.warningCbk = NULL;
    
    /* extract the file to the temporary directory */
    rc = bk_extract_as(&GBLvolInfo, pathAndNameOnIso, 
                       gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld)), 
                       randomizedItemName, false, activityProgressUpdaterCbk);
    if(rc <= 0)
    {
        warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("Failed to extract '%s': '%s'"),
                                               pathAndNameOnIso,
                                               bk_get_error_string(rc));
        gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
        gtk_dialog_run(GTK_DIALOG(warningDialog));
        gtk_widget_destroy(warningDialog);
        
        g_free(itemName);
        free(randomizedItemName);
        free(pathAndNameOnFs);
        free(pathAndNameOnIso);
        GBLvolInfo.warningCbk = savedWarningCbk;
        return;
    }
    
    addToTempFilesList(pathAndNameOnFs);
    
    /* start the editor */
    if(!fork())
    {
        execlp(gtk_entry_get_text(GTK_ENTRY(GBLeditorFld)), "editor", pathAndNameOnFs, NULL);
        
        printf("Failed to execute %s, error %d\n", gtk_entry_get_text(GTK_ENTRY(GBLeditorFld)), errno);
        printf("!! need to send a signal to iso master here to show a dialog\n");
        
        exit(1);
    }
    
    /* delete the file from the iso */
    rc = bk_delete(&GBLvolInfo, pathAndNameOnIso);
    if(rc <= 0)
    {
        warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("Failed to delete '%s': '%s'"),
                                               pathAndNameOnIso,
                                               bk_get_error_string(rc));
        gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
        gtk_dialog_run(GTK_DIALOG(warningDialog));
        gtk_widget_destroy(warningDialog);
        
        g_free(itemName);
        free(randomizedItemName);
        free(pathAndNameOnFs);
        free(pathAndNameOnIso);
        GBLvolInfo.warningCbk = savedWarningCbk;
        return;
    }
    
    GBLisoChangesProbable = true;
    
    /* add the file back fom tmp */
    rc = bk_add_as(&GBLvolInfo, pathAndNameOnFs, GBLisoCurrentDir, itemName, 
                   activityProgressUpdaterCbk);
    if(rc <= 0)
    {
        warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("Failed to add '%s': '%s'"),
                                               pathAndNameOnFs,
                                               bk_get_error_string(rc));
        gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
        gtk_dialog_run(GTK_DIALOG(warningDialog));
        gtk_widget_destroy(warningDialog);
    }
    
    g_free(itemName);
    free(randomizedItemName);
    free(pathAndNameOnFs);
    free(pathAndNameOnIso);
    GBLvolInfo.warningCbk = savedWarningCbk;
}

/* caller must free the string returned */
char* makeRandomFilename(const char* sourceName)
{
    int sourceNameLen;
    char* newName;
    char randomStr[RANDOM_STR_NAME_LEN];
    int numRandomCharsFilled;
    
    if(strlen(sourceName) > MAX_RANDOM_BASE_NAME_LEN)
        sourceNameLen = MAX_RANDOM_BASE_NAME_LEN;
    else
        sourceNameLen = strlen(sourceName);
    
    newName = malloc(sourceNameLen + RANDOM_STR_NAME_LEN + 2);
    if(newName == NULL)
        fatalError("newName = malloc(sourceNameLen + RANDOM_STR_NAME_LEN + 2) failed\n");
    
    numRandomCharsFilled = 0;
    while(numRandomCharsFilled < RANDOM_STR_NAME_LEN)
    {
        char oneRandomChar;
        bool gotGoodChar;
        
        gotGoodChar = false;
        while(!gotGoodChar)
        {
            oneRandomChar = random();
            if(64 < oneRandomChar && oneRandomChar < 91)
            {
                gotGoodChar = true;
            }
        }
        
        randomStr[numRandomCharsFilled] = oneRandomChar;
        
        numRandomCharsFilled++;
    }
    
    strncpy(newName, randomStr, RANDOM_STR_NAME_LEN);
    newName[RANDOM_STR_NAME_LEN] = '\0';
    strcat(newName, "-");
    strncat(newName, sourceName, sourceNameLen);
    newName[RANDOM_STR_NAME_LEN + sourceNameLen + 1] = '\0';
    
    return newName;
}

void viewSelectedBtnCbk(GtkMenuItem *menuitem, gpointer data)
{
    GtkTreeSelection* selection;
    
    /* do nothing if no image open */
    if(!GBLisoPaneActive)
        return;
    
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(GBLisoTreeView));
    
    /* there's just one row selected but this is the easiest way to do it */
    gtk_tree_selection_selected_foreach(selection, viewSelectedRowCbk, NULL);
}

void viewSelectedRowCbk(GtkTreeModel* model, GtkTreePath* path,
                        GtkTreeIter* iterator, gpointer data)
{
    int fileType;
    char* itemName;
    char* randomizedItemName;
    char* pathAndNameOnFs; /* to extract to and add from */
    char* pathAndNameOnIso; /* to delete from iso */
    int rc;
    GtkWidget* warningDialog;
    
    gtk_tree_model_get(model, iterator, COLUMN_HIDDEN_TYPE, &fileType, -1);
    
    
    gtk_tree_model_get(model, iterator, COLUMN_HIDDEN_TYPE, &fileType,
                                        COLUMN_FILENAME, &itemName, -1);
    if(fileType != FILE_TYPE_REGULAR)
    {
        warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("'%s' is not a regular file"),
                                               itemName);
        gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
        gtk_dialog_run(GTK_DIALOG(warningDialog));
        gtk_widget_destroy(warningDialog);
        
        g_free(itemName);
        return;
    }
    
    /* create full path and name for the file on the iso */
    pathAndNameOnIso = malloc(strlen(GBLisoCurrentDir) + strlen(itemName) + 1);
    if(pathAndNameOnIso == NULL)
        fatalError("malloc(strlen(GBLisoCurrentDir) + strlen(itemName) + 1) failed");
    strcpy(pathAndNameOnIso, GBLisoCurrentDir);
    strcat(pathAndNameOnIso, itemName);
    
    /* create full path and name for the extracted file */
    randomizedItemName = makeRandomFilename(itemName);
    pathAndNameOnFs = malloc(strlen(gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld))) + 
                             strlen(randomizedItemName) + 2);
    if(pathAndNameOnFs == NULL)
        fatalError("malloc(strlen(gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld))) + "
                   "strlen(randomizedItemName) + 2) failed");
    strcpy(pathAndNameOnFs, gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld)));
    strcat(pathAndNameOnFs, "/"); /* doesn't hurt even if not needed */
    strcat(pathAndNameOnFs, randomizedItemName);
    
    /* disable warnings, so user isn't confused with 'continue' buttons */
    bool(*savedWarningCbk)(const char*) = GBLvolInfo.warningCbk;
    GBLvolInfo.warningCbk = NULL;
    
    /* extract the file to the temporary directory */
    rc = bk_extract_as(&GBLvolInfo, pathAndNameOnIso, 
                       gtk_entry_get_text(GTK_ENTRY(GBLtempDirFld)), 
                       randomizedItemName, false, activityProgressUpdaterCbk);
    if(rc <= 0)
    {
        warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
                                               GTK_DIALOG_DESTROY_WITH_PARENT,
                                               GTK_MESSAGE_ERROR,
                                               GTK_BUTTONS_CLOSE,
                                               _("Failed to extract '%s': '%s'"),
                                               pathAndNameOnIso,
                                               bk_get_error_string(rc));
        gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
        gtk_dialog_run(GTK_DIALOG(warningDialog));
        gtk_widget_destroy(warningDialog);
        
        g_free(itemName);
        free(randomizedItemName);
        free(pathAndNameOnFs);
        free(pathAndNameOnIso);
        GBLvolInfo.warningCbk = savedWarningCbk;
        return;
    }
    
    addToTempFilesList(pathAndNameOnFs);
    
    /* start the viewer */
    if(!fork())
    {
        execlp(gtk_entry_get_text(GTK_ENTRY(GBLviewerFld)), "viewer", pathAndNameOnFs, NULL);
        
        printf("Failed to execute %s, error %d\n", gtk_entry_get_text(GTK_ENTRY(GBLviewerFld)), errno);
        printf("!! need to send a signal to iso master here to show a dialog\n");
        
        exit(1);
    }
    
    g_free(itemName);
    free(randomizedItemName);
    free(pathAndNameOnFs);
    free(pathAndNameOnIso);
    GBLvolInfo.warningCbk = savedWarningCbk;
}


syntax highlighted by Code2HTML, v. 0.9.1