#import "SignoSoft.h"
#include <sys/types.h>
#include <sys/sysctl.h>
#include <ifaddrs.h>
#include <arpa/inet.h>

@implementation SignoSoft
{
    CDVInvokedUrlCommand* initialInvokingCommand; //private by default
    NSMutableArray* discoveredDevices;
    NSNumber* actPressure;
    NSString* connectedPen;
    CGFloat maxPressure;
    BOOL wacomDeviceConnected;
    BOOL forceTouchCapable;
    CBCentralManager* m_centralManager;
    NSTimer *availabilityTimer;
    
}


- (void)checkAvailability {
    if([m_centralManager state] == CBCentralManagerStatePoweredOn) {
        
        NSArray* myArray = [NSArray arrayWithObject:[CBUUID UUIDWithString:@"180A"]];
        NSArray* peripherals = [m_centralManager retrieveConnectedPeripheralsWithServices:myArray];
        
        for (CBPeripheral* peripheral in peripherals)
        {
            if ([[peripheral name] isEqualToString:@"Apple Pencil"])
            {
                connectedPen = [peripheral identifier].UUIDString;
                
                NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                            @"StylusDeviceConnected", @"event",
                                            [peripheral identifier].UUIDString, @"UUID",
                                            @"Apple Pencil", @"device",
                                            nil];
                
                NSString* serialized = [self toJSON:outMessage];
                if(serialized != nil) [self returnMessage:serialized];
                
                [availabilityTimer invalidate];
            }
        }
        
    }
    else {
        
        [availabilityTimer invalidate];
        connectedPen = nil;
        
        NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"StylusDeviceDisconnected", @"event",
                                    nil, @"UUID",
                                    nil];
        
        NSString* serialized = [self toJSON:outMessage];
        if(serialized != nil) [self returnMessage:serialized];
    }
    
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    NSLog(@"centralManagerDidUpdateState state %ld", (long)[central state]);
    
    NSString* state;
    
    if ([central state] == CBCentralManagerStatePoweredOff) state = @"CBCentralManagerStatePoweredOff";
    if ([central state] == CBCentralManagerStatePoweredOn) state = @"CBCentralManagerStatePoweredOn";
    if ([central state] == CBCentralManagerStateResetting) state = @"CBCentralManagerStateResetting";
    if ([central state] == CBCentralManagerStateUnauthorized) state = @"CBCentralManagerStateUnauthorized";
    if ([central state] == CBCentralManagerStateUnknown) state = @"CBCentralManagerStateUnknown";
    if ([central state] == CBCentralManagerStateUnsupported) state = @"CBCentralManagerStateUnsupported";
    
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"DeviceDebbug",
                                @"event",
                                [NSString stringWithFormat:@"state %@", state],
                                @"log",
                                nil];
    NSString* serialized = [self toJSON:outMessage];
    if(serialized != nil) [self returnMessage:serialized];
    
    
    if ([central state] == CBCentralManagerStatePoweredOn)
    {
        NSLog(@"availabilityTimer set");
       availabilityTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target: self selector:@selector(checkAvailability) userInfo: nil repeats:YES];
        
    }
    else {
        
        [availabilityTimer invalidate];
        connectedPen = nil;
        
        NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"StylusDeviceDisconnected", @"event",
                                    nil, @"UUID",
                                    nil];
        
        NSString* serialized = [self toJSON:outMessage];
        if(serialized != nil) [self returnMessage:serialized];
    }
}

- (void)start:(CDVInvokedUrlCommand*)command
{
    wacomDeviceConnected = NO;
    forceTouchCapable = NO;
    maxPressure = 0.0f;
    
    m_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
    
    
    //NSLog(@"start maxPressure: %f",maxPressure);
    if ([self.viewController.traitCollection respondsToSelector:@selector(forceTouchCapability)]) {
        forceTouchCapable = YES; //temporary measure
        //NSLog(@"respondsToSelector");
        //if (self.viewController.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)
        //    {forceTouchCapable = YES; // capable
        //    NSLog(@"forceTouchCapable");
        //    }
    }
    //NSLog(@"endif");
    
    if (initialInvokingCommand != nil){//if already initiated
        initialInvokingCommand = command;
        NSLog(@"Olready inited");
        discoveredDevices = [[[WacomManager getManager] getDevices] mutableCopy];
        
        
        WacomDevice* connectedDevice = [self getConnectedDevice];
        if(connectedDevice != nil) {
            NSLog(@"Wacon connected");
            
            wacomDeviceConnected = YES;
            maxPressure = (CGFloat)[connectedDevice getMaximumPressure];
            [self deviceConnected:connectedDevice];
        }
        else [[WacomManager getManager] startDeviceDiscovery];
        
        [self returnSimpleEvent :@"CordovaConnected"];
        [self returnSimpleEvent :@"StylusPluginRepeatedStart"];
        return;
    }
    else {
        NSLog(@"Init now");
    }
    
    initialInvokingCommand = command;
    discoveredDevices = nil;
    actPressure = [NSNumber numberWithDouble:0.0];
    discoveredDevices = [[NSMutableArray alloc] init];
    
    [[WacomManager getManager] registerForNotifications:self];
    [[WacomManager getManager] startDeviceDiscovery];
    
    discoveredDevices = [[[WacomManager getManager] getDevices] mutableCopy];
    
    //NSLog(@"@%", listedDevice)
    
    [self.webView setUserInteractionEnabled:YES];
    MyGestureRecognizer * gr = [[MyGestureRecognizer alloc] initWithTarget:self action:@selector(recognizerFired:)];// autorelease];
    gr.delegate = self;
    gr.cancelsTouchesInView = NO;
    gr.delaysTouchesBegan = NO;
    gr.delaysTouchesEnded = NO;
    //[self.webView addGestureRecognizer:gr];
    
    [self returnSimpleEvent :@"CordovaConnected"];

    //[gr release];
    
    //if([discoveredDevices count] == 0)
    
    //old part
    //NSString* name = [[initialInvokingCommand arguments] objectAtIndex:0];
    //NSString* message = [NSString stringWithFormat: @"Hello, %@", name];
    
    //[self returnSimpleEvent :@"StylusDeviceDiscoveryStartedaaaaaa"];
    //[self returnSimpleEvent :@"StylusDeviceDiscoveryStartedbbbbbb"];
    
    //[self returnSimpleEvent :@"StylusDeviceDiscoveryStarted"];
    
}

- (NSString *) getUUID
{
    if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) {
        return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    }
    return @"";
}

- (NSString *) platform {
    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    char *machine = malloc(size);
    sysctlbyname("hw.machine", machine, &size, NULL, 0);
    NSString *platform = [NSString stringWithUTF8String:machine];
    free(machine);
    return platform;
}

- (NSString *)getIPAddress {
    
    NSString *address = @"error";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(&interfaces);
    if (success == 0) {
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        while(temp_addr != NULL) {
            if(temp_addr->ifa_addr->sa_family == AF_INET) {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                    
                }
                
            }
            
            temp_addr = temp_addr->ifa_next;
        }
    }
    // Free memory
    freeifaddrs(interfaces);
    return address;
    
}

- (void)confirmData:(CDVInvokedUrlCommand*)command
{
    discoveredDevices = [[[WacomManager getManager] getDevices] mutableCopy];
    WacomDevice* wacomDevice = [self getConnectedDevice];
    
    NSDictionary* mess = @{
                           @"manufacturer":@"",
                           @"model":@"",
                           @"serial":@"",
                           @"localTime":@"",
                           @"systemLang":@"",
                           @"clientIp":@"",
                           @"model":@"",
                           @"user":@""
                         };
    
    NSMutableDictionary *outMessage = [mess mutableCopy];
    [outMessage setObject:@"Apple" forKey:@"manufacturer"];
    [outMessage setObject:[self getUUID] forKey:@"serial"];
    [outMessage setObject:[NSString stringWithFormat:@"%@ %@", [self platform], [UIDevice currentDevice].systemVersion] forKey:@"model"];
    [outMessage setObject:[UIDevice currentDevice].name forKey:@"user" ];
    [outMessage setObject:[[NSLocale preferredLanguages] firstObject] forKey:@"systemLang"];
    [outMessage setObject:[NSString stringWithFormat:@"%f",[[NSDate date] timeIntervalSince1970]] forKey:@"localTime"];
    [outMessage setObject:[self getIPAddress] forKey:@"clientIp"];
    
    if(wacomDevice) {
         NSLog(@"Connected wacom");
        [outMessage setObject:[NSString stringWithFormat:@"%@, %@", [outMessage valueForKeyPath:@"manufacturer"], [wacomDevice getManufacturerName]] forKey:@"manufacturer"];
        [outMessage setObject:[NSString stringWithFormat:@"%@, %@ %lu", [outMessage valueForKeyPath:@"model"],[wacomDevice getName], (unsigned long)[wacomDevice getModel]] forKey:@"model"];
        [outMessage setObject:[NSString stringWithFormat:@"%@, %@", [outMessage valueForKeyPath:@"serial"], [wacomDevice getUUIDAsNSString]] forKey:@"serial"];
        
    }
    else if(connectedPen) {
        NSLog(@"Connected Apple pen");
        [outMessage setObject:[NSString stringWithFormat:@"%@, %@", [outMessage valueForKeyPath:@"model"], @"Apple pen"] forKey:@"model"];
        [outMessage setObject:[NSString stringWithFormat:@"%@, %@", [outMessage valueForKeyPath:@"serial"], connectedPen] forKey:@"serial"];
    }

    
    //[device getUUIDAsNSString]
    
   
    
    
    NSString* serialized = [self toJSON:outMessage];
    //now return
    //we always return to invoking command's callback
    if(serialized != nil) [self returnMessage:serialized invokingCommand: command];
}


- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command
{
    NSString* UUID = [[initialInvokingCommand arguments] objectAtIndex:0];
    WacomDevice* device;
    
    if(UUID == nil) device = [self getDeviceByUUID:UUID];
    else device = [self getConnectedDevice];
    
    if(device != nil) {
        NSLog(@"Wacon connected");
    }
    
    
    NSDictionary* outMessage = nil;
    
    NSLog(@"%@", outMessage);
    
    if (device != nil) outMessage = [self getDeviceInfoDict:device];
    else if(connectedPen) {
        NSLog(@"Connected Apple pen");
    }
    else {
        outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                      @"StylusDeviceNotFound",
                      @"event",
                      UUID,
                      @"UUID",
                      nil];
    }
    
    NSString* serialized = [self toJSON:outMessage];
    //now return
    //we always return to invoking command's callback
    if(serialized != nil) [self returnMessage:serialized invokingCommand: command];
}

- (void)getDeviceTable:(CDVInvokedUrlCommand*)command
{
    
    NSMutableDictionary* deviceTable = [NSMutableDictionary dictionary];
    for(WacomDevice * listedDevice in discoveredDevices )
    {
        NSDictionary* outMessage = [self getDeviceInfoDict:listedDevice];
        [deviceTable setObject:outMessage forKey:[listedDevice getUUIDAsNSString]];
    }
    NSString* serialized = nil;
    if([deviceTable count]==0) {
        NSDictionary* msg = [NSDictionary dictionaryWithObjectsAndKeys:
                             @"StylusDeviceTableEmpty",
                             @"event",
                             nil];
        serialized = [self toJSON:msg];
    }
    else serialized = [self toJSON:deviceTable];
    //now return
    //we always return to invoking command's callback
    if(serialized != nil) [self returnMessage:serialized invokingCommand: command];
}

- (void)selectDevice:(CDVInvokedUrlCommand*)command
{
    NSString* UUID = [[initialInvokingCommand arguments] objectAtIndex:0];
    WacomDevice* device = [self getDeviceByUUID:UUID];
    NSDictionary* outMessage = nil;
    if(device != nil) {
        [[WacomManager getManager] selectDevice:device];
        outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                      @"StylusDeviceSelected",
                      @"event",
                      [device getUUIDAsNSString],
                      @"UUID",
                      nil];
    }
    else {
        outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                      @"StylusDeviceNotFound",
                      @"event",
                      [device getUUIDAsNSString],
                      @"UUID",
                      nil];
    }
    NSString* serialized = [self toJSON:outMessage];
    //we always return to invoking command's callback
    if(serialized != nil) [self returnMessage:serialized invokingCommand: command];
}

- (void)stopDeviceDiscovery:(CDVInvokedUrlCommand*)command
{
    [[WacomManager getManager] stopDeviceDiscovery];
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"StylusDeviceDiscoveryEnded",
                                @"event",
                                nil];
    NSString* serialized = [self toJSON:outMessage];
    //we always return to invoking command's callback
    if(serialized != nil) [self returnMessage:serialized invokingCommand: command];
}

- (void)setTouchRejection:(CDVInvokedUrlCommand*)command
{
    NSString *message = nil;
    NSString* target = [[initialInvokingCommand arguments] objectAtIndex:0];
    if([target isEqualToString:@"On"]) {
        [TouchManager GetTouchManager].touchRejectionEnabled = YES;
    }
    else if([target isEqualToString:@"Off"]) {
        [TouchManager GetTouchManager].touchRejectionEnabled = NO;
    }
    else{
        //error bad input
    }
    
    if ([TouchManager GetTouchManager].touchRejectionEnabled == YES)
    {
        message = @"On"; //"You have turned ON touch rejection.";
    }
    else message = @"Off"; //"You have turned OFF touch rejection.";
    
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"StylusTouchRejectionSet",
                                @"event",
                                message,
                                @"status",
                                nil];
    NSString* serialized = [self toJSON:outMessage];
    if(serialized != nil) [self returnMessage:serialized invokingCommand: command];
}

//to implement:
//Coalesced Tracked touches
//? check if nsmutable in touchesmoved not too slow
//? [[WacomManager getManager] deselectDevice: device];
//? [self setMultipleTouchEnabled:YES];
//set handeness [[TouchManager GetTouchManager] setHandedness:eh_Left];
//? auto select [[WacomManager getManager] setMinimumSignalStrength:(int)value].
//? Multitasking gestures disabling - writting hand down


////////////////////////////////////////////////////////////////////////////////
// Notes: just add the device to the discovered table. demonstrates signal strength
- (void)deviceDiscovered:(WacomDevice *)device
{
    NSLog(@"wacom connected");

    //if the device list has not been allocated return
    //if(discoveredDevices == nil)
    //    return;//error
    //if we are not in discovery mode return
    if(![[WacomManager getManager] isDiscoveryInProgress])
        return;
    //if we already have the device in our list return
    for(WacomDevice * listDevice in discoveredDevices)
    {
        if([device getPeripheral] == [listDevice getPeripheral])
            return;
    }
    //add the device to our internal list
    [discoveredDevices addObject:device];
    
    //notify app
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"StylusDeviceDiscovered",
                                @"event",
                                [device getUUIDAsNSString],
                                @"UUID",
                                nil];
    NSString* serialized = [self toJSON:outMessage];
    if(serialized != nil) [self returnMessage:serialized];
    
    [[WacomManager getManager] selectDevice:device];
    [self stopDeviceDiscovery:initialInvokingCommand];
}
////////////////////////////////////////////////////////////////////////////////
// Notes: update the device table then dismiss the popover.
- (void)deviceConnected:(WacomDevice *)device
{
     NSLog(@"device connected");
      [availabilityTimer invalidate];
    
    //CGFloat maxpres = [device getMaximumPressure];
    //NSNumber* msxx = @(maxpres);
    wacomDeviceConnected = YES;
    maxPressure = (CGFloat)[device getMaximumPressure];
    //notify app to update status
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"StylusDeviceConnected",
                                @"event",
                                [device getUUIDAsNSString],
                                @"UUID",
                                @"Wacom", @"device",
                                nil];
    NSString* serialized = [self toJSON:outMessage];
    if(serialized != nil) [self returnMessage:serialized];
}
////////////////////////////////////////////////////////////////////////////////
// Notes: remove the device then dismiss the popover
-(void)deviceDisconnected:(WacomDevice *)device
{
    NSLog(@"device disconnect");
    // if the device list has been allocated
    //if(discoveredDevices == nil)
    //[device get]
    //    return;//error
    wacomDeviceConnected = NO;
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"StylusDeviceDisconnected",
                                @"event",
                                [device getUUIDAsNSString],
                                @"UUID",
                                nil];
    
    //if the device is in the list remove it.
    for(WacomDevice * listedDevice in discoveredDevices )
    {
        if([listedDevice getPeripheral] == [device getPeripheral])
            [discoveredDevices removeObject:listedDevice];
    }
    
    NSString* serialized = [self toJSON:outMessage];
    if(serialized != nil) [self returnMessage:serialized];
}
////////////////////////////////////////////////////////////////////////////////
// Notes: if the power is off, it pops a warning dialog.
- (void)discoveryStatePoweredOff
{
    //show message: You must turn on Bluetooth in Settings";
    [self returnSimpleEvent :@"StylusWarningBluetoothOff"];
}


-(void)stylusEvent:(WacomStylusEvent *)stylusEvent {
    
    NSDictionary* outMessage = nil;
    
    switch ([stylusEvent getType]) {
        case eStylusEventType_PressureChange: {//Pressure data received.
            CGFloat stylusPressure = [stylusEvent getPressure];
            if(maxPressure  == 0.0f) maxPressure = (CGFloat)[[self getConnectedDevice] getMaximumPressure];
            if(maxPressure != 0.0f) actPressure = @(stylusPressure/maxPressure);
            
            outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                          @"StylusPressureChange",
                          @"event",
                          actPressure,
                          @"pressure",
                          nil];
        }
            break;
        case eStylusEventType_ButtonReleased:
            switch ([stylusEvent getButton]) {
                case 2: {
                    //button 2 clicked
                    outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusButtonReleased",
                                  @"event",
                                  [NSNumber numberWithInt:2],
                                  @"button",
                                  nil];
                }
                    break;
                case 1:
                {
                    //button 1 clicked
                    outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusButtonReleased",
                                  @"event",
                                  [NSNumber numberWithInt:1],
                                  @"button",
                                  nil];
                }
                    break;
                default:
                    break;
            }
            break;
        case eStylusEventType_ButtonPressed:
        {
            switch ([stylusEvent getButton])
            {
                case 2:
                {
                    outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusButtonPressed",
                                  @"event",
                                  [NSNumber numberWithInt:2],
                                  @"button",
                                  nil];
                }
                    break;
                    
                case 1:
                {
                    outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusButtonPressed",
                                  @"event",
                                  [NSNumber numberWithInt:1],
                                  @"button",
                                  nil];
                }
                    break;
                default:
                    break;
            }
        }
            break;
        case eStylusEventType_MACAddressAvaiable:
            outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                          @"StylusMACAddressAvaiable",
                          @"event",
                          [stylusEvent getMACAddress],
                          @"MAC",
                          nil];
            break;
        case eStylusEventType_BatteryLevelChanged:
            outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                          @"StylusBatteryLevelChanged",
                          @"event",
                          [NSNumber numberWithUnsignedLong:[stylusEvent getBatteryLevel]],
                          @"batteryLevel",
                          nil];
            break;
        default:
            //includes eStylusEventType_NameUpdated
            //eStylusEventType_ManufacturerUpdated
            //eStylusEventType_FirmwareVersionUpdated
            //eStylusEventType_SoftwareVersionUpdated
            //see WacomDeviceFramework.h
            break;
    }
    
    if(outMessage != nil) {
        NSString* serialized = [self toJSON:outMessage];
        if(serialized != nil) [self returnMessage:serialized];
        else {
            //idiot device
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Notes: returns NSString of serialized JSON of almost full device information, or nil on failure
- (NSDictionary *)getDeviceInfoDict:(WacomDevice *)device
{
    NSLog(@"device info");
    //I don't know how to get info from following two data types:
    //a
    //WacomDeviceType deviceType = [device getDeviceType]; //eDeviceType_Stylus
    NSString* typeOfDevice = @"WacomStylus";
    
    
    //the peripheral provided by CoreBluetooth. see CBPeripheral.h for details of info that can be retrieved
    //it's details on bluetooth connection and its status
    //CBPeripheral* peripheral = [device getPeripheral];
    
    //[device getUUIDAsNSString]
    
    //build an info object and convert to json
    NSDictionary* deviceInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                [device getUUIDAsNSString],
                                @"UUID",  //NSString which contains the value of the UUID.
                                [device getMacAddress],
                                @"MAC", //the unique id for this device (Bluetooth address) in string form.
                                [device getShortName],
                                @"shortName",  //the name of the device, such as "Wacom"
                                [device getName],
                                @"longName",//the name of the device, such as "Wacom Intuos Creative Stylus"
                                [device isCurrentlyConnected],
                                @"isCurrentlyConnected",  //YES/NO if the device is currently connected
                                [NSNumber numberWithInt:[device getSignalStrength]],
                                @"signalStrength", //the strength of the signal in db higher is -1 being better than -2.
                                [device supportsPressure],
                                @"supportsPressure", //YES/NO based on whether the device supports pressure events.
                                [NSNumber numberWithInteger:[device getMaximumPressure]],
                                @"maximumPressure", //the maximum pressure reading that can be supplied by the stylus. Minimum is 0.
                                [NSNumber numberWithInteger:[device getMinimumPressure]],
                                @"minimumPressure", //the minimum pressure reading that can be supplied by the stylus, always 0.
                                [NSNumber numberWithInt:[device getButtonCount]],
                                @"ButtonCount", //a button count that can then be used with a loop to walk through the buttons.
                                [device getManufacturerName],
                                @"manufacturerName",  //the manufacturer of the device "Wacom Co. Ltd."
                                [device getFirmwareVersion],
                                @"firmwareVersion",  //the version of the firmware
                                [NSNumber numberWithInt:[device getSoftwareVersion]],
                                @"softwareVersion", //the stylus' software version.
                                typeOfDevice,
                                @"wacomDeviceType", //WacomDeviceType to determine if you are using a stylus or a Wacom tablet with a stylus
                                nil];
    
    //return NSDictionary
    return deviceInfo;
}

- (WacomDevice *)getDeviceByUUID:(NSString *)UUID{
    for(WacomDevice * listedDevice in discoveredDevices )
    {
        if([UUID isEqualToString:[listedDevice getUUIDAsNSString]])
            return listedDevice;
    }
    //if not found:
    return nil;
}

- (WacomDevice *)getConnectedDevice
{
    for(WacomDevice * listedDevice in discoveredDevices )
    {
        if([listedDevice isCurrentlyConnected])
            return listedDevice;
    }
    //if none connected
    return nil;
}

- (BOOL)returnSimpleEvent:(NSString *)message{
    NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                message,
                                @"event",
                                nil];
    
    NSString* serialized = [self toJSON:outMessage];
    if(serialized != nil) {
        [self returnMessage:serialized];
        return YES;
    }
    else return NO;
}

//see returnSimpleEvent for input example
- (NSString *)toJSON:(NSDictionary *)outData{
    //convert object to json data
    NSError* error = nil;
    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:outData
                                                       options:NSJSONWritingPrettyPrinted error:&error];
    
    if (error != nil) { //error
        return nil;
    }
    else { //ok
        //print out the data contents
        NSString* message = [[NSString alloc] initWithData:jsonData
                                                  encoding:NSUTF8StringEncoding];
        return message;
    }
    
}


- (void)returnMessage:(NSString *)message{
    CDVPluginResult* result = [CDVPluginResult
                               resultWithStatus:CDVCommandStatus_OK
                               messageAsString:message];
    result.keepCallback = [NSNumber numberWithBool:YES];
    [self.commandDelegate sendPluginResult:result callbackId:initialInvokingCommand.callbackId];
}
//usage:  [self returnMessage:serialized invokingCommand: command];
- (void)returnMessage:(NSString *)message invokingCommand:(CDVInvokedUrlCommand *)command{
    CDVPluginResult* result = [CDVPluginResult
                               resultWithStatus:CDVCommandStatus_OK
                               messageAsString:message];
    result.keepCallback = [NSNumber numberWithBool:YES];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

//When using wacom stylus. Not for apple pencil.
- (NSDictionary *)trackedTouchesGetDictFromManager:(NSSet *)touches
{
    NSArray *trackedTouches = [[TouchManager GetTouchManager] getTrackedTouches];
    NSMutableDictionary* touchesTable = [NSMutableDictionary dictionary];//building output message
    //may be slow to use MUTABLE
    NSDate *date = [NSDate date];
    double timestamp = [date timeIntervalSince1970];
    for (TrackedTouch *trackedTouch in trackedTouches)
    {
        if ([touches containsObject:trackedTouch.associatedTouch])
        {
            
            double timestamp_ms = (timestamp - [date timeIntervalSinceNow]) * 1000.0;
            NSNumber *timestampNumber = [NSNumber numberWithDouble:timestamp_ms];
            NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                        @"StylusTouchEvent",
                                        @"event",
                                        @(trackedTouch.currentLocation.x),
                                        @"x",
                                        @(trackedTouch.currentLocation.y),
                                        @"y",
                                        actPressure,
                                        @"pressure",
                                        @"",
                                        @"orientation",
                                        @(timestamp_ms),
                                        @"timestamp",
                                        @(trackedTouch.previousLocation.x),
                                        @"xPrevious",
                                        @(trackedTouch.previousLocation.y),
                                        @"yPrevious",
                                        @"",
                                        @"type",
                                        [trackedTouch isStylus],
                                        @"isStylus",
                                        [trackedTouch isRejected],
                                        @"isRejected",
                                        nil];
            
            [touchesTable setObject:outMessage forKey:[timestampNumber stringValue]];
        }
    }
    return touchesTable;
}
//For apple pencil or generally when wacom stylus is not connected.
- (NSDictionary *)touchesGetDictForReport:(NSSet *)touches
{
    NSMutableDictionary* touchesTable = [NSMutableDictionary dictionary];//building output message
    //may be slow to use MUTABLE
    NSDate *date = [NSDate date];
    for (UITouch *touch in touches)
    {
        //double timestamp_ms = (timestamp - [date timeIntervalSinceNow]) * 1000.0;
        //NSNumber *timestampNumber = [NSNumber numberWithDouble:timestamp_ms];
        NSTimeInterval timestamp = [touch timestamp];
        CGPoint coordinates = [touch locationInView:self.webView];
        CGPoint prevCoordinates = [touch locationInView:self.webView];
        //NSLog(@"touchesGetDictForReport");
        //if (self.viewController.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable
        //|| self.webView.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)
        //    {forceTouchCapable = YES; // capable
        //    NSLog(@"forceTouchCapable");
        //    }
        
        NSString* touchType = @"fingerTouchScreen";
        if(forceTouchCapable) {
            //NSLog(@"forceTouchCapable");
            switch (touch.type) {
                case UITouchTypeDirect:
                    touchType = @"fingerTouchScreen"; break;
                case UITouchTypeStylus:
                    touchType = @"applePencil"; break;
                case UITouchTypeIndirect: //apple TV etc.
                    touchType = @"indirectTouch"; break;
                default:
                    break;
            }
            maxPressure = touch.maximumPossibleForce;
            if(maxPressure  != 0.0f) actPressure = @(touch.force/maxPressure);
            //NSLog(@"maxPressure: %f",maxPressure);
        }
        
        NSDictionary* outMessage = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"StylusTouchEvent",
                                    @"event",
                                    @(coordinates.x),
                                    @"x",
                                    @(coordinates.y),
                                    @"y",
                                    actPressure,
                                    @"pressure",
                                    @"",
                                    @"orientation",
                                    @(timestamp),
                                    @"timestamp",
                                    @(prevCoordinates.x),
                                    @"xPrevious",
                                    @(prevCoordinates.y),
                                    @"yPrevious",
                                    touchType,
                                    @"type",
                                    @"",
                                    @"isStylus",
                                    @"",//@([[self.webView traitCollection] forceTouchCapability] == UIForceTouchCapabilityAvailable),
                                    @"isRejected",
                                    nil];
        
        [touchesTable setObject:outMessage forKey:[NSString stringWithFormat:@"%f", timestamp]];
        
    }
    return touchesTable;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Adding, moving, and removing touches aren't needed if the view is registered
// [[TouchManager GetTouchManager] registerView:self];
- (void) gestureRecognizer:(UIGestureRecognizer *)gr beganWithTouches:(NSSet*)touches andEvent:(UIEvent *)event
{
    //pass touches to manager
    [[TouchManager GetTouchManager] addTouches:touches view:self.webView event:event];
    
    NSDictionary* touchesTable;
    if (wacomDeviceConnected) touchesTable = [self trackedTouchesGetDictFromManager:touches];
    else touchesTable = [self touchesGetDictForReport:touches];
    
    if([touchesTable count] > 0){
        NSDictionary* outEvent = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusTouchesBegan",
                                  @"event",
                                  touchesTable,
                                  @"touches",
                                  nil];
        NSString* serialized = [self toJSON:outEvent];
        if(serialized != nil) [self returnMessage:serialized];
    }
    
}




////////////////////////////////////////////////////////////////////////////////////////////////////
// Adding, moving, and removing touches aren't needed if the view is registered
// [[TouchManager GetTouchManager] registerView:self];
- (void) gestureRecognizer:(UIGestureRecognizer *)gr movedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event
{
    //pass touches to manager
    [[TouchManager GetTouchManager] moveTouches:touches view:self.webView event:event];
    
    NSDictionary* touchesTable;
    if (wacomDeviceConnected) touchesTable = [self trackedTouchesGetDictFromManager:touches];
    else touchesTable = [self touchesGetDictForReport:touches];
    
    if([touchesTable count] > 0){
        NSDictionary* outEvent = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusTouchesMoved",
                                  @"event",
                                  touchesTable,
                                  @"touches",
                                  nil];
        NSString* serialized = [self toJSON:outEvent];
        if(serialized != nil) [self returnMessage:serialized];
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
// Adding, moving, and removing touches aren't needed if the view is registered
// [[TouchManager GetTouchManager] registerView:self];
- (void) gestureRecognizer:(UIGestureRecognizer *)gr endedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event
{
    //pass touches to manager
    [[TouchManager GetTouchManager] moveTouches:touches view:self.webView event:event];
    
    NSDictionary* touchesTable;
    if (wacomDeviceConnected) touchesTable = [self trackedTouchesGetDictFromManager:touches];
    else touchesTable = [self touchesGetDictForReport:touches];
    
    if([touchesTable count] > 0){
        NSDictionary* outEvent = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusTouchesEnded",
                                  @"event",
                                  touchesTable,
                                  @"touches",
                                  nil];
        NSString* serialized = [self toJSON:outEvent];
        if(serialized != nil) [self returnMessage:serialized];
    }
    
    //remove touches from the manager on touchEnd
    [[TouchManager GetTouchManager] removeTouches:touches view:self.webView event:event];
}



////////////////////////////////////////////////////////////////////////////////////////////////////
// Adding, moving, and removing touches aren't needed if the view is registered
// [[TouchManager GetTouchManager] registerView:self];
- (void) gestureRecognizer:(UIGestureRecognizer *)gr cancelledWithTouches:(NSSet*)touches andEvent:(UIEvent *)event
{
    NSDictionary* touchesTable;
    if (wacomDeviceConnected) touchesTable = [self trackedTouchesGetDictFromManager:touches];
    else touchesTable = [self touchesGetDictForReport:touches];
    
    if([touchesTable count] > 0){
        NSDictionary* outEvent = [NSDictionary dictionaryWithObjectsAndKeys:
                                  @"StylusTouchesCancelled",
                                  @"event",
                                  touchesTable,
                                  @"touches",
                                  nil];
        NSString* serialized = [self toJSON:outEvent];
        if(serialized != nil) [self returnMessage:serialized];
    }
    
    //remove touches from the manager on touchCancel
    [[TouchManager GetTouchManager] removeTouches:touches view:self.webView event:event];
}

- (void) recognizerFired:(UIGestureRecognizer *)recognizer
{
    //empty
}

- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return YES;
}

@end

/*@implementation MyTouchRecognizer
 {
 CDVInvokedUrlCommand* passedInvokingCommand; //private by default
 NSMutableArray* discoveredDevices;
 NSNumber* actPressure;
 }
 
 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
 //pass touches to manager
 [[TouchManager GetTouchManager] moveTouches:touches view:self event:event];
 
 NSDictionary* touchesTable = [self touchesGetDictForReport:touches];
 
 if([touchesTable count] > 0){
 NSDictionary* outEvent = [NSDictionary dictionaryWithObjectsAndKeys:
 @"StylusTouchesMoved",
 @"event",
 touchesTable,
 @"touches",
 nil];
 NSString* serialized = [self toJSON:outEvent];
 if(serialized != nil) [self returnMessage:serialized];
 }
 
 }
 @end*/

