//
//  PrinterFunctions.m
//  IOS_SDK
//
//  Created by Tzvi on 8/2/11.
//  Copyright 2011 - 2013 STAR MICRONICS CO., LTD. All rights reserved.
//

#import "PrinterFunctions.h"
#import <StarIO/SMPort.h>
#import <StarIO/SMBluetoothManager.h>
#import <sys/time.h>
#include <unistd.h>

@implementation PrinterFunctions

/**
 * This function checks the status of the printer.
 * The check status function can be used for both portable and non portable printers.
 * portName - Port name to use for communication. This should be (TCP:<IPAddress>)
 * portSettings - Should be blank
 */
+ (NSString*)CheckStatusWithPortname:(NSString *)portName portSettings:(NSString *)portSettings sensorSetting:(SensorActive)sensorActiveSetting
{
    SMPort *starPort = nil;
    @try
    {
        starPort = [SMPort getPort:portName :portSettings :10000];
        if (starPort == nil) {
            return @"No Printer Found";
        }
        usleep(1000 * 1000);
        
        StarPrinterStatus_2 status;
        [starPort getParsedStatus:&status :2];
        
        NSString *message = @"";
        if (status.offline == SM_TRUE)
        {
            message = @"The printer is offline";
            if (status.coverOpen == SM_TRUE)
            {
                message = [message stringByAppendingString:@"\nCover is Open"];
            }
            else if (status.receiptPaperEmpty == SM_TRUE)
            {
                message = [message stringByAppendingString:@"\nOut of Paper"];
            }
        }
        else
        {
            message = @"The Printer is online";
        }

        NSString *drawerStatus;
        if (sensorActiveSetting == SensorActiveHigh)
        {
            drawerStatus = (status.compulsionSwitch == SM_TRUE) ? @"Open" : @"Close";
            message = [message stringByAppendingFormat:@"\nCash Drawer: %@", drawerStatus];
        }
        else if (sensorActiveSetting == SensorActiveLow)
        {
            drawerStatus = (status.compulsionSwitch == SM_FALSE) ? @"Open" : @"Close";
            message = [message stringByAppendingFormat:@"\nCash Drawer: %@", drawerStatus];
        }
       
        return message;
    }
    @catch (PortException *exception)
    {
        return @"Get status failed";
    }
    @finally 
    {
        [SMPort releasePort:starPort];
    }
}

#pragma mark Text Formatting

/**
 * This function prints raw text to the print.  It show how the text can be formated.  For example changing its size.
 * portName - Port name to use for communication. This should be (TCP:<IPAddress>)
 * portSettings - Should be blank
 * slashedZero - boolean variable to tell the printer to weather to put a slash in the zero characters that it print
 * underline - boolean variable that Tells the printer if should underline the text
 * invertColor - boolean variable that tells the printer if it should invert the text its printing.  All White space will become black and the characters will be left white
 * emphasized - boolean variable that tells the printer if it should emphasize the printed text.  This is sort of like bold but not as dark, but darker then regular characters.
 * upperline - boolean variable that tells the printer if to place a line above the text.  This only supported by new printers.
 * upsideDown - boolean variable that tells the printer if the text should be printed upside-down
 * heightExpansion - This integer tells the printer what multiple the character height should be, this should be from 0 to 5 representing multiples from 1 to 6
 * widthExpansion - This integer tell the printer what multiple the character width should be, this should be from 0 to 5 representing multiples from 1 to 6.
 * leftMargin - The left margin for the text.  Although the max value for this can be 255, the value shouldn't get that high or the text could be pushed off the page.
 * alignment - The alignment of the text. The printers support left, right, and center justification
 * textData - The text to print
 * textDataSize - The amount of text to send to the printer
 */
+ (void)PrintTextWithPortname:(NSString *)portName portSettings:(NSString*)portSettings textData:(unsigned char *)textData  textDataSize:(unsigned int)textDataSize
{
    NSMutableData *commands = [[NSMutableData alloc] init];
    
	unsigned char initial[] = {0x1b, 0x40};
	[commands appendBytes:initial length:2];
    
    [commands appendBytes:textData length:textDataSize];
    
    unsigned char lf = 0x0a;
    [commands appendBytes:&lf length:1];
    
    [self sendCommand:commands portName:portName portSettings:portSettings timeoutMillis:10000];
    
}

+ (void)sendCommand:(NSData *)commandsToPrint portName:(NSString *)portName portSettings:(NSString *)portSettings
      timeoutMillis:(u_int32_t)timeoutMillis
{
    int commandSize = (int)[commandsToPrint length];
    unsigned char *dataToSentToPrinter = (unsigned char *)malloc(commandSize);
    [commandsToPrint getBytes:dataToSentToPrinter];
    
    NSMutableString *message;
    
    SMPort *starPort = nil;
    @try
    {
        starPort = [SMPort getPort:portName :portSettings :timeoutMillis];
        if (starPort == nil)
        {
            [message appendString:@"Fail to Open Port"];
            return;
        }
        
        StarPrinterStatus_2 status;
        [starPort beginCheckedBlock:&status :2];
        if (status.offline == SM_TRUE) {
            [message appendString:@"Printer is offline"];
            return;
        }
        
        struct timeval endTime;
        gettimeofday(&endTime, NULL);
        endTime.tv_sec += 30;
        
        int totalAmountWritten = 0;
        while (totalAmountWritten < commandSize)
        {
            int remaining = commandSize - totalAmountWritten;
            int amountWritten = [starPort writePort:dataToSentToPrinter :totalAmountWritten :remaining];
            totalAmountWritten += amountWritten;
            
            struct timeval now;
            gettimeofday(&now, NULL);
            if (now.tv_sec > endTime.tv_sec) {
                break;
            }
        }
        
        if (totalAmountWritten < commandSize) {
            [message appendString:@"Write port timed out"];
            return;
        }
        
        [starPort endCheckedBlock:&status :2];
        if (status.offline == SM_TRUE) {
            [message appendString:@"Printer is offline"];
            return;
        }
    }
    @catch (PortException *exception)
    {
        [message appendString:@"Write port timed out"];
    }
    @finally
    {
        free(dataToSentToPrinter);
        [SMPort releasePort:starPort];
    }
}

//#pragma mark Bluetooth Setting
//+ (SMBluetoothManager *)loadBluetoothSetting:(NSString *)portName portSettings:(NSString *)portSettings {
//    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
//
//    if ([portName rangeOfString:@"BT:" options:NSCaseInsensitiveSearch range:NSMakeRange(0, 3)].location == NSNotFound) {
//        alert.message = @"This function is available via the bluetooth interface only.";
//        [alert show];
//        return nil;
//    }
//
//    SMDeviceType deviceType;
//    if ([portSettings rangeOfString:@"MINI" options:NSCaseInsensitiveSearch].location != NSNotFound) {
//        deviceType = SMDeviceTypePortablePrinter;
//    } else {
//        deviceType = SMDeviceTypeDesktopPrinter;
//    }
//
//    SMBluetoothManager *manager = [[SMBluetoothManager alloc] initWithPortName:portName deviceType:deviceType];
//    if (manager == nil) {
//        alert.message = @"initWithPortName:deviceType: is failure.";
//        [alert show];
//        return nil;
//    }
//
//    if ([manager open] == NO) {
//        alert.message = @"open is failure.";
//        [alert show];
//        return nil;
//    }
//
//    if ([manager loadSetting] == NO) {
//        alert.message = @"loadSetting is failure.";
//        [alert show];
//        [manager close];
//        return nil;
//    }
//
//    [manager close];
//
//    return manager;
//}

//#pragma mark diconnect bluetooth
//
//+ (void)disconnectPort:(NSString *)portName portSettings:(NSString *)portSettings timeout:(u_int32_t)timeout {
//    SMPort *port = [SMPort getPort:portName :portSettings :timeout];
//    if (port == nil) {
//        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fail to Open Port"
//                                                        message:@""
//                                                       delegate:nil
//                                              cancelButtonTitle:@"OK"
//                                              otherButtonTitles:nil];
//        [alert show];
////        [alert release];
//        return;
//    }
//
//    BOOL result = [port disconnect];
//    if (result == NO) {
//        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fail to Disconnect" message:@"" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
//        [alert show];
////        [alert release];
//        return;
//    }
//
//    [SMPort releasePort:port];
//}

@end
