/*
 File:       InputController.m

 Contains:   Source code for the Controller of the "Input" window, see also PopupInTable.m

 Written by: Eric Simenel

 Created:    May 1997

 Copyright:  (c)1997 by Apple Computer, Inc., all rights reserved.

 Change History (most recent first):

 You may incorporate this sample code into your applications without
 restriction, though the sample code has been provided "AS IS" and the
 responsibility for its operation is 100% yours.  However, what you are
 not permitted to do is to redistribute the source as "DSC Sample Code"
 after having made changes. If you're going to re-distribute the source,
 we require that you make it clear in the source that the code was
 descended from Apple Sample Code, but that you've made changes.
*/

#import "InputController.h"
#import "ComicsObj.h"
#import "PopupInTable.h"

@implementation InputController

// private helper method: update the UI when the data array has changed
- (void)_update
{
    // release the popup button
    [puit releasePub];
    // update the table view
    [inputView noteNumberOfRowsChanged];
    [inputView reloadData];
}

// accessors
- (NSMutableArray *)array { return array;}
- (id)inputView { return inputView;}
- (short *)curIshArray { return curIshArray;}
- (short)theBuyMonth { return theBuyMonth;}
- (short)theEditMonth { return theEditMonth;}
- (NSFont *)fontProp { return fontProp;}
- (NSFont *)fontNonProp { return fontNonProp;}
- (void)setTheBuyMonth:(short)b
{
    [selBuyMonth setIntValue:b];
    [self selectBuyMonth:selBuyMonth];
}
- (void)selectBuyMonth:(id)sender
{
    [curBuyMonth setStringValue:[CComics nsStrDate:(theBuyMonth = [sender intValue])]];
}
- (void)setTheEditMonth:(short)e
{
    [selEditMonth setIntValue:e];
    [self selectEditMonth:selEditMonth];
}
- (void)selectEditMonth:(id)sender
{
    short i;
    [curEditMonth setStringValue:[CComics nsStrDate:(theEditMonth = [sender intValue])]];
    for(i=1; i <= 6; i++)
      {
        NSCell *cell = [[inputView tableColumnWithIdentifier:[NSString stringWithFormat:@"M%d",i]] headerCell];
        if (((theEditMonth-1-6+i) % 12) == 0)
            [cell setStringValue:[NSString stringWithCString:gnums[(theEditMonth-1-6+i) / 12]]];
        else
            [cell setStringValue:[NSString stringWithCString:gmonths[(theEditMonth-1-6+i) % 12]]];
      }
    [self _update];
}

- (void)doClick:(id)sender
{
    short clickedRow, clickedColumn;
    clickedRow = [inputView clickedRow];
    clickedColumn = [inputView clickedColumn];
    // release the popup button
    [puit releasePub];
    // reselect the current edited row in case releasePub (line above) has deselected it
    [inputView selectRow:clickedRow byExtendingSelection:NO];
    // if the user has clicked in a column where a popup button should appear, then set it up
    if ((clickedColumn > 2) && (clickedColumn != 13) && (clickedColumn != 14)) [puit setUpPopup];
    // if the user has clicked in the "Issue" column then we may have to slide the Edit Month to show it
    if (clickedColumn == 14)
      {
        CTitle *thisTitle = [array objectAtIndex:clickedRow];
        short theIsh = curIshArray[clickedRow];
        short theEMonth = [[[thisTitle issues] objectAtIndex:[thisTitle findIssue:theIsh]] editMonth];
        // if this edit month (line above) is not in the visible area, slide the Edit Month to show it
        if ((theEMonth < theEditMonth-5) || (theEMonth > theEditMonth)) [self setTheEditMonth:theEMonth+3];
      }
}

- (void)selChanged
{
    short i;
    // get the right array of titles
    [comicsBase sortArray:array withBrand:brand withSeries:series withKind:kind withState:state withSort:sort];
    nbRows = [array count];
    // reset the local array which fills the "Issue" column with the latest issue for each title
    if (curIshArray) free(curIshArray);
    curIshArray = (short *)malloc(sizeof(short) * nbRows);
    for(i=0; i<nbRows; i++) curIshArray[i] = [[[[array objectAtIndex:i] issues] lastObject] issueNumber];

    [nbSelTitles setIntValue:nbRows];
    [self _update];
}
- (void)contentChanged
{
    // update the displayed static text boxes with appropriate values
    [nbIssues setIntValue:[comicsBase nbIssues]];
    [nbTitles setIntValue:[comicsBase nbTitles]];
    // mark the window as modified, so that the close box reflects this state and we'll know later if we have to
    // ask the user for saving changes
    [[inputView window] setDocumentEdited:YES];
    // send the notification so that other Controllers will know about a change.
    [[NSNotificationCenter defaultCenter] postNotificationName:ComicsDidChangeNotification object:self];
}

- (void)brandSelect:(id)sender
{
    short newBrand = [sender indexOfSelectedItem];
    if (newBrand != brand) { brand = newBrand; [self selChanged]; }
}
- (void)kindSelect:(id)sender
{
    short newKind = [sender indexOfSelectedItem];
    if (newKind != kind) { kind = newKind; [self selChanged]; }
}
- (void)seriesSelect:(id)sender
{
    short newSeries = [sender indexOfSelectedItem];
    if (newSeries != series) { series = newSeries; [self selChanged]; }
}
- (void)stateSelect:(id)sender
{
    short newState = [sender indexOfSelectedItem];
    if (newState != state) { state = newState; [self selChanged]; }
}
- (void)sortSelect:(id)sender
{
    short newSort = [sender indexOfSelectedItem];
    if (newSort != sort) { sort = newSort; [self selChanged]; }
}

- (void)addNewTitle:(id)sender
{
    CTitle *thisTitle;
    CIssue *thisIssue;
    short flag = 0;
    flag |= (brand == 2)?mskDC:mskMarvel;
    flag |= (series == 2)?mskMini:mskLong;
    flag |= (state == 1)?mskDead:mskLive;
    flag |= (kind == 2)?mskDual:mskMain;
    thisTitle = [[CTitle alloc] initWithAbb:@"ZZZ" withTitle:@"Zzzzzzzzzz" withFlag:flag];
    switch([comicsBase addTitle:thisTitle])
      {
        case -2:
            NSRunAlertPanel(@"Comics", @"The title Zzzzzzzzzz already exists. Please change its name before you can create a new title.", @"OK", nil, nil);
            break;
        case -1:
            NSRunAlertPanel(@"Comics", @"The abbreviation ZZZ already exists. Please change its name before you can create a new title.", @"OK", nil, nil);
            break;
        case 0:
            thisIssue = [[CIssue alloc] initWithIsh:1 withEdit:theEditMonth withBuy:theBuyMonth withFlag:defaultFI];
            [thisTitle addIssue:thisIssue];
            [self selChanged];
            [self contentChanged];
            [inputView scrollRowToVisible:[array count]-1];
            [inputView selectRow:[array count]-1 byExtendingSelection:NO];
            break;
      }
}
- (void)deleteThisTitle:(id)sender
{
    CTitle *thisTitle;
    if ([inputView selectedRow] >= [array count]) return;
    thisTitle = [array objectAtIndex:[inputView selectedRow]];
    [comicsBase deleteTitle:thisTitle];
    [self selChanged];
    [self contentChanged];
}

#define whitey 0.8

- (id)init
{
   if (self = [super init])
     {
       nbRows = 0;
       brand = 0;
       series = 0;
       kind = 0;
       state = 0;
       sort = 0;
       // I have a lttle less than 1500 titles currently, hence the following capacity...
       array = [[NSMutableArray alloc] initWithCapacity:1500];
       curIshArray = (short *)nil;
       arrCol[0] = [[NSColor colorWithCalibratedRed:whitey green:1.0 blue:1.0 alpha:1.0] retain];
       arrCol[1] = [[NSColor colorWithCalibratedRed:1.0 green:whitey blue:whitey alpha:1.0] retain];
       arrCol[2] = [[NSColor colorWithCalibratedRed:whitey green:1.0 blue:whitey alpha:1.0] retain];
       arrCol[3] = [[NSColor colorWithCalibratedRed:whitey green:whitey blue:1.0 alpha:1.0] retain];
       arrCol[4] = [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:whitey alpha:1.0] retain];
       arrCol[5] = [[NSColor colorWithCalibratedRed:1.0 green:whitey blue:1.0 alpha:1.0] retain];
       fontProp = [[NSFont fontWithName:@"Helvetica" size:10] retain];
       fontNonProp = [[NSFont fontWithName:@"Ohlfs" size:10] retain];
     }
   return self;
}
- (void)dealloc
{
    int i;
    for(i=0; i<6; i++) [arrCol[i] release];
    [fontProp release];
    [fontNonProp release];
    [array release];
    [super dealloc];
}
- (void)userHasScrolled:(id)sender
{
    // if the user has scrolled then release the pop up button
    [puit releasePub];
    // and call back the original target and action of the vertical scroller so that we actually scroll... (see below)
    [myVerticalScroller sendAction:saveVerticalScrollerAction to:saveVerticalScrollerTarget];
}
- (void)awakeFromNib
{
   NSCalendarDate *date;
   NSView *aView;

   // finding the NSScrollView superview...
   for(aView=inputView; ![aView isKindOfClass:[NSScrollView class]]; aView = [aView superview]);
   myVerticalScroller = [(NSScrollView *)aView verticalScroller];
   // saving the current target and action of the vertical scroller to be able to call them later in userHasScrolled
   saveVerticalScrollerTarget = [myVerticalScroller target];
   saveVerticalScrollerAction = [myVerticalScroller action];
   // set the new target and action. Much more elegant than patching, much more efficient than subclassing.
   [myVerticalScroller setTarget:self];
   [myVerticalScroller setAction:@selector(userHasScrolled:)];

   theEditMonth = [comicsBase lastEditMonth] + 1;
   date = [NSCalendarDate calendarDate];
   theBuyMonth = [date monthOfYear] + 12 * ([date yearOfCommonEra] -1900);

   [selEditMonth setMinValue:[comicsBase startEditMonth]];
   [selEditMonth setMaxValue:theEditMonth];
   [self setTheEditMonth:theEditMonth];
   [selBuyMonth setMinValue:[comicsBase startBuyMonth]];
   [selBuyMonth setMaxValue:theBuyMonth];
   [self setTheBuyMonth:theBuyMonth];

   [nbIssues setIntValue:[comicsBase nbIssues]];
   [nbTitles setIntValue:[comicsBase nbTitles]];

   [inputView setRowHeight:14];
   [inputView setIntercellSpacing:NSMakeSize(1, 0)];
   [[[inputView tableColumns] objectAtIndex:2] setMinWidth:4];
   [[[inputView tableColumns] objectAtIndex:2] setWidth:4];
   [[[inputView tableColumns] objectAtIndex:13] setMinWidth:4];
   [[[inputView tableColumns] objectAtIndex:13] setWidth:4];

   [self selChanged];
}

// Since numberOfRowsInTableView is called a ZILLION times by the NSTableView object methods,
// do the number of rows calculation in selChanged, and save off the value in the nbRows field
// so that I can just return the value.
- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
   return nbRows;
}

// This method is called just before objectValueForTableColumn and allows us to modify the graphical aspect of the cell:
// font, size, color, background color, etc.
- (void)tableView:(NSTableView *)tv willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    NSString *identifier = [tableColumn identifier];
    [cell setFont:([identifier cString][0] == 'M')?fontNonProp:fontProp];
    if ([identifier isEqualToString:@"CurIsh"]) [cell setFont:fontNonProp];
    if ([identifier isEqualToString:@"Empty"])
        if (row == [tv selectedRow])
            [cell setBackgroundColor:[NSColor redColor]];
        else
            [cell setBackgroundColor:[NSColor blackColor]];
    else if ((row == [tv editedRow]) && ([tv columnWithIdentifier:identifier] == [tv editedColumn]))
        [cell setBackgroundColor:[NSColor whiteColor]];
    else
        [cell setBackgroundColor:arrCol[row % 6]];
    [cell setDrawsBackground:YES];
}

- (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    CIssue *thisIssue;
    CTitle *thisTitle = [array objectAtIndex:row];
    NSString *identifier = [tableColumn identifier], *result;
    thisIssue = [[thisTitle issues] objectAtIndex:[thisTitle findIssue:curIshArray[row]]];
    if ([identifier isEqualToString:@"Abb"]) result = [thisTitle abb];
    else if ([identifier isEqualToString:@"Title"]) result = [thisTitle title];
    else if ([identifier isEqualToString:@"Brand"]) result = [thisTitle brand];
    else if ([identifier isEqualToString:@"Series"]) result = [thisTitle series];
    else if ([identifier isEqualToString:@"State"]) result = [thisTitle tstate];
    else if ([identifier isEqualToString:@"Kind"]) result = [thisTitle kind];
    else if ([identifier isEqualToString:@"CurIsh"]) result = [NSString stringWithCString:gnums[curIshArray[row]]];
    else if ([identifier isEqualToString:@"Grade"]) result = [thisIssue grade];
    else if ([identifier isEqualToString:@"Type"]) result = [thisIssue ishtype];
    else if ([identifier isEqualToString:@"Content"]) result = [thisIssue content];
    else // column identifiers "M1" to "M6"
      {
        short k, j = -1, i = theEditMonth - 6 + [identifier cString][1] - '0';
        for(k = [thisTitle nbIssues]-1; (k >= 0) && (j == -1); k--)
            if (([thisIssue = [[thisTitle issues] objectAtIndex:k] editMonth] == i) && !([thisIssue issueFlags] & mskMiss)) j = k;
        if (j == -1) result = @"";
        else result = [NSString stringWithCString:gnums[[thisIssue issueNumber]]];
      }
    return result;
}

- (void)tableView:(NSTableView *)tv setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    NSString *identifier = [tableColumn identifier];
    CTitle *thisTitle = [array objectAtIndex:row];
    if ((([identifier isEqualToString:@"Abb"]) && (![[thisTitle abb] isEqualToString:object])) ||
        (([identifier isEqualToString:@"Title"]) && (![[thisTitle title] isEqualToString:object])))
      {
        CTitle *newTit = [[CTitle alloc] initWithAbb:[thisTitle abb] withTitle:[thisTitle title] withFlag:[thisTitle titleFlags]];
        if ([identifier isEqualToString:@"Abb"]) [newTit setAbb:object]; else [newTit setTitle:object];
        [comicsBase modTitle:thisTitle withNewTitle:newTit];

        [array replaceObjectAtIndex:row withObject:newTit];

        [self contentChanged];
      }
    else if ([identifier cString][0] == 'M')
      {
        CIssue *newIssue;
        short thisIssueNumber = [object intValue], j, ed = (theEditMonth - 6 + [identifier cString][1] - '0');
        if (thisIssueNumber >= 0)
            if ((j = ([thisTitle findIssue:thisIssueNumber])) == -1)
              {
                newIssue = [[CIssue alloc] initWithIsh:thisIssueNumber withEdit:ed withBuy:theBuyMonth withFlag:defaultFI];
                [thisTitle addIssue:newIssue];
                [self contentChanged];
              }
            // I don't allow the modification of an issue from typing its issue number, too dangerous...
            else NSRunAlertPanel(@"Comics", @"Sorry, you can't modify an existing issue by typing.", @"OK", nil, nil);
        [tableColumn setEditable:NO];
      }
}

- (BOOL)windowShouldClose:(id)sender
{
    if (![[inputView window] isDocumentEdited]) return YES;
    switch(NSRunAlertPanel(@"Comics", @"The comics database has been edited. Save?", @"Save", @"Don't save", @"Cancel"))
      {
        case NSAlertDefaultReturn:[comicsBase save:nil]; break;
        case NSAlertAlternateReturn:break;
        case NSAlertOtherReturn:return NO; break;
      }
    return YES;
}

@end
