/* 
   Project: FTP

   Copyright (C) 2005 Riccardo Mottola

   Author: Riccardo Mottola

   Created: 2005-03-30
   
   Application Controller

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
 
   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
 
   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#import "AppController.h"
#import "fileElement.h"

@implementation AppController

+ (void)initialize
{
    NSMutableDictionary *defaults = [NSMutableDictionary dictionary];

  /*
   * Register your app's defaults here by adding objects to the
   * dictionary, eg
   *
   * [defaults setObject:anObject forKey:keyForThatObject];
   *
   */

    [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (id)init
{
    if ((self = [super init]))
    {
    }
    connMode = defaultMode;
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

- (void)awakeFromNib
{
//  [[NSApp mainMenu] setTitle:@"FTP"];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotif
{
    NSArray        *dirList;
    NSUserDefaults *defaults;
    NSString       *readValue;

    /* read the user preferences */
    defaults = [NSUserDefaults standardUserDefaults];
    readValue = [defaults stringForKey:connectionModeKey];

    // if no value was set for the key we set default as mode
    if ([readValue isEqualToString:@"default"] || readValue == nil)
        connMode = defaultMode;
    else if ([readValue isEqualToString:@"port"])
        connMode = portMode;
    else if ([readValue isEqualToString:@"passive"])
        connMode = passiveMode;
    else
        NSLog(@"Unrecognized value in user preferences for %@: %@", connectionModeKey, readValue);
    NSLog(@"%@ maps to %d", connectionModeKey, connMode);
    
    /* set double actions for tables */
    [localView setTarget:self];
    [localView setDoubleAction:@selector(listDoubleClick:)];
    [remoteView setTarget:self];
    [remoteView setDoubleAction:@selector(listDoubleClick:)];
    
    /* startup code */
    local = [[localclient alloc] init];
    [local setWorkingDir:[local homeDir]];
    dirList = [local dirContents];
    [progBar setDoubleValue:0.0];  // reset the progress bar
    
    /* we create a data source and set the tableviews */
    localTableData = [[fileTable alloc] init];
    [localTableData initData:dirList];
    [localView setDataSource:localTableData];
    
    remoteTableData = [[fileTable alloc] init];

    /* we update the path menu */
    [self updatePath :localPath :[local workDirSplit]];
// #### and a release of this array ?!?
}

- (BOOL)applicationShouldTerminate:(id)sender
{
  return YES;
}

- (void)applicationWillTerminate:(NSNotification *)aNotif
{
}

- (BOOL)application:(NSApplication *)application openFile:(NSString *)fileName
{
    return NO;
}

/* update the pop-up menu with a new path */
- (void)updatePath :(NSPopUpButton *)path :(NSArray *)pathArray
{
    [path removeAllItems];
    [path addItemsWithTitles:pathArray];
}

/* performs the action of the path pull-down menu
   it navigates upwards the tree
   and works for both the local and remote path */
- (IBAction)changePathFromMenu:(id)sender
{
    client      *theClient;
    NSTableView *theView;
    fileTable   *theTable;
    NSString    *thePath;
    NSArray     *items;
    int         selectedIndex;
    int         i;
    NSArray    *dirList;

    NSLog(@"%@", [sender class]);
    if (sender == localPath)
    {
        theClient = local;
        theView = localView;
        theTable = localTableData;
    } else
    {
        theClient = ftp;
        theView = remoteView;
        theTable = remoteTableData;
    }
    thePath = [NSString string];
    selectedIndex = [sender indexOfItem:[sender selectedItem]];
    items = [sender itemTitles];
    for (i = [items count] - 1; i >= selectedIndex; i--)
        thePath = [thePath stringByAppendingPathComponent: [items objectAtIndex:i]];
    NSLog(@"selected path: %@", thePath);
    [theClient changeWorkingDir:thePath];
    if ((dirList = [theClient dirContents]) == nil)
        return;
    [theTable initData:dirList];
    [theView reloadData];
    
    [self updatePath :sender :[theClient workDirSplit]];
}

/* perform the action of a double click in a table element
   a directory should be opened, a file down or uploaded
   The same method works for local and remote, detecting them */
- (IBAction)listDoubleClick:(id)sender
{
    client        *theClient;
    NSTableView   *theView;
    fileTable     *theTable;
    int           elementIndex;
    fileElement   *fileEl;
    NSString      *thePath;
    NSArray       *dirList;
    NSPopUpButton *thePathMenu;

    theView = sender;
    NSLog(@"%@", [theView class]);
    if (theView == localView)
    {
        theClient = local;
        theTable = localTableData;
        thePathMenu = localPath;
    } else
    {
        theClient = ftp;
        theTable = remoteTableData;
        thePathMenu = remotePath;
    }

    elementIndex = [sender selectedRow];
    if (elementIndex < 0)
    {
        NSLog(@"error: double click with nothing selected");
        return;
    }
    fileEl = [theTable elementAtIndex:elementIndex];
    NSLog(@"element: %@ %d", [fileEl filename], [fileEl isDir]);
    thePath = [NSString stringWithString:[theClient workingDir]];
    thePath = [thePath stringByAppendingPathComponent: [fileEl filename]];
    if ([fileEl isDir])
    {
        NSLog(@"should cd to %@", thePath);
        [theClient changeWorkingDir:thePath];
        if ((dirList = [theClient dirContents]) == nil)
            return;
        [theTable initData:dirList];
        [theView reloadData];
        [self updatePath :thePathMenu :[theClient workDirSplit]];
    } else
    {
        if (theView == localView)
        {
            NSLog(@"should upload %@", thePath);
            [ftp storeFile:fileEl from:local beingAt:0];
        } else
        {
            NSLog(@"should download %@", thePath);
            [ftp retrieveFile:fileEl to:local beingAt:0];
        }
    }
}

- (IBAction)downloadButton:(id)sender
{
    NSEnumerator  *elemEnum;
    fileElement   *fileEl;
    id            currEl;

    elemEnum = [remoteView selectedRowEnumerator];

    while ((currEl = [elemEnum nextObject]) != nil)
    {
        fileEl = [remoteTableData elementAtIndex:[currEl intValue]];
        NSLog(@"should download: %@", [fileEl filename]);
        [ftp retrieveFile:fileEl to:local beingAt:0];
    }
}

- (IBAction)uploadButton:(id)sender
{
    NSEnumerator  *elemEnum;
    fileElement   *fileEl;
    id            currEl;

    elemEnum = [localView selectedRowEnumerator];

    while ((currEl = [elemEnum nextObject]) != nil)
    {
        fileEl = [localTableData elementAtIndex:[currEl intValue]];
        [ftp storeFile:fileEl from:local beingAt:0];
    }
}

- (IBAction)localDelete:(id)sender
{
    NSEnumerator  *elemEnum;
    fileElement   *fileEl;
    id            currEl;

    elemEnum = [localView selectedRowEnumerator];
    
    while ((currEl = [elemEnum nextObject]) != nil)
    {
        fileEl = [localTableData elementAtIndex:[currEl intValue]];
        [local deleteFile:fileEl beingAt:0];
    }
}

- (IBAction)remoteDelete:(id)sender
{
    NSEnumerator  *elemEnum;
    fileElement   *fileEl;
    id            currEl;    

    elemEnum = [remoteView selectedRowEnumerator];

    while ((currEl = [elemEnum nextObject]) != nil)
    {
        fileEl = [remoteTableData elementAtIndex:[currEl intValue]];
        [ftp deleteFile:fileEl beingAt:0];
    }
}

- (void)setTransferBegin:(NSString *)name :(unsigned long long)size
{
    [infoMessage setStringValue:name];
    [progBar setDoubleValue:0];
    gettimeofday(&beginTimeVal, NULL);
    transferSize = size;
    [mainWin displayIfNeeded];
    [mainWin flushWindowIfNeeded];
}

- (void)setTransferProgress:(unsigned long)bytes
{
    struct timeval currTimeVal;
    float    speed;
    NSString *speedStr;
    NSString *sizeStr;
    double   percent;

    gettimeofday(&currTimeVal, NULL);

    if ((currTimeVal.tv_sec - lastTimeVal.tv_sec) > 1.0)
    {
        speed = (float)((double)bytes / (double)(currTimeVal.tv_sec - beginTimeVal.tv_sec));
        percent = ((double)bytes / (double)transferSize) * 100;
        lastTimeVal = currTimeVal;

        [progBar setDoubleValue:percent];
        speedStr = [NSString alloc];
        if (speed < 1024)
            speedStr = [speedStr initWithFormat:@"%3.2fB/s", speed];
        else if (speed < 1024*1024)
            speedStr = [speedStr initWithFormat:@"%3.2fKB/s", speed/1024];
        else
            speedStr = [speedStr initWithFormat:@"%3.2fMB/s", speed/(1024*1024)];
        [infoSpeed setStringValue:speedStr];
        [speedStr release];

        sizeStr = [NSString alloc];
        if (transferSize < 1024)
            sizeStr = [sizeStr initWithFormat:@"%3.2f: %3.2 fB", (float)bytes, (float)transferSize];
        else if (transferSize < 1024*1024)
            sizeStr = [sizeStr initWithFormat:@"%3.2f %3.2f KB", (double)bytes/1024, (double)transferSize/1024];
        else
            sizeStr = [sizeStr initWithFormat:@"%3.2f %3.2f MB", (double)bytes/(1024*1024), (double)transferSize/(1024*1024)];
        [infoSize setStringValue:sizeStr];
        [sizeStr release];
        [mainWin displayIfNeeded];
        [mainWin flushWindowIfNeeded];
    }
}

- (void)setTransferEnd:(unsigned long)bytes
{
    struct timeval currTimeVal;
    double         deltaT;
    float          speed;
    NSString       *speedStr;
    NSString       *sizeStr;
    double         percent;

    gettimeofday(&currTimeVal, NULL);
    deltaT = (currTimeVal.tv_sec - beginTimeVal.tv_sec)+((double)(currTimeVal.tv_usec - beginTimeVal.tv_usec)/1000000);
    speed = (float)((double)bytes / deltaT);
    NSLog(@"Elapsed time: %f", (float)deltaT);
    percent = ((double)bytes / (double)transferSize) * 100;
    speedStr = [NSString alloc];
    if (speed < 1024)
        speedStr = [speedStr initWithFormat:@"%3.2fB/s", speed];
    else if (speed < 1024*1024)
        speedStr = [speedStr initWithFormat:@"%3.2fKB/s", speed/1024];
    else
        speedStr = [speedStr initWithFormat:@"%3.2fMB/s", speed/(1024*1024)];
    [infoSpeed setStringValue:speedStr];
    [speedStr release];

    sizeStr = [NSString alloc];
    if (transferSize < 1024)
        sizeStr = [sizeStr initWithFormat:@"%3.2f: %3.2 fB", (float)bytes, (float)transferSize];
    else if (transferSize < 1024*1024)
        sizeStr = [sizeStr initWithFormat:@"%3.2f %3.2f KB", (double)bytes/1024, (double)transferSize/1024];
    else
        sizeStr = [sizeStr initWithFormat:@"%3.2f %3.2f MB", (double)bytes/(1024*1024), (double)transferSize/(1024*1024)];
    [infoSize setStringValue:sizeStr];
    [sizeStr release];
    
    [progBar setDoubleValue:percent];
    [mainWin displayIfNeeded];
    [mainWin flushWindowIfNeeded];
}

- (IBAction)disconnect:(id)sender
{
    [ftp disconnect];
}

- (IBAction)showPrefPanel:(id)sender
{
    [prefPanel makeKeyAndOrderFront:self];
    switch (connMode)
    {
        case defaultMode:
            [portType selectCellWithTag:0];
            break;
        case portMode:
            [portType selectCellWithTag:1];
            break;
        case passiveMode:
            [portType selectCellWithTag:2];
            break;
        default:
            NSLog(@"Unexpected mode on pref pane setup.");
    }
}

- (IBAction)prefSave:(id)sender
{
    NSUserDefaults *defaults;

    defaults = [NSUserDefaults standardUserDefaults];
    
    NSLog(@"tag... %d", [[portType selectedCell] tag]);
    switch ([[portType selectedCell] tag])
    {
        case 0:
            //default
            NSLog(@"default");
            connMode = defaultMode;
            [ftp setPortDefault];
            [defaults setObject:@"default" forKey:connectionModeKey];
            break;
        case 1:
            //port
            NSLog(@"port");
            connMode = portMode;
            [ftp setPortPort];
            [defaults setObject:@"port" forKey:connectionModeKey];
            break;
        case 2:
            // passive
            NSLog(@"passive");
            connMode = passiveMode;
            [ftp setPortPassive];
            [defaults setObject:@"passive" forKey:connectionModeKey];
            break;
        default:
            NSLog(@"unexpected selection");
    }
    [prefPanel performClose:nil];
}

- (IBAction)prefCancel:(id)sender
{
    [prefPanel performClose:nil];
}

- (IBAction)showFtpLog:(id)sender
{
    [logWin makeKeyAndOrderFront:self];
}

/*
 This routine is called after adding new results to the text view's backing store.
 We now need to scroll the NSScrollView in which the NSTextView sits to the part
 that we just added at the end
 */
- (void)scrollToVisible:(id)ignore {
    [logTextField scrollRangeToVisible:NSMakeRange([[logTextField string] length], 0)];
}

- (IBAction)appendTextToLog:(NSString *)textChunk
{
    /* add the textChunk to the NSTextView's backing store as an attributed string */
    [[logTextField textStorage] appendAttributedString: [[[NSAttributedString alloc]
                             initWithString: textChunk] autorelease]];

    /* setup a selector to be called the next time through the event loop to scroll
       the view to the just pasted text.  We don't want to scroll right now,
       because of a bug in Mac OS X version 10.1 that causes scrolling in the context
       of a text storage update to starve the app of events */
    [self performSelector:@selector(scrollToVisible:) withObject:nil afterDelay:0.0];

}

/* --- connection panel methods --- */
- (IBAction)showConnPanel:(id)sender
{
    [connectPanel makeKeyAndOrderFront:self];
}

- (IBAction)connectConn:(id)sender
{
    NSArray *dirList;
    char    tempStr[1024];
    char    tempStr2[1024];
    
    [connectPanel performClose:nil];
    ftp = [[ftpclient alloc] initWithController:self :connMode];
    [[connAddress stringValue] getCString:tempStr];
    if ([ftp connect:[connPort intValue] :tempStr] < 0)
    {
        NSRunAlertPanel(@"Error", @"Connection failed.\nCheck that you typed the host name correctly.", @"Ok", nil, nil);
        NSLog(@"connection failed in connectConn");
        return;
    }
    if ([connAnon state] == NSOnState)
    {
        strcpy(tempStr, "anonymous");
        strcpy(tempStr2, "user@myhost.com");
    } else
    {
        [[connUser stringValue] getCString:tempStr];
        [[connPass stringValue] getCString:tempStr2];
    }
    if ([ftp authenticate:tempStr :tempStr2] < 0)
    {
        NSRunAlertPanel(@"Error", @"Authentication failed.\nCheck that your username and password are correct.", @"Ok", nil, nil);
        NSLog(@"authentication failed.");
        return;
    } else
    {
        [ftp setWorkingDir:[ftp homeDir]];
        if ((dirList = [ftp dirContents]) == nil)
        return;
        [remoteTableData initData:dirList];
        [remoteView setDataSource:remoteTableData];

        //we update the path menu
        [self updatePath :remotePath :[ftp workDirSplit]];
    }
}

- (IBAction)cancelConn:(id)sender
{
    [connectPanel performClose:nil];
}

- (IBAction)anonymousConn:(id)sender
{
    if ([connAnon state] == NSOnState)
    {
        [connUser setEnabled:NO];
        [connPass setEnabled:NO];
    } else
    {
        [connUser setEnabled:YES];
        [connPass setEnabled:YES];
    }
}

- (connectionModes)connectionMode
{
    return connMode;
}

@end
