//
//  EclipseFinderController.mm
//  celestia
//
//  Created by Da Woon Jung on 2007-05-07.
//  Copyright 2007 Da Woon Jung. All rights reserved.
//

#import "EclipseFinderController.h"
#import "CelestiaAppCore.h"
#import "NSString_ObjCPlusPlus.h"
#import "CelestiaBody_PrivateAPI.h"
#include "eclipsefinder.h"
#include <celutil/util.h>

@interface EclipseFinderController(Private)
- (void)getEclipses: (id)aObject;
- (void)getEclipsesDone: (id)aObject;
@end


@implementation EclipseFinderController

static NSMutableArray *eclipses;
static NSCalendarDate *startDate;
static NSCalendarDate *midDate;
static NSCalendarDate *endDate;
static string receiverName;
static Eclipse::Type eclipseType;
static CelestiaBody *eclipseBody;

- (id)init
{
    self = [super initWithWindowNibName: @"EclipseFinder"];
    if (self)
    {
        eclipses = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)windowDidLoad
{
    [eclipseList setDoubleAction: @selector(go:)];
}

- (void)windowWillClose:(NSNotification *)aNotification
{
    if ([self window] == [aNotification object])
    {
        keepGoing = NO;
    }
}

- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
    NSDictionary *userInfo = [aNotification userInfo];
    if (userInfo && NSReturnTextMovement == [[userInfo objectForKey: @"NSTextMovement"] intValue])
    {
        [self find: self];
    }
}

- (IBAction)find: (id)sender
{
    CelestiaSimulation *sim;
    CelestiaSelection *sel;
    Body *body;
    id startDateObj       = [eclipseFindStart objectValue];
    id endDateObj         = [eclipseFindEnd   objectValue];
    NSString *receiverStr = [eclipseReceiver  stringValue];
    keepGoing = YES;

    if (0 == [receiverStr length])
    {
        NSRunAlertPanel(NSLocalizedString(@"No Object Name Entered",@""),
                        NSLocalizedString(@"Please enter an object name.",@""),
                        nil,nil,nil);
        return;
    }

    if (nil == startDateObj || nil == endDateObj)
    {
        NSRunAlertPanel(NSLocalizedString(@"No Starting or Ending Date Entered",@""),
                        NSLocalizedString(@"Please enter starting and ending dates.",@""),
                        nil,nil,nil);
        return;
    }

    sim = [[CelestiaAppCore sharedAppCore] simulation];
    sel = [sim findObjectFromPath: receiverStr];
    if (nil == sel || [sel isEmpty])
    {
        NSRunAlertPanel(NSLocalizedString(@"Object Not Found",@""),
                        NSLocalizedString(@"Please check that the object name is correct.",@""),
                        nil,nil,nil);
        return;
    }

    eclipseBody = [sel body];
    body = [eclipseBody body];
    if (nil == body || nil == body->getSystem())
    {
        NSRunAlertPanel(NSLocalizedString(@"Object Not Found",@""),
                        NSLocalizedString(@"Please check that the object name is correct.",@""),
                        nil,nil,nil);
        return;
    }

    if (body->getSystem()->getPrimaryBody())
    {
        // Eclipse receiver is a moon -> find lunar eclipses
        eclipseType = Eclipse::Moon;
        PlanetarySystem *system = body->getSystem();
        if (system)
        {
            Body *parent = system->getPrimaryBody();
            if (NULL == parent)
            {
                return;
            }
            receiverName = parent->getName();
        }
    }
    else
    {
        eclipseType = Eclipse::Solar;
        receiverName = body->getName();
        // EclipseFinder class expects unlocalized names
    }
    

    startDate = (NSCalendarDate *)startDateObj;
    endDate = (NSCalendarDate *)endDateObj;
    midDate = [startDate dateByAddingYears: 0
                                     months: 0
                                       days: 15
                                      hours: 0
                                    minutes: 0
                                    seconds: 0];
    // Find eclipses in small timeslices, to give the user
    // a chance to abort with the stop button

    [findButton setEnabled: NO];
    eclipses = [[NSMutableArray alloc] init];
    [eclipseProgress startAnimation: self];

    [NSThread detachNewThreadSelector: @selector(getEclipses:)
                             toTarget: self withObject: nil];
}

/* Go code borrowed from Windows version */
- (IBAction)go: (id)sender
{
    NSInteger rowIndex = [eclipseList selectedRow];
    if (rowIndex < 0) return;

    CelestiaCore *appCore = (CelestiaCore*) [[CelestiaAppCore sharedAppCore] appCore];
    NSDictionary *eclipse = [eclipses objectAtIndex: rowIndex];
    Body *body = [(CelestiaBody *)[eclipse objectForKey: @"body"] body];
    double startTime = [[eclipse objectForKey: @"start"] doubleValue];
    Simulation *sim = appCore->getSimulation();
    sim->setTime(startTime);
    Selection target(body);
    Selection ref(body->getSystem()->getStar());
    sim->setFrame(ObserverFrame::PhaseLock, target, ref);
    sim->update(0.0);

    double distance = astro::kilometersToMicroLightYears(target.radius() * 4.0);
    sim->setSelection(target);
    sim->gotoLocation(Point3d(distance, 0, 0),
                      Quatd::yrotation(-0.5*PI)*Quatd::xrotation(-0.5*PI),
                      5.0);
}

- (IBAction)stopFind: (id)sender
{
    keepGoing = NO;
}


- (void)getEclipses: (id)aObject
{
    CelestiaCore *appCore = (CelestiaCore*) [[CelestiaAppCore sharedAppCore] appCore];
    EclipseFinder *eclipseFinder;
    const Eclipse *eclipse;
    double jd;
    double hours, minutes;
    NSString *eclipseName;
    NSString *duration;
    NSCalendarDate *eclipseDate;
    vector<Eclipse>::const_iterator iter;

    while (keepGoing)
    {
        if (NSOrderedDescending == [midDate compare: endDate])
        {
            if (NSOrderedAscending == [startDate compare: endDate])
            {
                midDate = endDate;
            }
            else
            {
                break;
            }
        }

        eclipseFinder =
            new EclipseFinder(appCore,
                              receiverName,
                              eclipseType,
                              (double)astro::Date(astro::secondsToJulianDate([startDate timeIntervalSince1970])) + (double)astro::Date(1970,1,1),
                              (double)astro::Date(astro::secondsToJulianDate([midDate timeIntervalSince1970])) + (double)astro::Date(1970,1,1)
                              );

        const vector<Eclipse> &freshEclipses = eclipseFinder->getEclipses();

        for (iter  = freshEclipses.begin();
             iter != freshEclipses.end() && keepGoing;
             ++iter)
        {
            eclipse = &(*iter);
            jd = astro::julianDateToSeconds((double)(*eclipse->date));
            if (0 == jd && eclipse->planete == "None")
                continue;
            if (eclipse->body != [(CelestiaBody *)eclipseBody body])
                continue;

            eclipseName = [NSString stringWithStdString: (eclipseType == Eclipse::Moon ? eclipse->planete : eclipse->sattelite)];
            eclipseDate = [NSCalendarDate dateWithTimeIntervalSince1970: (jd - astro::julianDateToSeconds((double)astro::Date(1970,1,1)))];
            hours = astro::julianDateToSeconds(eclipse->endTime - eclipse->startTime)/3600.0;
            minutes = 60.0*(hours - (int)hours);
            duration = [NSString stringWithFormat: @"%02d:%02d", (int)hours, (int)minutes];

            [eclipses addObject:
                [NSDictionary dictionaryWithObjectsAndKeys:
                    [NSString stringWithUTF8String:_([eclipseName UTF8String])], @"caster",
                    eclipseDate, @"date",
                    [[CelestiaBody alloc] initWithBody: eclipse->body], @"body",
                    [NSNumber numberWithDouble: eclipse->startTime], @"start",
                    duration, @"duration",                
                    nil]
            ];
        }

        delete eclipseFinder;

        startDate = midDate;
        midDate = [startDate dateByAddingYears: 0
                                           months: 0
                                             days: 15
                                            hours: 0
                                          minutes: 0
                                         seconds: 0];
    }

    [self performSelectorOnMainThread: @selector(getEclipsesDone:)
                           withObject: nil
                        waitUntilDone: NO];
}

- (void)getEclipsesDone: (id)aObject
{
    startDate = nil;
    midDate = nil;
    endDate = nil;
    eclipseBody = nil;
    [eclipseProgress stopAnimation: self];
    [findButton setEnabled: YES];
    [eclipseList reloadData];    
}


- (NSInteger)numberOfRowsInTableView: (NSTableView *)aTableView
{
    return [eclipses count];
}

- (id)tableView: (NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
    id result = nil;
    NSDictionary *eclipse = nil;

    if ([eclipses count] > 0)
    {
        eclipse = [eclipses objectAtIndex: rowIndex];
        if ([[aTableColumn identifier] isEqualToString: @"begin"])
        {
            result = [NSCalendarDate dateWithTimeIntervalSince1970: astro::julianDateToSeconds([[eclipse objectForKey: @"start"] doubleValue] - (double)astro::Date(1970,1,1))];
        }
        else
        {
            result = [eclipse objectForKey: [aTableColumn identifier]];
        }
    }

    return result;
}
@end
