//
//  RecipeTests.m
//  MealBuilder
//
//  Created by Phil Horowitz on 2/14/13.
//  Copyright (c) 2013 Phil Horowitz. All rights reserved.
//

#import "RecipeTests.h"
#import "Recipe.h"
#import "Measurements.h"
@interface RecipeTests()
@property (nonatomic, strong) NSDateFormatter* formatter;
@end

@implementation RecipeTests

-(void)setUp{
    self.formatter = [[NSDateFormatter alloc] init];
    [self.formatter setDateFormat:@"yyyy-MM-dd HH:mm"];
}

-(void) testCopy{
    Recipe *testRecipe = [[Recipe alloc] init];
    testRecipe.finishTime = [NSDate date];
    testRecipe.servings = 10;
    testRecipe.servingsIncrement = 3;
    testRecipe.portionSize = [[Gram alloc] initWithAmount:37.5];
    [testRecipe addAction:[[RecipeAction alloc] initWithAction:@"Stuff" withTime:6]];
    Ingredient *ing = [[Ingredient alloc] initWithName:@"Some crap" andAmount:[[Milliliter alloc] initWithAmount:777]];
    ing.isAllergen = YES;
    [testRecipe addIngredient:ing];
    Tool* newTool = [[Tool alloc] init];
    newTool.name = @"fork";
    RecipeAction *toolAction = [[RecipeAction alloc] initWithAction:@"stir" withTime:9999];
    [newTool.actions addObject:toolAction];
    
    Recipe *copyRecipe = [testRecipe copy];
    STAssertTrue([testRecipe.finishTime compare: copyRecipe.finishTime] == NSOrderedSame, @"Copy should duplicate finishTime");
    STAssertEquals(testRecipe.servings, copyRecipe.servings, @"Copy should duplicate servings");
    STAssertEquals(testRecipe.servingsIncrement, copyRecipe.servingsIncrement, @"Copy should duplicate servingsIncrement");
    STAssertTrue([testRecipe.portionSize isEqual:copyRecipe.portionSize], @"Copy should duplicate portion size");
    
    STAssertTrue([[testRecipe getDirections] isEqualToArray:[copyRecipe getDirections]], @"Copy should copy directions");
    STAssertTrue([[testRecipe getIngredients]  isEqualToArray:[copyRecipe getIngredients]], @"Copy should copy ingredients");
    STAssertTrue([[testRecipe getTools] isEqualToArray:[copyRecipe getTools]], @"Copy should copy tools");
}
-(void) testFinishTimeDelegatesToActions{
    RecipeAction *firstAction = [[RecipeAction alloc] init];
    RecipeAction *secondAction = [[RecipeAction alloc] init];
    firstAction.time = 20;
    secondAction.time = 235;
    
    Recipe *testRecipe = [[Recipe alloc] init];
    [testRecipe addAction:firstAction];
    [testRecipe addAction:secondAction];
    testRecipe.finishTime = [self.formatter dateFromString:@"1970-01-01 12:00"];
    NSArray *recipeDirections = [testRecipe getDirections];
    for (RecipeAction *r in recipeDirections) {
        STAssertNotNil(r.minStartTime, @"Setting finishTime on a recipe should set startTime on its actions");
    }
}

-(void) testAddingSameIngredientCombinesIngredients{
    Recipe *testRecipe = [[Recipe alloc] init];
    NSString *ingName = @"stuff";
    float amount = 400;
    UnitContainer *container = [[UnitContainer alloc] initWithUnit:[[Gram alloc] initWithAmount:amount] ];
    Ingredient *ing1 = [[Ingredient alloc] initWithName:ingName andContainer:container];
    [testRecipe addIngredient:ing1];
    [testRecipe addIngredient:ing1];
    NSArray *ingredientList = [testRecipe getIngredients];
    STAssertEquals(1U, [ingredientList count], @"Adding an existing ingredient should not add the ingredient again");
    STAssertEqualsWithAccuracy(amount*2, ((Ingredient *)ingredientList[0]).amount.amount, 0.1, @"Adding an existing ingredient should combine the resuls if compatible");
}

-(void)testRecipeServingSizeIsConstrainedToIncrement{
    Recipe *recipe = [[Recipe alloc] init];
    STAssertEquals(1U, recipe.servingsIncrement, @"Recipe.servingsIncrement should default to 1");
    
    recipe.servingsIncrement = 0;
    STAssertEquals(1U, recipe.servingsIncrement, @"Recipe.servingsIncrement should never be less than 1");
    
    recipe.servingsIncrement = 4;
    recipe.servings = 3;
    STAssertEquals(recipe.servings, recipe.servingsIncrement, @"Setting a recipe's serving should be constrained to multiples of servingsIncrement");
    
    recipe.servings = 5;
    STAssertEquals(recipe.servings, recipe.servingsIncrement * 2, @"Servings should always round up to the nearest servingsIncrement multiple");
}

-(void) testAddingIngredientCreatesSubstitutionsArray{
    Recipe *recipe = [[Recipe alloc] init];
    Ingredient *ing = [[Ingredient alloc] initWithName:@"Ingredient"];
    NSArray *subs = [recipe getSubstutitionsForIngredient:ing];
    STAssertNil(subs, @"Getting substitutitions for an ingredient that's not part of the recipe should return nil");
    
    [recipe addIngredient:ing];
    subs = [recipe getSubstutitionsForIngredient:ing];
    STAssertNil(subs, @"Getting substitutions for an ingredient that's part of the recipe should return nil");

    Ingredient *sub = [[Ingredient alloc] init];
    [recipe addIngredient:sub asSubstitutionFor:ing];
    subs = [recipe getSubstutitionsForIngredient:ing];
    STAssertNotNil(subs, @"Getting substitutions for an ingredient with substiturtions should return a value");
    STAssertEquals(subs[0], sub, @"addIngredient:asSubstutitionFor: should add the ingredient to the substitution list");
    
    [recipe addIngredient:ing asSubstitutionFor:sub];
    STAssertNil([recipe getSubstutitionsForIngredient:sub], @"attempting to add a substitution for an ingredient that isn't a part of the recipe should not add the substitution");
}

-(void) testSubstutitions{
    Recipe *recipe = [[Recipe alloc] init];
    Ingredient *toSub = [[Ingredient alloc] initWithName:@"startingIngredient"];
    Ingredient *sub = [[Ingredient alloc] initWithName:@"Substitute with me"];
    Ingredient *noSub = [[Ingredient alloc] initWithName:@"Don't substitute me"];
    [recipe addIngredient:toSub];
    [recipe addIngredient:sub asSubstitutionFor:toSub];

    
    [recipe substituteIngredient:toSub with:noSub];
    NSArray *ingredient = [recipe getIngredients];
    STAssertEqualObjects(toSub, ingredient[0], @"Trying to substitute an item that isn't a designated substutition should not change the ingredient");
    
    [recipe substituteIngredient:toSub with:sub];
    ingredient = [recipe getIngredients];
    STAssertEqualObjects((Ingredient *)ingredient[0], sub, @"Substitution method should change the ingredient it's substituting");
    
    STAssertNil([recipe getSubstutitionsForIngredient:toSub], @"An item that has been substituted should be listed as substitutable");
    NSArray *subsItems = [recipe getSubstutitionsForIngredient:sub];
    STAssertNotNil(subsItems, @"Performing a substitution should set itself a substitutable");
    
    STAssertTrue([subsItems containsObject:toSub], @"Original item should be listed as a valid substitution");
}

-(void) testResizingRecipeChangesIngredientSize{
    Recipe *recipe = [[Recipe alloc] init];
    NSUInteger baseAmount = 4;
    Ingredient *ingredient = [[Ingredient alloc] initWithName:@"" andAmount:[[Gram alloc] initWithAmount:100]];
    Ingredient *subIngredient = [[Ingredient alloc] initWithName:@"sub" andAmount:[[Gram alloc] initWithAmount:275]];
    recipe.servings = baseAmount;
    [recipe addIngredient:ingredient];
    [recipe addIngredient:subIngredient asSubstitutionFor:ingredient];
    float expectedAmount = ingredient.amount.amount/baseAmount;
    
    recipe.servings = 1;
    STAssertEqualsWithAccuracy(expectedAmount, ingredient.amount.amount, 0.1f, @"Scaling the serving size should also scale the size of the ingredients");
    Ingredient *sub = [recipe getSubstutitionsForIngredient:ingredient][0];
    STAssertEqualsWithAccuracy(sub.amount.amount, 275.0f/baseAmount, 0.1, @"Setting the meal size should resize substitutions");
}

#pragma mark Tool method tests
-(void) testAddTool{
    Recipe *recipe = [[Recipe alloc] init];
    Tool *testTool = [[Tool alloc] init];
    RecipeAction *testAction = [[RecipeAction alloc] init];
    [testTool.actions addObject:testAction];

    [recipe addTool:testTool];
    STAssertEqualObjects(testTool, [recipe getTools][0], @"Should be able to add tools and get tools");
    STAssertEquals(1U, [[recipe getDirections] count], @"Adding a tool with associated actions should add those actions to the meal");
}

-(void) testRemoveTool{
    Recipe *recipe = [[Recipe alloc] init];
    Tool *testTool = [[Tool alloc] init];
    RecipeAction *testAction = [[RecipeAction alloc] init];
    [testTool.actions addObject:testAction];
    
    [recipe addTool:testTool];
    [recipe removeTool:testTool];
    
    STAssertEquals(0U, [[recipe getTools] count], @"removeTool should remove tool");
    STAssertEquals(0U, [[recipe getDirections] count], @"Removing a tool should remove its actions");
}

-(void)testSettingFinishTimeSetsTimeForToolActions{
    Recipe *recipe = [[Recipe alloc] init];
    Tool *testTool = [[Tool alloc] init];
    RecipeAction *testAction = [[RecipeAction alloc] init];
    testAction.time = 400;
    [testTool.actions addObject:testAction];
    
    [recipe addTool:testTool];
    NSDate *now = [NSDate date];
    NSDate *expected = [[NSDate alloc] initWithTimeInterval:-400 sinceDate:now];
    recipe.finishTime = now;
    
    STAssertTrue([testAction.maxStartTime compare:expected] == NSOrderedSame, @"setting a finishTime should set the time on actions added by a tool");
}

-(void) testAddingToolActionIsAffectedByStartTime{
    Recipe *recipe = [[Recipe alloc] init];
    Tool *testTool = [[Tool alloc] init];
    RecipeAction *testAction = [[RecipeAction alloc] init];
    testAction.time = 400;
    [testTool.actions addObject:testAction];
    
    NSDate *now = [NSDate date];
    NSDate *expected = [[NSDate alloc] initWithTimeInterval:-400 sinceDate:now];
    recipe.finishTime = now;
    [recipe addTool:testTool];
    
    STAssertNotNil(testAction.maxStartTime, @"Adding a tool after a finish time is set should not result in a nil startTime");
    STAssertTrue([testAction.maxStartTime compare:expected] == NSOrderedSame, @"setting a finishTime should set the time on actions added by a tool");
}

-(void) testSubstituteTool{
    Recipe *recipe = [[Recipe alloc] init];
    Tool *testTool1 = [[Tool alloc] init];
    Tool *testTool2 = [[Tool alloc] init];
    testTool1.name = @"fork";
    testTool2.name = @"blender";
    RecipeAction *testAction = [[RecipeAction alloc] init];
    testAction.time = 400;
    [testTool2.actions addObject:testAction];
    
    NSDate *now = [NSDate date];
    NSDate *expected = [[NSDate alloc] initWithTimeInterval:-400 sinceDate:now];
    recipe.finishTime = now;
    
    [recipe addTool:testTool1];
    [recipe addTool:testTool2 asSubstituteFor:testTool1];
    [recipe substituteTool:testTool1 with:testTool2];
    Tool *currentTool = [recipe getTools][0];
    
    STAssertEqualObjects(currentTool, testTool2, @"Substituting a tool should swap it");
    STAssertEquals(1U, [[recipe getDirections] count], @"Swapping tools should also swap tool directions, if any");
    STAssertNotNil(testAction.minStartTime, @"Swapping a tool should update the tool's actions' start times");
    STAssertTrue([testAction.minStartTime compare:expected] == NSOrderedSame, @"Swapping a tool should update the tool's actions start times");
}

@end
