/*
SREncodings.m

Author: Makoto Kinoshita

Copyright 2004 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "SREncodings.h"

NSString*	SREncodingToIANATransformerName = @"SREncodingToIANATransformer";
NSString*	SRIANAToEncodingTransformerName = @"SRIANAToEncodingTransformer";

unsigned     SRConvertedShiftJISStringEncoding;
unsigned     SRConvertedJapaneseEUCStringEncoding;
unsigned     SRConvertedISO2022JPStringEncoding;

static CFStringEncodings    _cfEncodings[] = {
    kSRStringEncodingJapaneseAutoDetect,    // Japanese (Auto detect)
    kCFStringEncodingShiftJIS,              // Japanese (Shift JIS)
    kCFStringEncodingISO_2022_JP,           // Japanese (ISO 2022-JP)
    kCFStringEncodingEUC_JP,                // Japanese (EUC)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingUTF8,                  // Unicode (UTF-8)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatin1,             // Western (ISO Latin 1)
    kCFStringEncodingMacRoman,              // Western (Mac OS Roman)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingBig5,                  // Traditional Chinese (Big 5)
    kCFStringEncodingBig5_HKSCS_1999,       // Traditional Chinese (Big 5 HKSCS)
    kCFStringEncodingDOSChineseTrad,        // Traditional Chinese (Windows, DOS)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISO_2022_KR,           // Korean (ISO 2022-KR)
    kCFStringEncodingMacKorean,             // Korean (Mac OS)
    kCFStringEncodingDOSKorean,             // Korean (Windows, DOS)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatinArabic,        // Arabic (ISO 8859-6)
    kCFStringEncodingDOSArabic,             // Arabic (Windows)
    kCFStringEncodingISOLatinHebrew,        // Hebrew (ISO 8859-8)
    kCFStringEncodingWindowsHebrew,         // Hebrew (Windows)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatinGreek,         // Greek (ISO 8859-7)
    kCFStringEncodingWindowsGreek,          // Greek (Windows)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatinCyrillic,      // Cyrillic (ISO 8859-5)
    kCFStringEncodingMacCyrillic,           // Cyrillic (Mac OS)
    kCFStringEncodingKOI8_R,                // Cyrillic (KOI8-R)
    kCFStringEncodingWindowsCyrillic,       // Cyrillic (Windows)
    kCFStringEncodingMacUkrainian,          // Ukrainian (KOI8-U)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingDOSThai,               // Thai (Windows, DOS)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingGB_2312_80,            // Simplified Chinese (GB 2312)
    kCFStringEncodingHZ_GB_2312,            // Simplified Chinese (HZ GB 2312)
    kCFStringEncodingGB_18030_2000,         // Chinese (GB 18030)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatin2,             // Central European (ISO Latin 2)
    kCFStringEncodingMacCentralEurRoman,    // Central European (Mac OS)
    kCFStringEncodingWindowsLatin2,         // Central European (Windows Latin 2)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingWindowsVietnamese,     // Vietnamese (Windows)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatin5,             // Turkish (ISO Latin 5)
    kCFStringEncodingWindowsLatin5,         // Turkish (Windows Latin 5)
    kCFStringEncodingInvalidId, 
    kCFStringEncodingISOLatin4,             // Central European (ISO Latin 4)
    kCFStringEncodingWindowsBalticRim,      // Baltic Rim (Windows)
};

#pragma mark -
//--------------------------------------------------------------//
// SRIANAToEncodingTransformer
//--------------------------------------------------------------//

@implementation SRIANAToEncodingTransformer

+ (void)load
{
    // Register instance of SRIANAToEncodingTransformer
	[self setValueTransformer:[[SRIANAToEncodingTransformer alloc] init] 
            forName:SRIANAToEncodingTransformerName];
    
    // Initialize encoding value
    SRConvertedShiftJISStringEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingShiftJIS);
    SRConvertedJapaneseEUCStringEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_JP);
    SRConvertedISO2022JPStringEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISO_2022_JP);
}

+ (BOOL)allowsReverseTransformation
{
	return YES;
}

+ (Class)transformedValueClass
{
	return [NSNumber class];
}

+ (NSString*)_JapaneseAutoDetectEncodingName
{
    static NSString*    _autoDetectName = nil;
    if (!_autoDetectName) {
        _autoDetectName = NSLocalizedString(@"Japanese (Auto detect)", nil);
    }
    return _autoDetectName;
}

- (id)transformedValue:(id)value
{
	if (!value) {
		return nil;
	}
	if (![value isKindOfClass:[NSString class]]) {
		return nil;
	}
	
    // For Japanese auto detect
    if ([value isEqualToString:[SRIANAToEncodingTransformer _JapaneseAutoDetectEncodingName]]) {
        return [NSNumber numberWithUnsignedInt:SRJapaneseAutoDetectEncoding];
    }
    
    // Get CFStringEncoding value from IANA name
	CFStringEncoding	cfEncoding;
	cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)value);
	
    // Convert to NSStringEncoding
	NSStringEncoding  encoding;
	encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
	
	return [NSNumber numberWithUnsignedInt:encoding];
}

- (id)reverseTransformedValue:(id)value
{
	if (!value) {
		return nil;
	}
	if (![value respondsToSelector:@selector(unsignedIntValue)]) {
		return nil;
	}
    
    // Get NSStringEncoding value
	NSStringEncoding	encoding;
	encoding = [value unsignedIntValue];
	
    // For Japanese auto encoding
    if (encoding == kSRStringEncodingJapaneseAutoDetect) {
        return [SRIANAToEncodingTransformer _JapaneseAutoDetectEncodingName];
    }
    
	// Convert to CFStringEncoding
    CFStringEncoding    cfEncoding;
    cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
    return (NSString*)CFStringConvertEncodingToIANACharSetName(cfEncoding);
}

@end

#pragma mark -
//--------------------------------------------------------------//
// SREncodingToIANATransformer
//--------------------------------------------------------------//

@implementation SREncodingToIANATransformer

+ (void)load
{
    // Register instance of SREncodingToIANATransformer
	[self setValueTransformer:[[SREncodingToIANATransformer alloc] init] 
            forName:SREncodingToIANATransformerName];
}

+ (BOOL)allowsReverseTransformation
{
	return YES;
}

+ (Class)transformedValueClass
{
	return [NSString class];
}

- (id)transformedValue:(id)value
{
    return [[NSValueTransformer valueTransformerForName:SRIANAToEncodingTransformerName] 
            reverseTransformedValue:value];
}

- (id)reverseTransformedValue:(id)value
{
    return [[NSValueTransformer valueTransformerForName:SRIANAToEncodingTransformerName] 
            transformedValue:value];
}

@end

#pragma mark -
//--------------------------------------------------------------//
// SREncodings
//--------------------------------------------------------------//

@implementation SREncodings

+ (id<NSMenuItem>)_createMenuItemOfCFStringEncoding:(CFStringEncoding)cfEncoding
{
    id<NSMenuItem> menuItem;
    
    if (cfEncoding == kCFStringEncodingInvalidId) {
        menuItem = [NSMenuItem separatorItem];
    }
    else {
        // Convert to NSStringEncoding and get encoding name
        NSStringEncoding    encoding;
        NSString*           encodingName;
        if (cfEncoding != kSRStringEncodingJapaneseAutoDetect) {
            encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
            encodingName = [NSString localizedNameOfStringEncoding:encoding];
        }
        else {
            // Selecting Japanese encoding enables Japanese auto detection
            encoding = SRJapaneseAutoDetectEncoding;
            encodingName = NSLocalizedString(@"Japanese (Auto detect)", nil);
        }
        
        // Create menu item
        menuItem = [[NSMenuItem alloc] initWithTitle:encodingName 
                action:@selector(setTextEncodingAction:) 
                keyEquivalent:@""];
        [menuItem autorelease];
        [menuItem setTag:encoding];
    }
    
    return menuItem;
}

+ (NSMenu*)textEncodingMenu
{
    // Create text encoding menu
    NSMenu* encodingMenu;
    encodingMenu = [[NSMenu alloc] initWithTitle:@"encoding"];
    [encodingMenu autorelease];
    
    int i;
    for (i = 0; i < sizeof(_cfEncodings) / sizeof(CFStringEncodings); i++) {
        // Add menu item
        [encodingMenu addItem:[self _createMenuItemOfCFStringEncoding:_cfEncodings[i]]];
    }
    
    return encodingMenu;
}

+ (NSArray*)textEncodingMenuItems
{
    // Create array
    NSMutableArray* items;
    items = [NSMutableArray array];
    
    int i;
    for (i = 0; i < sizeof(_cfEncodings) / sizeof(CFStringEncodings); i++) {
        // Add menu item
        [items addObject:[self _createMenuItemOfCFStringEncoding:_cfEncodings[i]]];
    }
    
    return items;
}

- (void)awakeFromNib
{
    // Create text encoding menu
    NSMenu* encodingMenu;
    encodingMenu = [SREncodings textEncodingMenu];
    
    // Set menu popup button
    [_encodingPopup setMenu:encodingMenu];
}

- (NSPopUpButton*)encodingPopup
{
    return _encodingPopup;
}

@end
