//
//  ViewController.m
//  LEDArray
//
//  Created by Brian Burley on 2025-02-24.
//

#import "LEDArrayController.h"

@implementation LEDArrayController
#pragma mark Form load/shutdown

- (void)viewWillDisappear {
    [super viewWillDisappear];
    if(ch){
        //Safely close example
        Phidget_setOnAttachHandler((PhidgetHandle)ch, NULL, NULL);
        Phidget_setOnDetachHandler((PhidgetHandle)ch, NULL, NULL);
        Phidget_setOnErrorHandler((PhidgetHandle)ch, NULL, NULL);
        Phidget_close((PhidgetHandle)ch);
        PhidgetLEDArray_delete(&ch);
        ch = nil;
    }
}

- (void)tabDidRequestResize:(NSSize)size {
    [self resizeWindowToContentSize:size];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    for (NSTabViewItem *item in self.tabViewController.tabViewItems) {
            if ([item.viewController isKindOfClass:[basicTabViewController class]]) {
                basicTabViewController *tabVC = (basicTabViewController *)item.viewController;
                tabVC.delegate = self;   // <-- THIS IS CRITICAL
            }
        }

    PhidgetReturnCode result;

    // Load the separate storyboard that contains PhidgetInfoBoxN
    NSStoryboard *phidgetStoryboard = [NSStoryboard storyboardWithName:@"PhidgetInfoBoxN" bundle:nil];

    // Instantiate the view controller
     self.infoBoxVC = [phidgetStoryboard instantiateControllerWithIdentifier:@"PhidgetInfoBoxN"];
    
    // Add the view to your placeholder container
    NSView *infoBoxView = self.infoBoxVC.view;
    infoBoxView.translatesAutoresizingMaskIntoConstraints = NO;

    [self->phidgetInfoView addSubview:infoBoxView];

    // Add constraints to make it fill the container
    [NSLayoutConstraint activateConstraints:@[
        [infoBoxView.topAnchor constraintEqualToAnchor:self->phidgetInfoView.topAnchor],
        [infoBoxView.bottomAnchor constraintEqualToAnchor:self->phidgetInfoView.bottomAnchor],
        [infoBoxView.leadingAnchor constraintEqualToAnchor:self->phidgetInfoView.leadingAnchor],
        [infoBoxView.trailingAnchor constraintEqualToAnchor:self->phidgetInfoView.trailingAnchor]
    ]];
    
    
    [self.infoBoxVC fillPhidgetInfo:nil];
    [phidgetInfoBox setHidden:NO];
    
    
    result = PhidgetLEDArray_create(&ch);
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
    
    result = [self initChannel:(PhidgetHandle)ch];
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
    
    
    [PhidgetInfoBoxN openCmdLine:(PhidgetHandle)ch];
    
    

}

-(PhidgetReturnCode)initChannel:(PhidgetHandle) channel{
    PhidgetReturnCode result;
    
    result = Phidget_setOnAttachHandler(channel, gotAttach, (__bridge void*)self);
    if(result != EPHIDGET_OK){
        return result;
    }
    
    result = Phidget_setOnDetachHandler(channel, gotDetach, (__bridge void*)self);
    if(result != EPHIDGET_OK){
        return result;
    }
    
    result = Phidget_setOnErrorHandler(channel, gotError, (__bridge void*)self);
    if(result != EPHIDGET_OK){
        return result;
    }
    return EPHIDGET_OK;
}


#pragma mark Device Event Callbacks

static void gotAttach(PhidgetHandle phid, void *context){
    [(__bridge id)context performSelectorOnMainThread:@selector(onAttachHandler)
                                           withObject:nil
                                        waitUntilDone:NO];
}

static void gotDetach(PhidgetHandle phid, void *context){
    [(__bridge id)context performSelectorOnMainThread:@selector(onDetachHandler)
                                           withObject:nil
                                        waitUntilDone:NO];
}

static void gotError(PhidgetHandle phid, void *context, Phidget_ErrorEventCode errcode, const char *error){
    [(__bridge id)context performSelectorOnMainThread:@selector(errorHandler:)
                                           withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:errcode], [NSString stringWithUTF8String:error], nil]
                                        waitUntilDone:NO];
}


#pragma mark Attach, detach, and error events
- (void)onAttachHandler{
    NSWindow *window = self.view.window;
    NSRect newFrame = window.frame;
    newFrame.size = NSMakeSize(538, 625);
    [window setFrame:newFrame display:YES];
    
    Phidget_DeviceID deviceID;

    
    //Get information from channel which will allow us to configure the GUI properly
    Phidget_getDeviceID((PhidgetHandle)ch, &deviceID);
    
    //Adjust GUI based on information from channel
    [self.infoBoxVC fillPhidgetInfo:(PhidgetHandle)ch];
    [configBox setHidden:NO];
    configBox.translatesAutoresizingMaskIntoConstraints = YES;

    [colorOrderCmb selectItemAtIndex:1];
    [gammaTrk setDoubleValue:22]; //this seems to work well to emulate the colors of a standard pc monitor
    [gammaTxt setStringValue:@"2.2"];
    [brightnessTrk setDoubleValue:5]; //set it low initially because the leds tend to be really painfully bright.
}

- (void)onDetachHandler{
    //Taking items out of view
    configBox.hidden = YES;
    [self.infoBoxVC fillPhidgetInfo:nil];
    
    NSWindow *window = self.view.window;
    [window setContentSize:NSMakeSize(538, 125)];

}

static int errorCounter = 0;
-(void) outputError:(const char *)errorString{
    errorCounter++;
    NSAttributedString *outputString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n",[NSString stringWithUTF8String:errorString]]
        attributes:@{NSForegroundColorAttributeName: NSColor.controlTextColor}];
    [[errorEventLog textStorage] beginEditing];
    [[errorEventLog textStorage] appendAttributedString:outputString];
    [[errorEventLog textStorage] endEditing];
    
    [errorEventLogCounter setIntValue:errorCounter];
    /*if(![errorEventLogWindow isHidden])
        [errorEventLogWindow setHidden:YES];*/
    
    if (!self.errorEventLogWindow) {
        NSStoryboard *storyboard = [NSStoryboard storyboardWithName:@"Main" bundle:nil];
        self.errorEventLogWindow = [storyboard instantiateControllerWithIdentifier:@"errorEventLogWindow"];
    }

    [self.errorEventLogWindow showWindow:self];
}


-(void)errorHandler:(NSArray *)errorEventData{
    const char* errorString = [[errorEventData objectAtIndex:1] UTF8String];
    [self outputError:errorString];
}

-(void)outputLastError {
    const char *errorString;
    char *errorDetailString = NULL;
    size_t errorDetailStringLen = 0;
    PhidgetReturnCode lastErr;
    
    if (!Phidget_getLastError(&lastErr, &errorString, NULL, &errorDetailStringLen)) {
        errorDetailString = malloc(errorDetailStringLen);
        Phidget_getLastError(&lastErr, &errorString, errorDetailString, &errorDetailStringLen);
        [self outputError:errorDetailString];
        free(errorDetailString);
    }
}

- (IBAction)clearErrorLog:(id)sender{
    [[errorEventLog textStorage] setAttributedString:[[NSAttributedString alloc] initWithString:@""]];
    [errorEventLogCounter setIntValue:0];
    errorCounter = 0;
}


#pragma mark UI Events

- (IBAction)colorOrderComboChange:(id)sender {
    PhidgetReturnCode result = EPHIDGET_OK;
    
    if([colorOrderCmb.objectValueOfSelectedItem isEqualToString:@"RGB"])
        result = PhidgetLEDArray_setColorOrder(ch, LED_COLOR_ORDER_RGB);
    else if([colorOrderCmb.objectValueOfSelectedItem isEqualToString:@"GRB"])
        result = PhidgetLEDArray_setColorOrder(ch, LED_COLOR_ORDER_GRB);
    else if([colorOrderCmb.objectValueOfSelectedItem isEqualToString:@"RGBW"])
        result = PhidgetLEDArray_setColorOrder(ch, LED_COLOR_ORDER_RGBW);
    else if([colorOrderCmb.objectValueOfSelectedItem isEqualToString:@"GRBW"])
        result = PhidgetLEDArray_setColorOrder(ch, LED_COLOR_ORDER_GRBW);
    
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
}

- (IBAction)gammaTrkChange:(id)sender {
    PhidgetReturnCode result = EPHIDGET_OK;
    result = PhidgetLEDArray_setGamma(ch, [gammaTrk doubleValue]/10.0);
    NSString *temp = [NSString stringWithFormat:@"%.1f", [gammaTrk doubleValue]/10.0];
    [gammaTxt setStringValue:temp];
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
}
- (IBAction)powerBtnClick:(id)sender {
    PhidgetReturnCode result = EPHIDGET_OK;
    if([[powerBtn title] isEqualToString:@"Power Off"]){
        result = PhidgetLEDArray_setPowerEnabled(ch, TRUE);
        [powerBtn setTitle:@"Power On"];
    }
    else if([[powerBtn title] isEqualToString:@"Power On"]){
        result = PhidgetLEDArray_setPowerEnabled(ch, FALSE);
        [powerBtn setTitle:@"Power Off"];
    }
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
}
- (IBAction)clearBtnClick:(id)sender {
    PhidgetReturnCode result = EPHIDGET_OK;
    result = PhidgetLEDArray_clearLEDs(ch);
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
}
- (IBAction)brightnessTrkChange:(id)sender {
    PhidgetReturnCode result = EPHIDGET_OK;
    result = PhidgetLEDArray_setBrightness(ch, [brightnessTrk doubleValue]/100);
}

- (void)resizeWindowToContentSize:(NSSize)size {
    NSWindow *window = self.view.window;
    if (!window) return;

    NSRect frame = window.frame;
    NSRect contentFrame = window.contentLayoutRect;

    CGFloat deltaHeight = size.height - contentFrame.size.height;
    CGFloat deltaWidth  = size.width  - contentFrame.size.width;

    frame.origin.y -= deltaHeight; // keep top-left anchored
    frame.size.height += deltaHeight;
    frame.size.width += deltaWidth;

    [window setFrame:frame display:YES animate:YES];
}

- (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
    NSLog(@"Segue identifier: %@", segue.identifier);

    if ([segue.destinationController isKindOfClass:[NSTabViewController class]]) {
        NSTabViewController *tabVC = segue.destinationController;
        // Save reference for later
        self.tabViewController = tabVC;

        // Set up delegates on child tabs
        basicTabViewController *basic = (basicTabViewController *)tabVC.childViewControllers[0];
        basic.delegate = self;  // <-- THIS IS KEY
        basic.resizeDelegate = self;

        advancedTabViewController *adv = (advancedTabViewController *)tabVC.childViewControllers[1];
        adv.delegate = self;
        adv.resizeDelegate = self;
    }
}

#pragma mark - BasicTabViewControllerDelegate

- (void)basicTabViewController:(basicTabViewController *)controller
                didSelectColor:(NSColor *)color
                      integers:(NSArray<NSNumber *> *)integers
{
    // 1. Grab the color
    NSColor *rgbColor = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
    CGFloat redF, greenF, blueF, alphaF;
    [rgbColor getRed:&redF green:&greenF blue:&blueF alpha:&alphaF];
    int red = (int)(redF * 255);
    int green = (int)(greenF * 255);
    int blue = (int)(blueF * 255);

    PhidgetLEDArray_Color toSet = {red, green, blue, 0};
    size_t toSetLen = sizeof(toSet);

    // 2. Grab the integers
    NSInteger startAddress = integers.count > 0 ? integers[0].integerValue : 0;
    NSInteger endAddress   = integers.count > 1 ? integers[1].integerValue : 0;
    
    if(endAddress<startAddress)
        endAddress = startAddress;
    
    NSInteger numLEDs = endAddress-startAddress+1;
    PhidgetLEDArray_Color *fullStrand = malloc(sizeof(PhidgetLEDArray_Color) * numLEDs);

    for(int i = 0;i<numLEDs;i++){
        fullStrand[i].r = red;
        fullStrand[i].g = green;
        fullStrand[i].b = blue;
        //fullStrand[0].w = white;
    }
    
    PhidgetReturnCode result = EPHIDGET_OK;
    result = PhidgetLEDArray_setLEDs(ch, startAddress, endAddress, fullStrand, numLEDs, 0);
    if(result != EPHIDGET_OK){
        [self outputLastError];
    }
    
    free(fullStrand);
}

#pragma mark - AdvancedTabViewControllerDelegate
- (void)advancedTab:(advancedTabViewController *)controller
       didSendColors:(NSArray<ledColorEntryRow *> *)entries
        startAddress:(NSInteger)startAddress
          endAddress:(NSInteger)endAddress
             fadeTime:(NSInteger)fadeTime
             ledCount:(NSInteger)ledCount
{
    NSLog(@"Received %lu colors from %ld to %ld with fadeTime %ld",
          (unsigned long)entries.count, (long)startAddress, (long)endAddress, (long)fadeTime);

    PhidgetLEDArray_Color *fullStrand =
        malloc(sizeof(PhidgetLEDArray_Color) * ledCount);

    for (NSInteger i = 0; i < ledCount; i++) {
        fullStrand[i] = (PhidgetLEDArray_Color){0,0,0,0};
    }

    for (ledColorEntryRow *entry in entries) {
        if (entry.ledIndex < startAddress || entry.ledIndex > endAddress) continue;

        NSColor *rgb = [entry.color colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace];
        CGFloat r,g,b,a;
        [rgb getRed:&r green:&g blue:&b alpha:&a];

        NSInteger offset = entry.ledIndex - startAddress;
        fullStrand[offset] = (PhidgetLEDArray_Color){
            (int)(r * 255),
            (int)(g * 255),
            (int)(b * 255),
            0
        };
    }

    // send to hardware (Phidget API supports fade time as last argument if desired)
    PhidgetReturnCode result =
        PhidgetLEDArray_setLEDs(ch, startAddress, endAddress, fullStrand, ledCount, fadeTime);

    free(fullStrand);

    if (result != EPHIDGET_OK) {
        [self outputLastError];
    }
}

@end
