//
//  OmPlacesList.m
//  OmLoc
//
//  Created by david van brink on 7/24/10.
//  Copyright 2010 omino.com. All rights reserved.
//

#import "OmPlacesList.h"
#import "OmXmlHelper.h"


@implementation OmPlacesList


UIImage *omMarkers[MARKER_COUNT] = {0};





static NSString *pathForMapName(const char *mapName)
{
    // we make the file $Home/Documents/MapName.xml.
    
    NSString *currentPath = NSHomeDirectory();
    NSString *filePath = [NSString stringWithFormat:@"%@/Documents/%s/%s.xml",currentPath,mapName,mapName];
    return filePath;
}

static int lastIndexOf(NSString *s,char c)
{
    int len = [s length];
    while(--len >= 0)
    {
        char cAt = [s characterAtIndex:len];
        if(c == cAt)
            return len;
    }
    return -1;
}

/* Get the directory holding a file. if a directory (ends in slash) return one level up. */
static NSString *parentPath(NSString *filePath)
{
    // Strip off trailing slash, if any.
    int len = [filePath length];
    if(len > 0 && [filePath characterAtIndex:len-1] == '/')
        filePath = [filePath substringToIndex:len - 1];
    
    int lastSlash = lastIndexOf(filePath,'/');
    if(lastSlash >= 0)
        filePath = [filePath substringToIndex:lastSlash + 1];
    return filePath;
}

static NSString *getBaseName(NSString *filePath)
{
    // remove everything up to the last slash...
    int lastSlash = lastIndexOf(filePath,'/');
    if(lastSlash >= 0)
        filePath = [filePath substringFromIndex:lastSlash + 1];
    return filePath;
}

// Get the path for this image name, or nil if there's no such path. nil input ok too.
- (NSString *)pathForImgName:(NSString *)imgName
{
    if(imgName == nil)
        return nil;
    
    NSString *filePath = pathForMapName(self->mapName);
    NSString *ourDir = parentPath(filePath);
    NSString *imgPath = imgName;
    // not absolute path? look relative to our data file.
    if('/' != [imgPath characterAtIndex:0])
        imgPath = [NSString stringWithFormat:@"%@%@",ourDir,imgPath];
    
    if(![[NSFileManager defaultManager] fileExistsAtPath:imgPath])
        imgPath = nil;
    
    return imgPath;
}




// if you init an existing placeslist to a new name, be sure to clearALl first! maybe after saving.
- (OmPlacesList *)initNewMap:(char *)newMapName;
{
    sprintf(self->mapName,"%s",newMapName);
    self->wayPointCount = 0;
    self->mapCenterWayPoint = -1;

    // load up the markers
    if(omMarkers[0] == 0)
        for(int i = 0; i < MARKER_COUNT; i++)
        {
            NSString *markerFileName = [NSString stringWithFormat:@"marker%d.png",i+1];
            omMarkers[i] = [UIImage imageNamed:markerFileName];
            [omMarkers[i] retain];
        }
    return self;
}

static void fillInTimeDescription(OmWayPoint *p)
{
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:p->t];
    NSCalendar *gregorian = [[NSCalendar alloc]                      initWithCalendarIdentifier:NSGregorianCalendar];
    
    NSDateComponents *components =[gregorian components:NSWeekdayCalendarUnit|NSHourCalendarUnit fromDate:date];
    
    int weekday = [components weekday]; // weekday 1 = Sunday for Gregorian calendar
    int hour = [components hour];
    char *weekdayString;
    char *todString;
    switch(weekday)
    {
    case 0:
    case 7: weekdayString = "SAT"; break;
    case 1: weekdayString = "SUN"; break;
    case 2: weekdayString = "MON"; break;
    case 3: weekdayString = "TUE"; break;
    case 4: weekdayString = "WED"; break;
    case 5: weekdayString = "THU"; break;
    case 6: weekdayString = "FRI"; break;
    default: weekdayString = "NOD"; break;
    }
    if(hour < 6)       todString = "AM";
    else if(hour < 12) todString = "MRN";
    else if(hour < 16) todString = "AFT";
    else if(hour < 20) todString = "EVE";
    else               todString = "NGT";
    
    sprintf(p->timeDescription,"%s %s",weekdayString,todString);
    
    [gregorian release];
}


- (int)addWayPoint:(OmWayPoint *)p
{
    if(self->wayPointCount < MAX_PLACES)
    {
        OmWayPoint *newP = self->wayPoints + self->wayPointCount;
        *newP = *p;
        fillInTimeDescription(newP);
        
        self->wayPointCount++;
    }
    return self->wayPointCount;
}

- (int)getWayPointCount
{
    return self->wayPointCount;
}

- (OmWayPoint *)getWayPoint:(int)ix;
{
    return self->wayPoints + ix;
}


- (void)clear:(int)index
{
    if(index >= 0 && index < self->wayPointCount)
    {
        OmWayPoint *p = &self->wayPoints[index];
        if(p->image)
        {
            [p->image release];
            p->image = 0;
        }
        if(p->imageName)
        {
            NSString *imgPath = [self pathForImgName:p->imageName];
            if(imgPath)
                [[NSFileManager defaultManager] removeItemAtPath:imgPath error:nil];
            [p->imageName release];
            p->imageName = 0;
        }
        
        // pull everyone up by one
        for(int i = index; i < self->wayPointCount - 1; i++)
            self->wayPoints[i] = self->wayPoints[i + 1];
        self->wayPointCount--;
        OmWayPoint po = {0};
        self->wayPoints[wayPointCount] = po;
    }
}

- (void)clearLast
{
    [self clear:self->wayPointCount - 1];
}

- (void)clearAll
{
    while(self->wayPointCount)
        [self clearLast];
}

static NSString *findUniqueSavePath(char *mapNamePrefix, char *back)
{
    int i = 0;
    NSString *path;
    do
    {
        path = [NSString stringWithFormat:@"%@/Documents/%s/%s_%04d%s",NSHomeDirectory(),mapNamePrefix,mapNamePrefix,i++,back];
        
    } while([[NSFileManager defaultManager] fileExistsAtPath:path]);
    return path;
}

- (bool)saveToFile
{
    NSString *filePath = pathForMapName(self->mapName);
    NSString *ourDir = parentPath(filePath);
    
    [[NSFileManager defaultManager] createDirectoryAtPath:ourDir withIntermediateDirectories:YES attributes:nil error:nil];
    NSSTRING_TO_C(filePath,filePathC);

    NSLog(@"writing map file path: %s\n",filePathC);
    
    OmXmlHelper *x = [OmXmlHelper alloc];
    [x autorelease];
    
    bool result;
    result = [x beginWriting:filePathC comment:"new map" outerElement:"map"];
    if(!result)
        return result;
    
    // for amusement purposes only, save the file location in the file.
    [x beginElement:"file"];
    [x addText:filePathC];
    [x endElement];
    
    // write out all the places!
    for(int i = 0; i < self->wayPointCount; i++)
    {
        OmWayPoint *p = [self getWayPoint:i];
        
        // write the image file, if we havent yet.
        // it'll have a name if it was loaded, or if we have already saved it the onceish.
        if(p->image && !p->imageName)
        {
            NSString *imPath = findUniqueSavePath(mapName, ".jpg");
            NSData *jpg = UIImageJPEGRepresentation(p->image, 0.9);
            [jpg writeToFile:imPath atomically:YES];
            p->imageName = getBaseName(imPath);
            [p->imageName retain];
        }

        [x beginElement:"place"];
        [x addAttribute:"lat" value:GSPrintf("%f",p->lat)];
        [x addAttribute:"lon" value:GSPrintf("%f",p->lon)];
        [x addAttribute:"alt" value:GSPrintf("%f",p->alt)];
        [x addAttribute:"t" value:GSPrintf("%d",p->t)];
        [x addAttribute:"mk" value:GSPrintf("%d",p->marker)];
        [x addAttribute:"ctr" value:GSPrintf("%d",i == self->mapCenterWayPoint ? 1 : 0)];
        if(p->imageName)
        {
            NSSTRING_TO_C(p->imageName,baseNameC);
            [x addAttribute:"img" value:baseNameC];
        }
        if(p->desc)
        {
            NSSTRING_TO_C(p->desc,descriptionC);
            [x addAttribute:"desc" value:descriptionC];
        }
        [x endElement];
        
    }
    
    result = [x close];
    return result;
}

// switch to another map. (init isn't as severe as it sounds)
- (void)loadMap:(char *)aMapName
{
    [self initNewMap:aMapName];
    [self loadFromFile];
}

- (bool)loadFromFile
{
    
//    // TMP LOAD FROM OLD PLC
//    NSString *currentPath = NSHomeDirectory();
//    NSString *filePath = [NSString stringWithFormat:@"%@/Documents/%s.xml",currentPath,mapName,mapName];
//
//    
//    
    NSString *filePath = pathForMapName(self->mapName);
    NSError *err = 0;
    NSData *d = [NSData dataWithContentsOfFile:filePath options:NSDataReadingUncached  error:&err];
    NSXMLParser *p = [NSXMLParser alloc];
    [p autorelease];
    [p initWithData:d];
    
    // safety. and from: http://codesofa.com/blog/archive/2008/07/23/make-nsxmlparser-your-friend.html handy! thx.
    [p setDelegate:self]; // The parser calls methods in this class
    [p setShouldProcessNamespaces:NO]; // We don't care about namespaces
    [p setShouldReportNamespacePrefixes:NO]; //
    [p setShouldResolveExternalEntities:NO]; // We just want data, no other stuff
    
    [self clearAll]; // start empty again.
    [p parse];
    return true;
}

- (NSString *) getImageFilePath:(int)wayPointIndex
{
    if(wayPointIndex > wayPointCount)
        return nil;
    OmWayPoint *wp = [self getWayPoint:wayPointIndex];
    if(wp->imageName == nil)
        return nil;
    NSString *path = [NSString stringWithFormat:@"%@/Documents/%s/%@",NSHomeDirectory(),mapName,wp->imageName];
    return path;
}

// +-----------------------------------------
// | Delegate methods for xml parsing
// | (crass to use the same class here
// | for this, but there I am. dvb10)
// |

- (void)parser:(NSXMLParser *)parser 
            didStartElement:(NSString *)elementName 
            namespaceURI:(NSString *)namespaceURI 
            qualifiedName:(NSString *)qName 
            attributes:(NSDictionary *)attributeDict
{
    // we here recognize our one crucial element, "place", and its attributes.
    if([elementName compare:@"place"] == 0)
    {
        NSString *ourDir = parentPath(pathForMapName(self->mapName));

        OmWayPoint p = {0};
        
        p.image = 0;
        p.lat = [[attributeDict valueForKey:@"lat"] doubleValue];
        p.lon = [[attributeDict valueForKey:@"lon"] doubleValue];
        p.alt = [[attributeDict valueForKey:@"alt"] doubleValue];
        p.t = [[attributeDict valueForKey:@"t"] intValue];
        p.marker = [[attributeDict valueForKey:@"mk"] intValue];
        p.desc = [attributeDict valueForKey:@"desc"];
        int isCenter = [[attributeDict valueForKey:@"ctr"] intValue];
        if(p.desc)
            [p.desc retain];
        NSString *imgName = [attributeDict valueForKey:@"img"];
        if(imgName && [imgName length] > 0)
        {
            NSString *imgPath = imgName;
            // not absolute path? look relative to our data file.
            if('/' != [imgPath characterAtIndex:0])
                imgPath = [NSString stringWithFormat:@"%@%@",ourDir,imgPath];
            
            if([[NSFileManager defaultManager] fileExistsAtPath:imgPath])
            {
                UIImage *im = [UIImage alloc];
                [im initWithContentsOfFile:imgPath];
                p.image = im;
                [imgName retain];
                p.imageName = imgName;
            }
        }
        int wpCount = [self addWayPoint:&p];
        if(isCenter)
            self->mapCenterWayPoint = wpCount - 1;
    }
}

- (void)parser:(NSXMLParser *)parser 
            didEndElement:(NSString *)elementName 
            namespaceURI:(NSString *)namespaceURI 
            qualifiedName:(NSString *)qName
{
    // do nothing for now.
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // do nothing for now
}


void setWayPointDescription(OmWayPoint *p,NSString *desc)
{
    if(p->desc)
        [p->desc release];
    p->desc = desc;
    if(p->desc)
        [p->desc retain];
}

- (void)drawWayPoint:(int)wayPointIndex context:(CGContextRef)context
{
    OmWayPoint *p = wayPoints + wayPointIndex;
    UIImage *im = omMarkers[p->marker % MARKER_COUNT];
    CGSize size = [im size];
    CGPoint xy = p->screenPoint;
    xy.x -= size.width / 2;
    xy.y -= size.height / 2;
    [im drawInRect:CGRectMake(xy.x,xy.y,size.width,size.height)];
}


- (NSString *)getMapFilePath
{
    NSString *result = pathForMapName(self->mapName);
    return result;
}

- (int)getMapCenter
{
    return self->mapCenterWayPoint;
}

- (void)setMapCenter:(int)ix
{
    self->mapCenterWayPoint = ix;
}

- (char *) getMapName
{
    return self->mapName;
}
@end
