//
// $Id: EmpireImageVendor.m,v 1.11 1997/10/31 04:51:48 nygard Exp $
//

//
//  This file is a part of Empire, a game of exploration and conquest.
//  Copyright (C) 1996  Steve Nygard
//
//  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.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//  You may contact the author by:
//     e-mail:  nygard@telusplanet.net
//

#import "Empire.h"

RCSID ("$Id: EmpireImageVendor.m,v 1.11 1997/10/31 04:51:48 nygard Exp $");

#import "EmpireImageVendor.h"
#import "Token.h"

static EmpireImageVendor *_instance = nil;

// Common images
static NSImage *default_cursor;
static NSImage *direction_cursor;

static NSImage *explosion;

struct image_names
{
    NSString *i_name;
    NSImage **i_image;
};

static struct image_names class_images[] =
{
    { @"cursor.tiff", &default_cursor },
    { @"direction_cursor.tiff", &direction_cursor },

    { @"explosion.tiff", &explosion },
};

//======================================================================
// This provides access to many of the images required by Empire,
// especially since most of them are dynamically generated.  It also
// provides notification to observers when any of the player colors
// change, so that views that use these images can be updated with the
// new colors.
//======================================================================

#define EmpireImageVendor_VERSION 1

@implementation EmpireImageVendor

+ (void) initialize
{
    if (self == [EmpireImageVendor class])
    {
        [self setVersion:EmpireImageVendor_VERSION];

        // It appears that there is no guarantee that we can get the bundle
        // in this method, so we have to work around this.

        if ([NSBundle bundleForClass:self] == nil)
        {
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector (loadClassImages)
                                                  name:NSApplicationDidFinishLaunchingNotification
                                                  object:NSApp];
        }
        else
        {
            [self loadClassImages];
        }
    }
}

//----------------------------------------------------------------------

+ (void) loadClassImages
{
    int l;
    NSBundle *thisBundle;
    NSString *imagePath;

    if (self == [EmpireImageVendor class])
    {
        thisBundle = [NSBundle bundleForClass:self];
        NSAssert (thisBundle != nil, @"Could not get bundle.");

        // load class images
        for (l = 0; l < sizeof (class_images) / sizeof (struct image_names); l++)
        {
            imagePath = [thisBundle pathForImageResource:class_images[l].i_name];
            NSAssert1 (imagePath != nil, @"Could not find image: '%@'", class_images[l].i_name);

            *(class_images[l].i_image) = [[NSImage alloc] initByReferencingFile:imagePath];
            NSAssert1 (*(class_images[l].i_image) != nil, @"Couldn't load image: '%@'\n", class_images[l].i_name);
        }
    }

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

//----------------------------------------------------------------------

+ instance
{
    // Might want to grab colors from defaults.
    if (_instance == nil)
    {
        NSColor *player1 = [NSColor colorWithCalibratedRed:34 / 255.0 green:255 / 255.0 blue:248 / 255.0 alpha:1.0];
        NSColor *player2 = [NSColor colorWithCalibratedRed:255 / 255.0 green:26 / 255.0 blue:6 / 255.0 alpha:1.0];
        NSColor *player3 = [NSColor colorWithCalibratedRed:243 / 255.0 green:255 / 255.0 blue:0 / 255.0 alpha:1.0];
    
        _instance = [[EmpireImageVendor alloc] initWithColors:player1:player2:player3];
    }
  
    return _instance;
}

//----------------------------------------------------------------------

- initWithColors:(NSColor *)color1:(NSColor *)color2:(NSColor *)color3
{
    Icon loop_to_land[] = {i_army, i_sentry, i_fighter, i_hovercraft};
    Icon loop_to_water[] = {i_fighter, i_unloaded_transport, i_loaded_transport, i_submarine, i_destroyer,
                            i_cruiser, i_unloaded_carrier, i_loaded_carrier, i_battleship, i_hovercraft};
    Player p;
    int l;
  
    [super init];

    for (p = p_neutral; p <= p_player3; p++)
    {
        cityTokens[p] = [[Token alloc] initIcon:i_none onTerrain:t_city forPlayer:p];
        cityImages[p] = [cityTokens[p] tokenImage];
    }

    for (p = p_neutral; p <= p_player3; p++)
    {
        for (l = 0; l < 4; l++)
        {
            onLandTokens[p][l] = [[Token alloc] initIcon:loop_to_land[l] onTerrain:t_land forPlayer:p];
            onLandImages[p][l] = [onLandTokens[p][l] tokenImage];
        }

        for (l = 0; l < 10; l++)
        {
            onWaterTokens[p][l] = [[Token alloc] initIcon:loop_to_water[l] onTerrain:t_water forPlayer:p];
            onWaterImages[p][l] = [onWaterTokens[p][l] tokenImage];
        }

        for (l = 0; l < 12; l++)
        {
            onCityTokens[p][l] = [[Token alloc] initIcon:i_army + l onTerrain:t_city forPlayer:p];
            onCityImages[p][l] = [onCityTokens[p][l] tokenImage];
        }
    }

    unknown = [[Token alloc] initIcon:i_none onTerrain:t_unknown forPlayer:p_neutral];
    land = [[Token alloc] initIcon:i_none onTerrain:t_land forPlayer:p_neutral];
    water = [[Token alloc] initIcon:i_none onTerrain:t_water forPlayer:p_neutral];

    [self setColor:[NSColor lightGrayColor] forPlayer:p_neutral];
    [self setColor:color1 forPlayer:p_player1];
    [self setColor:color2 forPlayer:p_player2];
    [self setColor:color3 forPlayer:p_player3];

    observers = [[NSMutableArray array] retain];

    return self;
}

//----------------------------------------------------------------------

- (void) dealloc
{
    int l;
    Player p;
  
    for (p = p_neutral; p <= p_player3; p++)
    {
        SNRelease (cityTokens[p]);

        for (l = 0; l < 4; l++)
            SNRelease (onLandTokens[p][l]);

        for (l = 0; l < 10; l++)
            SNRelease (onWaterTokens[p][l]);

        for (l = 0; l < 12; l++)
            SNRelease (onCityTokens[p][l]);
    }
  
    // Notify things using this?  Observer pattern?
    SNRelease (observers);

    [super dealloc];
}

//----------------------------------------------------------------------

- (NSColor *) colorForPlayer:(Player)p
{
    return [Token colorForPlayer:p];
}

//----------------------------------------------------------------------

- (void) setColor:(NSColor *)aColor forPlayer:(Player)p
{
    [Token setColor:aColor forPlayer:p];

    [self recacheImagesForPlayer:p];
    [self notify:(p == p_player1):(p == p_player2):(p == p_player3):(p == p_neutral)];
}

//----------------------------------------------------------------------

- (void) recacheImagesForPlayer:(Player)p
{
    int l;

    [cityImages[p] recache];
    for (l = 0; l < 4; l++)
        [onLandImages[p][l] recache];
    for (l = 0; l < 10; l++)
        [onWaterImages[p][l] recache];
    for (l = 0; l < 12; l++)
        [onCityImages[p][l] recache];
}

//----------------------------------------------------------------------

- (NSImage *) cursor
{
    return default_cursor;
}

//----------------------------------------------------------------------

- (NSImage *) directionCursor
{
    return direction_cursor;
}

//----------------------------------------------------------------------

- (NSImage *) unknown
{
    return [unknown tokenImage];
}

//----------------------------------------------------------------------

- (NSImage *) land
{
    return [land tokenImage];
}

//----------------------------------------------------------------------

- (NSImage *) water
{
    return [water tokenImage];
}

//----------------------------------------------------------------------

- (NSImage *) explosion
{
    return explosion;
}

//----------------------------------------------------------------------

- (NSImage *) neutralCity
{
    return cityImages[p_neutral];
}

//----------------------------------------------------------------------

- (void) player:(Player)aPlayer:(NSImage **)city:(NSImage ***)onLand:(NSImage ***)onWater:(NSImage ***)onCity:(NSImage ***)odd
{
    if (city != NULL)
        *city = cityImages[aPlayer];

    if (onLand != NULL)
        *onLand = onLandImages[aPlayer];

    if (onWater != NULL)
        *onWater = onWaterImages[aPlayer];

    if (onCity != NULL)
        *onCity = onCityImages[aPlayer];

    if (odd != NULL)
        *odd = [Token oddballsForPlayer:aPlayer]; 
}

@end

//======================================================================

@implementation EmpireImageVendor (ObserverPattern)

- (void) attach:observer
{
    // What if same observer attached twice, detached once?
    [observers addObject:observer];
}

//----------------------------------------------------------------------

- (void) detach:observer
{
    [observers removeObject:observer];
}

//----------------------------------------------------------------------

- (void) notify:(BOOL)player1:(BOOL)player2:(BOOL)player3:(BOOL)other
{
    NSEnumerator *observerEnumerator = [observers objectEnumerator];
    id tmp;

    while (tmp = [observerEnumerator nextObject])
    {
        if ([tmp respondsToSelector:@selector (vendorImagesUpdated::::)] == YES)
        {
            [tmp vendorImagesUpdated:player1:player2:player3:other];
        }
    }
}

@end
