#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jsapi.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <time.h>
#include <utime.h>
#include "jsencrypt.h"
#include "encrypt.h"


static EncryptionTypes *loadConfigFile(const char *path); 
static char *getConfigFilePath();

int main(int argc, char **argv){
    EncryptionTypes *encMap=NULL;
    EncryptionTypes *currentType=NULL;
    char *config = getConfigFilePath();
    char *stdinData=NULL;
    char *encryptedData=NULL;
    int stdinLength=0;
    int bufferLength=0;
    char buffer[1024];


    if (config == NULL){
        printf("Configuration file could not be loaded.");
        return 1;
    }


    encMap = loadConfigFile(config);
    if (encMap == NULL){
        printf("Cannot load config file, probably an invalid format.");
        return 2;
    }


    if (argc < 2){
        printf("Must specify encryption type to use as command line argument.");
        return 3;
    }
    
    currentType = encMap;
    while (currentType){
        if (strcmp(currentType->typeID, argv[1]) == 0){
            break;
        }
        else {
            currentType = currentType->next;
        }
    }

    if (currentType == NULL){
        printf("Specified encryption type is not supported.");
        return 4;
    }



    while (!feof(stdin)){
        memset(buffer, 0, sizeof(buffer));
        bufferLength=fread(buffer, sizeof(char), sizeof(buffer), stdin);
        if (bufferLength > 0){
            char *tmp = realloc(stdinData, stdinLength+bufferLength+1);
            if (tmp != NULL){
                stdinData = tmp;
                memset(&stdinData[stdinLength], 0, bufferLength+1);
                if (stdinLength > 0){
                    strcpy(&stdinData[stdinLength], buffer);
                }
                else {
                    strcpy(stdinData, buffer);
                }
                stdinLength += bufferLength;
            }
        }
    }

    printf("STDIN Data to Encrypt (with %s): \n%s", currentType->typeID, stdinData);
    encryptedData = encryptData(currentType, stdinData);

    printf("Encrypted data: \n\n%s", encryptedData);

    free(stdinData);
    free(encryptedData);

    return 0;
}

/* Loads the configuration file into a singly-linked list of encryption types.
 */

static EncryptionTypes *loadConfigFile(const char *path){
    EncryptionTypes *map=NULL;
    EncryptionTypes *node=NULL;
    EncryptionTypes *lastNode=NULL;

    FILE *fp = NULL;
    char data[1024];

    
    fp = fopen(path, "r");
    if (fp == NULL){
        return NULL;
    }

    while (!feof(fp)){
        memset(data, 0, sizeof(data));
        if (fgets(data, sizeof(data), fp) != NULL){
            char typeID[1024];
            char filePath[1024];
            char functionName[1024];
            memset(typeID, 0, sizeof(typeID));
            memset(filePath, 0, sizeof(filePath));
            memset(functionName, 0, sizeof(functionName));

            if (sscanf(data, "%s %s %s", typeID, filePath, functionName) == 3){
                node = malloc(sizeof(*node));
                memset(node, 0, sizeof(*node));
                node->typeID = strdup(typeID);
                node->filePath = strdup(filePath);
                node->functionName = strdup(functionName);

                if (map == NULL){
                    map = node;
                    lastNode = map;
                }
                else {
                    while (lastNode->next != NULL){
                        lastNode = lastNode->next;
                    }
                    lastNode->next = node;
                }
            }
        }
    }
    return map;
}

/* Return the path of the configuration file for this applciation.  
 * First we will check for a user config in ~
 * Second we will check for a global config in /etc
 * If no config file is found, return NULL
 */
static char *getConfigFilePath(){
    char *filePath=NULL;
    char *expandedPath=NULL;
    int filePathLen = 0;
    struct stat fileDetails;


    filePathLen = snprintf(NULL, 0, "~/.%s", APPCONFIG_NAME)+1;

    if (filePathLen > 1){
        filePath = malloc(sizeof(*filePath)*filePathLen);
        memset(filePath, 0, sizeof(*filePath)*filePathLen);
        snprintf(filePath, sizeof(*filePath)*filePathLen, "~/.%s", APPCONFIG_NAME);

        expandedPath = expandTilda(filePath);   
        free(filePath);
        filePath = NULL;

        /* Check to see if this config file exists using stat. */
        if (stat(expandedPath, &fileDetails) == 0){
            return expandedPath;
        }
        else {
            free(expandedPath);
            expandedPath=NULL;
            filePath=NULL;
        }
    }

    
    filePathLen = snprintf(NULL, 0, "/etc/%s", APPCONFIG_NAME)+1;
    if (filePathLen > 1){
        filePath = malloc(sizeof(*filePath)*filePathLen);
        memset(filePath, 0, sizeof(*filePath)*filePathLen);
        snprintf(filePath, sizeof(*filePath)*filePathLen, "/etc/%s", APPCONFIG_NAME);

        expandedPath = expandTilda(filePath);   
        free(filePath);

        /* Check to see if this config file exists using stat. */
        if (stat(expandedPath, &fileDetails) == 0){
            return expandedPath;
        }
        else {
            free(expandedPath);
            filePath = NULL;
        }
    }
    return expandedPath;
}


/* Returns the current user's home directory in place of the shortcut ~
 */
char *expandTilda(const char *str){
    char *homeDir=NULL;
    char *finalOutput = NULL;
    int finalOutputLen = 0;

    homeDir = getenv("HOME");
    if (homeDir!=NULL){
        char *copy = strdup(str);
        char *beforeTilda=copy;
        char *afterTilda=copy;

        if (homeDir[strlen(homeDir)-1] != '/'){
            char *tmp=homeDir;
            homeDir = malloc((strlen(tmp)+1)*sizeof(*homeDir));
            memset(homeDir, 0, (strlen(tmp)+1));
            sprintf(homeDir, "%s/", tmp);
        }
        else {
            homeDir = strdup(homeDir);
        }
    
        beforeTilda = copy;
        afterTilda = strchr(copy, '~');
        while (afterTilda != NULL){
            *afterTilda = '\0';
            afterTilda++;

            while (*afterTilda == '/'){
                *afterTilda='\0';
                afterTilda++;
            }

            finalOutputLen = strlen(beforeTilda)+strlen(homeDir)+strlen(afterTilda)+1;
            finalOutput = malloc(sizeof(*finalOutput)*finalOutputLen);
            memset(finalOutput, 0, sizeof(*finalOutput)*finalOutputLen);
    
            snprintf(finalOutput, sizeof(*finalOutput)*finalOutputLen, "%s%s%s", beforeTilda, homeDir, afterTilda);


            free(copy);
            copy = finalOutput;
            beforeTilda = copy;
            afterTilda = strchr(copy, '~');
        }
        
        return finalOutput;
    }
    else {
        return strdup(str);
    }
}

