#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>  // For decompression

#include "dshlib.h"

/*
 * Implement your main function by building a loop that prompts the
 * user for input.  Use the SH_PROMPT constant from dshlib.h and then
 * use fgets to accept user input.  Since we want fgets to also handle
 * end of file so we can run this headless for testing we need to check
 * the return code of fgets.  I have provided an example below of how
 * to do this assuming you are storing user input inside of the cmd_buff
 * variable.
 *
 *      while(1){
 *        printf("%s", SH_PROMPT);
 *        if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){
 *           printf("\n");
 *           break;
 *        }
 *        //remove the trailing \n from cmd_buff
 *        cmd_buff[strcspn(cmd_buff,"\n")] = '\0';
 *
 *        //IMPLEMENT THE REST OF THE REQUIREMENTS
 *      }
 *
 *   Also, use the constants in the dshlib.h in this code.
 *      SH_CMD_MAX              maximum buffer size for user input
 *      EXIT_CMD                constant that terminates the dsh program
 *      SH_PROMPT               the shell prompt
 *      OK                      the command was parsed properly
 *      WARN_NO_CMDS            the user command was empty
 *      ERR_TOO_MANY_COMMANDS   too many pipes used
 *
 *   Expected output:
 *
 *      CMD_OK_HEADER      if the command parses properly. You will
 *                         follow this by the command details
 *
 *      CMD_WARN_NO_CMD    if the user entered a blank command
 *      CMD_ERR_PIPE_LIMIT if the user entered too many commands using
 *                         the pipe feature, e.g., cmd1 | cmd2 | ... |
 *
 *  See the provided test cases for output expectations.
 */

#define DRAGON_FILE "dragon.bin"
#define DRAGON_BUFFER_SIZE 100000

void print_dragon_from_file() {
    FILE *file = fopen(DRAGON_FILE, "rb");
    if (!file) {
        fprintf(stderr, "Error: Could not open %s\n", DRAGON_FILE);
        return;
    }

    // Determine the size of the compressed file
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    rewind(file);

    unsigned char *compressed_data = (unsigned char *)malloc(file_size);
    if (!compressed_data) {
        fprintf(stderr, "Memory allocation failed!\n");
        fclose(file);
        return;
    }

    // Read the compressed data from the file
    fread(compressed_data, 1, file_size, file);
    fclose(file);

    unsigned char *decompressed_data = (unsigned char *)malloc(DRAGON_BUFFER_SIZE);
    if (!decompressed_data) {
        fprintf(stderr, "Memory allocation failed!\n");
        free(compressed_data);
        return;
    }

    // Decompress the data
    uLongf decompressed_size = DRAGON_BUFFER_SIZE;
    if (uncompress(decompressed_data, &decompressed_size, compressed_data, file_size) != Z_OK) {
        fprintf(stderr, "Decompression failed!\n");
        free(compressed_data);
        free(decompressed_data);
        return;
    }

    printf("%s", decompressed_data);

    free(compressed_data);
    free(decompressed_data);
}

int main()
{
    char *cmd_buff = (char *)malloc(SH_CMD_MAX * sizeof(char));
    if (cmd_buff == NULL) {
        fprintf(stderr, "Memory allocation failed!\n");
        return -1;
    }

    command_list_t clist;

    while (1) {
        printf("%s", SH_PROMPT);
        if (fgets(cmd_buff, SH_CMD_MAX, stdin) == NULL) {
            printf("\n");
            break;
        }

        // Remove the trailing newline character
        cmd_buff[strcspn(cmd_buff, "\n")] = '\0';

        // Check for empty command
        if (strlen(cmd_buff) == 0) {
            printf(CMD_WARN_NO_CMD);
            continue;
        }

        // Exit command
        if (strcmp(cmd_buff, EXIT_CMD) == 0) {
            break;
        }

        // Check for dragon command
        if (strcmp(cmd_buff, "dragon") == 0) {
            print_dragon_from_file();
            continue;
        }

        // Parse the command line
        int rc = build_cmd_list(cmd_buff, &clist);

        if (rc == ERR_TOO_MANY_COMMANDS) {
            printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
            continue;
        } else if (rc == ERR_CMD_OR_ARGS_TOO_BIG) {
            fprintf(stderr, "Error: Command or arguments too big.\n");
            continue;
        }

        // Display parsed commands
        printf(CMD_OK_HEADER, clist.num);
        for (int i = 0; i < clist.num; i++) {
            printf("<%d> %s", i + 1, clist.commands[i].exe);
            if (strlen(clist.commands[i].args) > 0) {
                printf(" [%s]", clist.commands[i].args);
            }
            printf("\n");
        }
    }

    free(cmd_buff);
    return OK;
}