//
//  TestOGModel.m
//  OminoGobo
//
//  Created by David Van Brink on 1/9/14.
//  Copyright (c) 2014 omino. All rights reserved.
//

#import "TestOGModel.h"
#import "OmTestView.h"
#import "OGModel.h"
#import "OmXmlHelper.h"
#import "Util.h"
#import "OGScenesFromXml.h"
#import "OGKinds.h"

#import "OGSpotlight.h"
#import "OGPhoto.h"
#import "OGMedia.h"

#import "SpotlightLayer.h"
#import "PhotoLayer.h"
#import "MovieLayer.h"

@interface Lis : NSObject <OGSceneListener>
- (void) sceneChanged:(OGScene *)scene;
@property int k;
@end
@implementation Lis
- (id) init
{
    self = [super init];
    self.k = 0;
    return self;
}

- (void) sceneChanged:(OGScene *)scene
{
    self.k++;
}
@end


@implementation TestOGModel
- (void) testAddingItem
{
    OGScene *sc = [[OGScene alloc] init];

    
    NSArray *items = sc.stageItems;
    ASSERT_NOT_NULL(items,"stage items property");
    ASSERT_EQUALS_FLOAT(0,items.count,"items count");

    
    OGStageItem *si = [[OGStageItem alloc] init];
    [sc addStageItem:si];

    items = sc.stageItems;
    ASSERT_NOT_NULL(items,"stage items property");
    ASSERT_EQUALS_FLOAT(1,items.count,"items count");
    
    [sc removeStageItem:si];
    items = sc.stageItems;
    ASSERT_NOT_NULL(items,"stage items property");
    ASSERT_EQUALS_FLOAT(0,items.count,"items count");

    [sc removeStageItem:si];
    items = sc.stageItems;
    ASSERT_NOT_NULL(items,"stage items property");
    ASSERT_EQUALS_FLOAT(0,items.count,"items count");
}

- (void) testStageItemUniqueIds
{
    NSMutableArray *ids = [[NSMutableArray alloc] init];
    
    for(int i = 0; i < 1000; i++)
    {
        OGStageItem *si = [[OGStageItem alloc] init];
        int uid = si.uid;
        NSNumber *uidNs = [NSNumber numberWithInt:uid];
        BOOL already = [ids containsObject:uidNs];
        ASSERT_EQUALS_INT(NO, already, "unique?");
        [ids addObject:uidNs];
        if(i > 5)
        {
            HIDE_OK
        }

    }
    SHOW_OK
}


- (void) testAddingAndRemovingALot
{
    OGScene *sc = [[OGScene alloc] init];
    for(int i = 0; i < 1000; i++)
    {
        OGStageItem *si = [[OGStageItem alloc] init];
        [sc addStageItem:si];
        
        if(i % 3)
            [sc addStageItem:si];
        if(i % 5)
            [sc removeStageItem:si];
        
        [sc removeStageItem:si];
    }
    ASSERT_EQUALS_INT(0, sc.stageItems.count, "all gone");
}

- (void) testAddingOneNotRemoving
{
    OGScene *sc = [[OGScene alloc] init];
    OGStageItem *si = [[OGStageItem alloc] init];
    [sc addStageItem:si];
}


- (void) testSceneListening
{
    OGScene *sc = [[OGScene alloc] init];
    Lis *lis = [[Lis alloc] init];
    
    [sc addListener:lis];
    
    OGStageItem *si = [[OGStageItem alloc] init];
    [sc addStageItem:si];
    
    ASSERT_EQUALS_INT(1, lis.k, "change notifications");
    
    si.size = 0.5;

    ASSERT_EQUALS_INT(2, lis.k, "change notifications");
    
    si.pos = CGPointMake(.12,.34);
    si.brightness = 0.9;

    ASSERT_EQUALS_INT(4, lis.k, "change notifications");
    
    // remove the item, and ensure no more changes...
    [sc removeStageItem:si];
    ASSERT_EQUALS_INT(5, lis.k, "change notifications");
    [sc removeStageItem:si];
    ASSERT_EQUALS_INT(5, lis.k, "change notifications");

    si.size = 0.5;
    ASSERT_EQUALS_INT(5, lis.k, "change notifications on item after removed?");
}

- (void) testGetState
{
    OGScene *sc = [[OGScene alloc] init];
    OGSpotlight *si = [[OGSpotlight alloc] init];
    si.hue = 0.2;
    si.brightness = 0.3;
    si.pos = CGPointMake(0.4,0.5);
    [sc addStageItem:si];
    
    NSArray *state = [sc getState];
    ASSERT_NOT_NULL(state, "got state");

    OGStageItemState *sis = state[0];

    ASSERT_EQUALS_INT(si.uid, sis.uid ,"uid?");
    ASSERT_EQUALS(@"spotlight", sis.kind, "type?");
    NSNumber *n = [sis.parameters objectForKey:@"hue"];
    ASSERT_EQUALS_FLOAT(0.2, [n floatValue], "hue");
    
    n = [sis.parameters objectForKey:@"brightness"];
    ASSERT_EQUALS_FLOAT(0.3, [n floatValue], "brightness");
    
    NSString *v = sis.parameters[@"pos"];
    CGPoint pos = CGPointFromString(v);
    ASSERT_EQUALS_FLOAT(0.4, pos.x, "pos");
    ASSERT_EQUALS_FLOAT(0.5, pos.y, "pos");
}

- (void) testSetState
{
    OGScene *sc = [[OGScene alloc] init];
    OGSpotlight *si = [[OGSpotlight alloc] init];
    si.hue = 0.2;
    si.brightness = 0.3;
    si.pos = CGPointMake(0.4,0.5);
    [sc addStageItem:si];
    
    NSMutableArray *state = [[NSMutableArray alloc]init];
    OGStageItemState *sis = [[OGStageItemState alloc]init];
    
    sis.kind = @"spotlight";
    sis.uid = si.uid;
    
    NSMutableDictionary *parameters = [[NSMutableDictionary alloc]init];
    sis.parameters = parameters;
    NSNumber *v = [NSNumber numberWithFloat:.25];
    parameters[@"hue" ] = v;

    [state addObject:sis];
    
    [sc setState:state];
    
    ASSERT_EQUALS_FLOAT(0.25, si.hue, "set hue?");
}


- (void) testSetStateAddsAndDeletes
{
    // if the saved state has new items, or omits current ones,
    // setting the state should add or delete as necessary.

    OGScene *sc = [[OGScene alloc] init];
    OGSpotlight *si1 = [[OGSpotlight alloc] init];
    [sc addStageItem:si1];
    
    NSArray *state1 = [sc getState];
    ASSERT_EQUALS_INT(1, state1.count, "sanity check");
    
    OGPhoto *si2 = [[OGPhoto alloc] init];
    [sc removeStageItem:si1];
    [sc addStageItem:si2];

    NSArray *state2 = [sc getState];
    ASSERT_EQUALS_INT(1, state2.count, "sanity check");
    
    // OK. Lets set them, and see the scene roll.
    [sc setState:state1];
    ASSERT_EQUALS_INT(1, sc.stageItems.count, "stage count")
    OGStageItem *si1B = sc.stageItems[0];
    ASSERT_EQUALS(@"spotlight", si1B.kind, "item type");

    [sc setState:state2];
    ASSERT_EQUALS_INT(1, sc.stageItems.count, "stage count")
    OGStageItem *si2B = sc.stageItems[0];
    ASSERT_EQUALS(@"photo", si2B.kind, "item type");
}

- (void) assertContains:(NSString *)container substring:(NSString *)substring
{
    NSString *messageNs = [NSString stringWithFormat:@"contains %@",substring];
    const char *message = [messageNs cStringUsingEncoding:NSASCIIStringEncoding];
    
    BOOL has =  ([container rangeOfString:substring].location != NSNotFound);
    ASSERT_EQUALS_INT(YES, has, message);
}

- (void) testItemXmlWrite
{
    OGSpotlight *si1 = [[OGSpotlight alloc] init];
    OGStageItem *si = si1;
    
    OmXmlHelper *x = [[OmXmlHelper alloc] init];
    [si getXml:x];
    
    NSString *xml = x.xml;
    
    NSString *uidAttr = [NSString stringWithFormat:@" uid=|%d|",si.uid];
    // crush it for easier assertions
    xml = [xml stringByReplacingOccurrencesOfString:@"\"" withString:@"|"];
    [self assertContains:xml substring:@"parameter name=|hue|"];
    [self assertContains:xml substring:@"parameter name=|pos|"];
    [self assertContains:xml substring:uidAttr];
}


- (void) testSceneXmlWrite
{
    OGSpotlight *si1 = [[OGSpotlight alloc] init];
    OGScene *sc = [[OGScene alloc] init];
    [sc addStageItem:si1];
    
    OmXmlHelper *x = [[OmXmlHelper alloc] init];
    OGSceneState *ss = [[OGSceneState alloc]init];
    [ss setScene:sc];
    [ss getXml:x];
    
    NSString *xml = x.xml;
    
    NSString *uidAttr = [NSString stringWithFormat:@" uid=|%d|",si1.uid];
    // crush it for easier assertions
    xml = [xml stringByReplacingOccurrencesOfString:@"\"" withString:@"|"];
    [self assertContains:xml substring:@"parameter name=|hue|"];
    [self assertContains:xml substring:@"parameter name=|pos|"];
    [self assertContains:xml substring:uidAttr];
    [self assertContains:xml substring:@"<scene "];
    [self assertContains:xml substring:@"</scene>"];
}

- (void) testScenesXmlWrite
{
    NSMutableArray *savedScenes = [[NSMutableArray alloc]init];
    OmXmlHelper *x = [[OmXmlHelper alloc] init];

    for(int i = 0; i < 10; i++)
    {
        OGScene *sc = [[OGScene alloc] init];

        for(int j = 0; j <i; j++)
        {
            OGSpotlight *sp = [[OGSpotlight alloc] init];
            sp.brightness = rr(1);
            sp.size = rr(1);
            sp.pos = CGPointMake(rr(1),rr(1));
            [sc addStageItem:sp];
        }
        OGSceneState *ss = [[OGSceneState alloc]init];
        [ss setScene:sc];
        [savedScenes addObject:ss];
    }
    
    OGScene *stageScene = [[OGScene alloc] init];
    
    
    [OGSceneState getAllXml:x savedScenes:savedScenes stageScene:stageScene selectedSceneIndex:7 selectedItemUid:8];
    NSString *xml = x.xml;
    NSString *xml0 = xml;
    xml = [xml stringByReplacingOccurrencesOfString:@"\"" withString:@"|"];
    xml = [xml stringByReplacingOccurrencesOfString:@" " withString:@""];
    xml = [xml stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    xml = [xml stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    xml = [xml stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    
//    NSLog(@"%@",xml);
    [self assertContains:xml substring:@"</scene><scene"];
    
    [self assertContains:xml substring:@"selectedSceneIndex="];
    [self assertContains:xml substring:@"selectedItemUid="];

    OGScenesFromXml *appState = [OGScenesFromXml parseXml:xml0];
    NSArray *scenesBack = appState.savedSceneStates;
    ASSERT_EQUALS_INT(10, (int)scenesBack.count, "enough scenes back from XML");
    
    ASSERT_EQUALS_INT(7, appState.selectedSceneIndex, "selected scene index");
    ASSERT_EQUALS_INT(8, appState.selectedItemUid, "selected item uid");
    
    // check each recovered scene for some details.
    // scene N has N spotlights in it...
    for(int ix = 0; ix < 10; ix++)
    {
        OGSceneState *ss = scenesBack[ix];
        OGScene *sc = [[OGScene alloc]init];
        [ss putScene:sc];
        ASSERT_EQUALS_INT(ix,sc.stageItems.count, "items in scene?");
    }
}


- (void) check1OgClassByName:(NSString *)name modelClass:(Class)expectedModelClass layerClass:(Class)expectedLayerClass
{
    OGKinds *kinds = [[OGKinds alloc]init];
    
    Class cM = [kinds getModelClassForName:name];
    Class cL = [kinds getLayerClassForName:name];
    NSString *nssM = [NSString stringWithFormat:@"%@ found %@?",name,NSStringFromClass(cM)];

    NSString *nssL = [NSString stringWithFormat:@"%@ found %@?",name,NSStringFromClass(cL)];

    const char *sM = [nssM cStringUsingEncoding:NSASCIIStringEncoding];
    const char *sL = [nssL cStringUsingEncoding:NSASCIIStringEncoding];
    
    ASSERT_EQUALS_INT(YES, cM == expectedModelClass, sM);
    ASSERT_EQUALS_INT(YES, cL == expectedLayerClass, sL);
    
    cM = getModelClassForName(name);
    cL = getLayerClassForName(name);
    ASSERT_EQUALS_INT(YES, cM == expectedModelClass, "");
    ASSERT_EQUALS_INT(YES, cL == expectedLayerClass, "");
}

- (void) testKinds
{
    [self check1OgClassByName:@"spotlight"
                   modelClass:[OGSpotlight class]
                   layerClass:[SpotlightLayer class]];
    [self check1OgClassByName:@"photo"
modelClass:[OGPhoto class]
layerClass:[PhotoLayer class]];
    [self check1OgClassByName:@"movie"
modelClass:[OGMedia class]
layerClass:[MovieLayer class]];
}

@end


