//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. 
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
// GNU General Public License for more details. 
//

#include <stdlib.h>
#include <string.h>
#include <AppKit/AppKit.h>

#include "config.h"
#include "IWADController.h"

typedef enum
{
    IWAD_DOOM1,
    IWAD_DOOM2,
    IWAD_TNT,
    IWAD_PLUTONIA,
    IWAD_CHEX,
    IWAD_HERETIC,
    IWAD_HEXEN,
    IWAD_STRIFE,
    IWAD_FREEDOOM1,
    IWAD_FREEDOOM2,
    IWAD_FREEDM,
    NUM_IWAD_TYPES
} IWAD;

static NSString *IWADLabels[NUM_IWAD_TYPES] =
{
    @"Doom",
    @"Doom II: Hell on Earth",
    @"Final Doom: TNT: Evilution",
    @"Final Doom: Plutonia Experiment",
    @"Chex Quest",
    @"Heretic",
    @"Hexen",
    @"Strife",
    @"Freedoom: Phase 1",
    @"Freedoom: Phase 2",
    @"FreeDM",
};

static NSString *IWADFilenames[NUM_IWAD_TYPES + 1] =
{
    @"doom.wad",
    @"doom2.wad",
    @"tnt.wad",
    @"plutonia.wad",
    @"chex.wad",
    @"heretic.wad",
    @"hexen.wad",
    @"strife.wad",
    @"freedoom1.wad",
    @"freedoom2.wad",
    @"freedm.wad",
    @"undefined"
};

@implementation IWADController

- (void) getIWADList: (NSPathControl **) iwadList
{
    iwadList[IWAD_DOOM1] = self->doom1;
    iwadList[IWAD_DOOM2] = self->doom2;
    iwadList[IWAD_TNT] = self->tnt;
    iwadList[IWAD_PLUTONIA] = self->plutonia;
    iwadList[IWAD_CHEX] = self->chex;
    iwadList[IWAD_HERETIC] = self->heretic;
    iwadList[IWAD_HEXEN] = self->hexen;
    iwadList[IWAD_STRIFE] = self->strife;
    iwadList[IWAD_FREEDOOM1] = self->freedoom1;
    iwadList[IWAD_FREEDOOM2] = self->freedoom2;
    iwadList[IWAD_FREEDM] = self->freedm;
}

- (IWAD) getSelectedIWAD
{
    unsigned int i;

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        if ([self->iwadSelector titleOfSelectedItem] == IWADLabels[i])
        {
            return i;
        }
    }

    return NUM_IWAD_TYPES;
}

// Get the location of the selected IWAD. 

- (NSString *) getIWADLocation
{
    IWAD selectedIWAD;
    NSPathControl *iwadList[NUM_IWAD_TYPES];

    selectedIWAD = [self getSelectedIWAD];

    if (selectedIWAD == NUM_IWAD_TYPES)
    {
        return nil;
    }
    else
    {
        [self getIWADList: iwadList];

	return [[iwadList[selectedIWAD] URL] path];
    }
}

static const char *NameForIWAD(IWAD iwad)
{
    switch (iwad)
    {
        case IWAD_HERETIC:
            return "heretic";

        case IWAD_HEXEN:
            return "hexen";

        case IWAD_STRIFE:
            return "strife";

        default:
            return "doom";
    }
}

// Get the name used for the executable for the selected IWAD. 

- (const char *) getGameName
{
    return NameForIWAD([self getSelectedIWAD]);
}

- (void) setIWADConfig
{
    NSPathControl *iwadList[NUM_IWAD_TYPES];
    NSUserDefaults *defaults;
    NSString *key;
    NSString *value;
    unsigned int i;

    [self getIWADList: iwadList];

    // Load IWAD filename paths

    defaults = [NSUserDefaults standardUserDefaults];

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        key = IWADFilenames[i];
        value = [defaults stringForKey:key];

        if (value ! = nil)
        {
            [iwadList[i] setURL: [NSURL fileURLWithPath: value]];
        }
    }
}

// On startup, set the selected item in the IWAD dropdown

- (void) setDropdownSelection
{
    NSUserDefaults *defaults;
    NSString *selected;
    unsigned int i;

    defaults = [NSUserDefaults standardUserDefaults];
    selected = [defaults stringForKey: @"selected_iwad"];

    if (selected == nil)
    {
        return;
    }

    // Find this IWAD in the filenames list, and select it. 

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        if ([selected isEqualToString:IWADFilenames[i]])
        {
            [self->iwadSelector selectItemWithTitle:IWADLabels[i]];
            break;
        }
    }
}

// Set the dropdown list to include an entry for each IWAD that has
// been configured.   Returns true if at least one IWAD is configured. 

- (BOOL) setDropdownList
{
    NSPathControl *iwadList[NUM_IWAD_TYPES];
    BOOL have_wads;
    id location;
    unsigned int i;
    unsigned int enabled_wads;

    // Build the new list. 

    [self getIWADList: iwadList];
    [self->iwadSelector removeAllItems];

    enabled_wads = 0;

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        location = [[iwadList[i] URL] path];

        if (location ! = nil && [location length] > 0)
        {
            [self->iwadSelector addItemWithTitle: IWADLabels[i]];
            ++enabled_wads;
        }
    }

    // Enable/disable the dropdown depending on whether there
    // were any configured IWADs. 

    have_wads = enabled_wads > 0;
    [self->iwadSelector setEnabled: have_wads];

    // Restore the old selection. 

    [self setDropdownSelection];

    return have_wads;
}

- (void) saveConfig
{
    NSPathControl *iwadList[NUM_IWAD_TYPES];
    IWAD selectedIWAD;
    NSUserDefaults *defaults;
    NSString *key;
    NSString *value;
    unsigned int i;

    [self getIWADList: iwadList];

    // Store all IWAD locations to user defaults. 

    defaults = [NSUserDefaults standardUserDefaults];

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        key = IWADFilenames[i];
        value = [[iwadList[i] URL] path];

        [defaults setObject:value forKey:key];
    }

    // Save currently selected IWAD. 

    selectedIWAD = [self getSelectedIWAD];
    [defaults setObject:IWADFilenames[selectedIWAD]
              forKey:@"selected_iwad"];
}

// Callback method invoked when the configuration button in the main
// window is clicked. 

- (void) openConfigWindow: (id)sender
{
    if (![ self->configWindow isVisible])
    {
        [self->configWindow makeKeyAndOrderFront: sender];
    }
}

// Callback method invoked when the close button is clicked. 

- (void) closeConfigWindow: (id)sender
{
    [self->configWindow orderOut: sender];
    [self saveConfig];
    [self setDropdownList];
}

- (void) awakeFromNib
{
    [self->configWindow center];

    // Set configuration for all IWADs from configuration file. 

    [self setIWADConfig];

    // Populate the dropdown IWAD list. 

    if ([self setDropdownList])
    {
        [self setDropdownSelection];
    }
}

// Generate a value to set for the DOOMWADPATH environment variable
// that contains each of the configured IWAD files. 

- (char *) doomWadPath
{
    NSPathControl *iwadList[NUM_IWAD_TYPES];
    NSString *location;
    unsigned int i;
    BOOL first;
    char *env;
    size_t env_len;

    [self getIWADList: iwadList];

    // Calculate length of environment string. 

    env_len = 0;

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        location = [[iwadList[i] URL] path];

        if (location ! = nil && [location length] > 0)
        {
            env_len += [location length] + 1;
        }
    }

    // Build string. 

    env = malloc(env_len);
    strlcpy(env, "", env_len);

    first = YES;

    for (i=0; i<NUM_IWAD_TYPES; ++i)
    {
        location = [[iwadList[i] URL] path];

        if (location ! = nil && [location length] > 0)
        {
            if (! first)
            {
                strlcat(env, ":", env_len);
            }

            strlcat(env, [location UTF8String], env_len);
            first = NO;
        }
    }

    return env;
}

- (NSString *) autoloadPath
{
    NSArray *array = NSSearchPathForDirectoriesInDomains(
        NSApplicationSupportDirectory, NSUserDomainMask, YES);
    if ([array count] == 0)
    {
        return nil;
    }

    IWAD selectedIWAD = [self getSelectedIWAD];
    if (selectedIWAD == NUM_IWAD_TYPES)
    {
        return nil;
    }

    NSString *base = [array objectAtIndex:0];
    return [NSString pathWithComponents:
        [NSArray arrayWithObjects: base, @PACKAGE_TARNAME, @"autoload",
                                   IWADFilenames[selectedIWAD], nil]];
}

// Set the DOOMWADPATH environment variable to contain the path to each
// of the configured IWAD files. 

- (void) setEnvironment
{
    char *doomwadpath;
    char *env;

    // Get the value for the path. 

    doomwadpath = [self doomWadPath];

    asprintf(&env, "DOOMWADPATH=%s", doomwadpath);

    free(doomwadpath);

    // Load into environment:

    putenv(env);

    //free(env);
}

// Examine a path to a WAD and determine whether it is an IWAD file. 
// If so, it is added to the IWAD configuration, and true is returned. 

- (BOOL) addIWADPath: (NSString *) path
{
    NSPathControl *iwadList[NUM_IWAD_TYPES];
    NSArray *pathComponents;
    NSString *filename;
    unsigned int i;

    [self getIWADList: iwadList];

    // Find an IWAD file that matches the filename in the path that we
    // have been given. 

    pathComponents = [path pathComponents];
    filename = [pathComponents objectAtIndex: [pathComponents count] - 1];

    for (i = 0; i < NUM_IWAD_TYPES; ++i)
    {
        if ([filename caseInsensitiveCompare: IWADFilenames[i]] == 0)
        {
            // Configure this IWAD. 

            [iwadList[i] setURL: [NSURL fileURLWithPath: path]];

            // Rebuild dropdown list and select the new IWAD. 

            [self setDropdownList];
            [self->iwadSelector selectItemWithTitle:IWADLabels[i]];
            return YES;
        }
    }

    // No IWAD found with this name. 

    return NO;
}

- (BOOL) selectGameByName: (const char *) name
{
    NSPathControl *iwadList[NUM_IWAD_TYPES];
    NSString *location;
    const char *name2;
    int i;

    // Already selected an IWAD of the desired type?  Just return
    // success. 
    if (! strcmp(name, [self getGameName]))
    {
        return YES;
    }

    // Search through the configured IWADs and try to select the
    // desired game. 
    [self getIWADList: iwadList];

    for (i = 0; i < NUM_IWAD_TYPES; ++i)
    {
        location = [[iwadList[i] URL] path];
        name2 = NameForIWAD(i);

        if (! strcmp(name, name2)
         && location ! = nil && [location length] > 0)
        {
            [self->iwadSelector selectItemWithTitle:IWADLabels[i]];
            return YES;
        }
    }

    // User hasn't configured any WAD(s) for the desired game type. 
    return NO;
}

@end

