//
//  AppDelegate.m
//  stitchUI
//
//  Created by Xin Zhang on 10/24/21.
//

#import "AppDelegate.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <math.h>

#define detectorXSize 487
#define detectorYSize 619
#define detectorPixelSize 0.172

#define NumberLimit 1024

typedef struct EDF{
double detX;
double detZ;
int detectorCount;
double countTime;
int data[detectorXSize][detectorYSize];
} EDFData;

int readLine(FILE *file, char* lineBuffer) {

    if (file == NULL) {
        printf("Error: file pointer is null.\n");
        return 1;
    }

    int maximumLineLength = NumberLimit;
    //char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error: line buffer Error\n");
        return 1;
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
                printf("Error: buffer full\n");
                return 1;
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }
    
    lineBuffer[count] = '\0';
    if(ch == EOF ) return 2;
    return 0;
}

int readEDF(char *fileName, EDFData ** dataChain, unsigned short quietMode){
    char lineBuffer [NumberLimit];
    int success;
    int detectorWidth = 0;
    int detectorHeight = 0;
    int detectorCount = 0;
    double detectorPosX = -1;
    double detectorPosZ = -1;
    double countTime = -1;
    int fileSize;
    int headerSize = 0;

    EDFData * currentData = 0;

    FILE * dataFile = fopen(fileName, "r");
    if(NULL == dataFile){
            printf("Error opening file: %s", fileName);
            return 1;
    }
    
    for(int i = 0; i < NumberLimit ; i ++){

        readLine( dataFile, lineBuffer );
        
        if(lineBuffer[0] == '}') {
            //printf("break\n");
            break;
        }
        success = sscanf(lineBuffer,"Detector = %d",     &detectorCount);
        if( success > 0 ) {
            if(quietMode == NO ) printf("......detectorCount = %d\n",detectorCount);
            //return detectorCount;
        }

        //    Dim_1          = 487 ;
        success = sscanf(lineBuffer,"Dim_1          = %d ",     &detectorWidth);
        //if( success > 0 ) printf("detectorWidth = %d\n",detectorWidth);

        success = sscanf(lineBuffer,"Dim_2          = %d ",     &detectorHeight);
        //if( success > 0 ) printf("detectorHeight = %d\n",detectorHeight);

        success = sscanf(lineBuffer,"detx = %lf ",     &detectorPosX);
        //if( success > 0 ) printf("detectorPosX = %lf\n",detectorPosX);

        success = sscanf(lineBuffer,"detz = %lf ",     &detectorPosZ);
        //if( success > 0 ) printf("detectorPosZ = %lf\n",detectorPosZ);

        success = sscanf(lineBuffer,"EDF_HeaderSize = %d ",     &headerSize);
        //if( success > 0 ) printf("headerSize = %d\n",headerSize);

        success = sscanf(lineBuffer,"EDF_BinarySize = %d ",     &fileSize);
        //if( success > 0 ) printf("fileSize = %d\n",fileSize);

        success = sscanf(lineBuffer,"count_time     = %lf ",     &countTime);
        //if( success > 0 ) printf("count_time = %lf\n",countTime);
    }//for
    int mode = 0;
    for(int i = 0; i < NumberLimit ; i ++){
        if( dataChain[i] == 0) {
            currentData = (EDFData*)malloc(sizeof(EDFData));
            if( NULL == currentData){
                fclose(dataFile);
                printf("Error: could not allocate memory!\n");
                return 1;
            }
            //memset(currentData->data, 1, sizeof(currentData->data));

            dataChain[i] = currentData;
            currentData -> detX = detectorPosX;
            currentData -> detZ = detectorPosZ;
            currentData -> detectorCount = detectorCount;
            currentData -> countTime = countTime;
            //printf("new file\n");
            break;
        }
        if( dataChain[i]-> detX == detectorPosX && dataChain[i]-> detZ == detectorPosZ){
            currentData = dataChain[i];
            mode = 1;
            currentData -> detectorCount += detectorCount;
            currentData -> countTime += countTime;
            if(quietMode == NO ) printf("DetectorCount= %d\n", currentData -> detectorCount);

            break;
        }
    }
    //printf("...%d vs. %d\n", detectorWidth * detectorHeight * 4, fileSize);
    //fileSize =  detectorWidth * detectorHeight * 4;
    fseek( dataFile, headerSize , SEEK_SET );
    if(quietMode == NO ) printf("Header size: %d\n", headerSize);
    int n = 0;
    for(int i = headerSize; i < headerSize + detectorXSize * detectorYSize * 4 ; i +=4){
        char ch0 = getc(dataFile);
        char ch1 = getc(dataFile);
        char ch2 = getc(dataFile);
        char ch3 = getc(dataFile);

         int value =     ((unsigned int)ch0&0x000000ff) |
                (((unsigned int)ch1 <<  8)&0x0000ff00) |
                (((unsigned int)ch2 << 16)&0x00ff0000) |
                (((unsigned int)ch3 << 24)&0xff000000);
        if(value < 0 ) { value = 0 ;}
        int y = n/detectorXSize;
        int x = n%detectorXSize;
        n ++;
        if(mode == 0 ){
            currentData ->data[x][detectorYSize-y-1] = value;
        }else{
            currentData ->data[x][detectorYSize-y-1] += value;
        }
    }
    fclose(dataFile);

    return 0;
}

NSSize stitch(NSString *input, NSMutableString *output, bool transCorrection){
    unsigned short doTransmission = transCorrection;
    unsigned short quietMode = NO;
    //char * fileName = "output.asc";

    EDFData * dataArray[NumberLimit] = {0};
    __block EDFData ** dataArrayPointer = dataArray;

      // Pointer for directory entry
  
    DIR *dr = opendir([input UTF8String]);
        //printf("%s\n",[dir cStringUsingEncoding:typeUTF8Text]);
    if (dr == NULL){
        printf("Failed to open directory" );
        return NSMakeSize(0,0);
    }
    struct dirent **fileListTemp;
    int noOfFiles = scandir([input UTF8String], &fileListTemp, NULL, alphasort);
    __block int nOfEDF = 0;
    
    dispatch_semaphore_t changeCount = dispatch_semaphore_create(1);
    dispatch_queue_t loader = dispatch_queue_create("loader", NULL);
    
    for(int n =2; n < noOfFiles; n ++){
        
        dispatch_sync(loader, ^{
        struct dirent *de = fileListTemp[n];
        
        if( 0 != strstr( de->d_name, ".edf")){
            NSString * fullName = [input stringByAppendingFormat:@"/%s", de->d_name];
            if(quietMode == NO ) NSLog(@"processing: %@\n", fullName);
            dispatch_semaphore_wait(changeCount, DISPATCH_TIME_FOREVER);
            readEDF((char *)[fullName UTF8String], dataArrayPointer, quietMode);
            nOfEDF ++;
            dispatch_semaphore_signal(changeCount);
        }
        });
    }
    if(nOfEDF == 0){
        NSLog(@"no EDF");
        return NSMakeSize(0,0);
    }
  
    closedir(dr);
    
    double lowX = 10000.0;
    double highX = -100000.0;
    double lowY = 10000.0;
    double highY = -10000.0;
    int maxCount = 0;

    for( int i = 0; i < NumberLimit ; i ++){
        if(dataArray[i] == 0 )     break;
        double detectorX = dataArray[i]->detX;
        double detectorY = dataArray[i]->detZ;

        if(detectorX < lowX)  lowX = detectorX;
        if(detectorX > highX) highX = detectorX;
        if(detectorY < lowY)  lowY = detectorY;
        if(detectorY > highY) highY = detectorY;

        if(dataArray[i]->detectorCount > maxCount) {
            maxCount = dataArray[i]->detectorCount;
            //printf("max DetectorCount= %d\n", maxCount);
        }
    }
    double detectorXShift = highX - lowX;
    double detectorYShift = highY - lowY;
    if(quietMode == NO ) printf("xshift=%f, detectorXsize %i, yshift=%f, detectorYSize %i\n", detectorXShift, detectorXSize, detectorYShift,  detectorYSize);

    unsigned int sizeX = (int)round(detectorXShift/detectorPixelSize) + detectorXSize;
    unsigned int sizeY = (int)round(detectorYShift/detectorPixelSize) + detectorYSize;

    if(quietMode == NO ) printf("size x: %d; size y: %d\n", sizeX, sizeY);

    int image[sizeX][sizeY];

    memset(image, 0, sizeof(image));

    for( int i = 0; i < NumberLimit ; i ++){
        
        if(dataArray[i] == 0 )     break;

        double transmission = (double)(dataArray[i]->detectorCount)/(double)maxCount;
        //printf("transmission %lf\n", transmission);
        dataArray[i]->detX -= lowX;
        dataArray[i]->detZ -= lowY;
           int xOffset = (int)round(dataArray[i]->detX/detectorPixelSize) ;
            int yOffset = (int)round(dataArray[i]->detZ/detectorPixelSize) ;

        //for(int yIndex = detectorYSize - 1; yIndex > -1; yIndex --){
        for(int yIndex = 0; yIndex <detectorYSize; yIndex ++){
            for(int xIndex = 0; xIndex < detectorXSize; xIndex ++){
                    //if( 1 > image [xIndex + xOffset] [yIndex + yOffset]) {
                        if(yIndex < 195 || yIndex >424 || (yIndex > 213 && yIndex < 407)){
                            if(YES == doTransmission) image [xIndex + xOffset] [yIndex + yOffset]
                                = (int)((double)(dataArray[i]->data[xIndex][yIndex])/transmission);
                            else image [xIndex + xOffset] [yIndex + yOffset] = dataArray[i]->data[xIndex][yIndex]  ;
                        }
            }
        }
    }
    
    printf("\n");
    //NSString * outputName = [dir stringByAppendingFormat:@"/%s", "output.txt"];
    //FILE * dataFile = fopen([outputName UTF8String], "w");
    //if( NULL == dataFile){
    //    printf("Error: file name:%s", [outputName UTF8String]);
    //    return 0;
    //}
    
    //FILE * dataFile = fopen(fileName, "w");
    //fprintf(dataFile, "(DAS)^2 2-D text output of image region:\n");
    [output appendString:@"(DAS)^2 2-D text output of image region:\n"];
    //fprintf(dataFile, "Number of pixels in X direction =%8d\n", sizeX);
    [output appendFormat:@"Number of pixels in X direction =%8d\n", sizeX];
    //fprintf(dataFile, "Number of pixels in Y direction =%8d\n", sizeY);
    [output appendFormat:@"Number of pixels in Y direction =%8d\n", sizeY];
    //fprintf(dataFile, "X starting pixel of ROI =          1\n");
    [output appendString:@"X starting pixel of ROI =          1\n"];
    //fprintf(dataFile, "Y starting pixel of ROI =          1\n");
    [output appendString:@"Y starting pixel of ROI =          1\n"];
    //fprintf(dataFile, "ROI pixel values follow (X-direction changing fastest, bottom left first):\n");
    [output appendString:@"ROI pixel values follow (X-direction changing fastest, bottom left first):\n"];
    int index = 0;
    for( int y = sizeY -1 ; y >= 0; y -- ){
    for( int x = 0; x < sizeX; x ++){
        //fprintf(dataFile,"%8d", image[x][y]);
        //printf("%8d", image[x][y]);
        [output appendFormat:@"%8d", image[x][y]];
        if( ++index == 10){
            index = 0;
            //fprintf(dataFile, "\n");
            [output appendString:@"\n"];
        }
    }
    }

    //fclose(dataFile);
    printf("Stiching edf files together. V2.0 with GUI. zhangx@umd.edu copyleft 2021\n");

    return NSMakeSize((CGFloat)sizeX,(CGFloat)sizeY);
}


@interface AppDelegate ()

@property (strong) IBOutlet NSWindow *window;
@end

@implementation AppDelegate
@synthesize bTransmissionCorrect;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    bTransmissionCorrect = false;
}


- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}


- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
    return YES;
}

- (IBAction)openFunction:(id)sender{
    //printf("open\n");
    NSOpenPanel* openDlg = [NSOpenPanel openPanel];
    openDlg.title = @"Select a directory containing EDF files, this program will combine them into a 2D ASCII data file";
    openDlg.prompt = @"convert EDFs in a folder to 2D ASCII";
    [openDlg setCanChooseFiles:NO]; //no files selection

    [openDlg setAllowsMultipleSelection:NO]; //one directory only

    [openDlg setCanChooseDirectories:YES]; //can select directory

    // Display the dialog. If the OK button was pressed,
    // process the files.
    if ( [openDlg runModal] == NSModalResponseOK )
    {
        // Get an array containing the full filenames of all
        // files and directories selected.
        NSArray* dirNames = [openDlg URLs];
        //dispatch_queue_t que = dispatch_queue_create("que", NULL);
        // Loop through all the files and process them.
        for(int i = 0; i < [dirNames count]; i++ )
        {
            NSURL* url = [dirNames objectAtIndex:i];
            NSLog(@"path: %@", url.path);
            
            NSSavePanel *save = [NSSavePanel savePanel];
            [save setAllowedFileTypes:[NSArray arrayWithObject:@"asc"]];
            [save setAllowsOtherFileTypes:NO];
            NSInteger result = [save runModal];

            if (result == NSModalResponseOK)
            {
                NSString *selectedFile = [[save URL] path];
                
                NSMutableString *output = [NSMutableString stringWithCapacity:1024*1024];
                NSSize size = stitch(url.path, output, bTransmissionCorrect);
                
                 NSError *error = nil;
                 [output writeToFile:selectedFile
                                 atomically:NO
                                   encoding:NSUTF8StringEncoding
                                      error:&error];
                if (error) {
                    [NSApp presentError:error];
                }else{
                    NSString * info = [NSString stringWithFormat:@"data with Width=%d; Height=%d saved into %@", (int)size.width, (int)size.height, selectedFile];
                    NSAlert * alert = [[NSAlert alloc] init];
                    alert.alertStyle = NSAlertStyleInformational;
                    alert.informativeText = info;
                    [alert runModal];
                }
            }
            
        }
    }
}

-(IBAction)TransmissionCorrectChanged:(id)sender{
    NSSwitch * switchCorrect = (NSSwitch*) sender;
    if(switchCorrect.state == NSControlStateValueOn){
        bTransmissionCorrect = true;
    }else{
        bTransmissionCorrect = false;
        
    }
}

@end
