//
//  HMCalculateDamageCommand.m
//  KCD
//
//  Created by Hori,Masaki on 2014/05/22.
//  Copyright (c) 2014年 Hori,Masaki. All rights reserved.
//

#import "HMCalculateDamageCommand.h"

#import "HMTemporaryDataStore.h"
#import "HMServerDataStore.h"


#define DAMAGE_CHECK 0

typedef NS_ENUM(NSUInteger, HMBattleType) {
	typeNormal = 0,
	typeCombinedAir,
	typeCombinedWater,
};

@interface HMCalculateDamageCommand ()
@property (nonatomic, strong) HMTemporaryDataStore *store;
@property HMBattleType battleType;
@end

@implementation HMCalculateDamageCommand

- (id)init
{
	self = [super init];
	if(self) {
		_store = [HMTemporaryDataStore oneTimeEditor];
	}
	return self;
}

- (void)resetBattle
{
	NSManagedObjectContext *moc = self.store.managedObjectContext;
	
	NSError *error = nil;
	NSArray *array = [self.store objectsWithEntityName:@"Battle"
										predicate:nil
											error:&error];
	if(error) {
		[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
		return;
	}
	for(id object in array) {
		[moc deleteObject:object];
	}
		
	[self.store saveAction:nil];
}

- (void)resetDamage
{
	NSManagedObjectContext *moc = self.store.managedObjectContext;
	
	NSError *error = nil;
	NSArray *array = [self.store objectsWithEntityName:@"Damage"
							   predicate:nil
								   error:&error];
	if(error) {
		[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
		return;
	}
	for(id object in array) {
		[moc deleteObject:object];
	}
		
	[self.store saveAction:nil];
}

- (void)startBattle
{
	NSManagedObjectContext *moc = self.store.managedObjectContext;
	
	// Battleエンティティ作成
	id battle = [NSEntityDescription insertNewObjectForEntityForName:@"Battle"
											  inManagedObjectContext:moc];
	
	[battle setValue:@([[self.arguments valueForKey:@"api_deck_id"] integerValue]) forKeyPath:@"deckId"];
	[battle setValue:@([[self.arguments valueForKey:@"api_maparea_id"] integerValue]) forKeyPath:@"mapArea"];
	[battle setValue:@([[self.arguments valueForKey:@"api_mapinfo_no"] integerValue]) forKeyPath:@"mapInfo"];
	[battle setValue:@([[self.json valueForKeyPath:@"api_data.api_no"] integerValue]) forKeyPath:@"no"];
	
	[self.store saveAction:nil];
}

- (void)nextCell
{
	NSError *error = nil;
	NSArray *battles = [self.store objectsWithEntityName:@"Battle"
											   predicate:nil
												   error:&error];
	if(error) {
		[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
	}
	if(battles.count == 0) {
		NSLog(@"Battle is invalid.");
		return;
	}
	id battle = battles[0];
	
	[battle setValue:@([[self.json valueForKeyPath:@"api_data.api_no"] integerValue]) forKeyPath:@"no"];
	
	[self.store saveAction:nil];
}

- (NSMutableArray *)damages
{
	NSManagedObjectContext *moc = self.store.managedObjectContext;
	
	NSError *error = nil;
	NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"id" ascending:YES];
	NSArray *array = [self.store objectsWithEntityName:@"Damage"
									   sortDescriptors:@[sortDescriptor]
											 predicate:nil
												 error:&error];
	if(error) {
		[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
	}
	// TODO: error handling
	
	NSInteger frendShipCount = 12;
	if(array.count != frendShipCount) {
		// Battleエンティティ取得
		error = nil;
		NSArray *battles = [self.store objectsWithEntityName:@"Battle"
												   predicate:nil
													   error:&error];
		if(error) {
			[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
		}
		if(battles.count == 0) {
			NSLog(@"Battle is invalid.");
			return [NSMutableArray new];
		}
		id battle = battles[0];
		
		// Damage エンティティ作成6個
		NSMutableArray *damages = [NSMutableArray new];
		for(NSInteger i = 0; i < frendShipCount; i++) {
			id damage = [NSEntityDescription insertNewObjectForEntityForName:@"Damage"
													  inManagedObjectContext:moc];
			[damage setValue:battle forKeyPath:@"battle"];
			[damage setValue:@(i) forKeyPath:@"id"];
			[damages addObject:damage];
		}
		array = damages;
	}
	
	return [NSMutableArray arrayWithArray:array];
}

- (void)calculateHougeki:(NSMutableArray *)damages targetsKeyPath:(NSString *)targetKeyPath damageKeyPath:(NSString *)damageKeyPath
{
#if DAMAGE_CHECK
	NSLog(@"Start Hougeki %@", targetKeyPath);
#endif
	id targetShips = [self.json valueForKeyPath:targetKeyPath];
	if(!targetShips || [targetShips isKindOfClass:[NSNull class]]) return;
	
	id hougeki1Damages = [self.json valueForKeyPath:damageKeyPath];
	NSInteger i = 0;
	NSInteger offset = self.calcSecondFleet ? 6 : 0;
	for(NSArray *array in targetShips) {
		if(![array isKindOfClass:[NSArray class]]) {
			i++;
			continue;
		}
		
		NSInteger j = 0;
		for(id ship in array) {
			NSInteger target = [ship integerValue];
#if DAMAGE_CHECK
			NSLog(@"Hougeki target -> %ld", target + offset);
#endif
			if(target < 0 || target > 6) {
				j++;
				continue;
			}
			
			id damageObject = [damages objectAtIndex:target - 1 + offset];
			NSInteger damage = [[[hougeki1Damages objectAtIndex:i] objectAtIndex:j] integerValue];
			damage += [[damageObject valueForKey:@"damage"] integerValue];
			[damageObject setValue:@(damage) forKeyPath:@"damage"];
			
#if DAMAGE_CHECK
			NSLog(@"Hougeki %ld -> %ld", target + offset, damage);
#endif
			
			j++;
		}
		i++;
	}
}

- (void)calculateFDam:(NSMutableArray *)damages fdamKeyPath:(NSString *)fdamKeyPath
{
#if DAMAGE_CHECK
	NSLog(@"Start FDam %@", fdamKeyPath);
#endif
	id koukuDamage = [self.json valueForKeyPath:fdamKeyPath];
	if(!koukuDamage || [koukuDamage isEqual:[NSNull null]]) return;
	
	NSInteger offset = self.calcSecondFleet ? 6 : 0;
	for(NSInteger i = 1; i <= 6; i++) {
		id damageObject = [damages objectAtIndex:i - 1 + offset];
		NSInteger damage = [[koukuDamage objectAtIndex:i] integerValue];
		damage += [[damageObject valueForKey:@"damage"] integerValue];
		[damageObject setValue:@(damage) forKeyPath:@"damage"];
		
#if DAMAGE_CHECK
		NSLog(@"FDam %ld -> %ld", i + offset, damage);
#endif
	}
}

- (BOOL)isCombinedBattle
{
	return [self.api hasPrefix:@"/kcsapi/api_req_combined_battle"];
}

- (void)calculateBattle
{
	// 艦隊のチェック
	
	// Damage エンティティ作成6個
	NSMutableArray *damages = [self damages];
	
	// koukuu
	[self calculateFDam:damages
			fdamKeyPath:@"api_data.api_kouku.api_stage3.api_fdam"];
	
	if(self.isCombinedBattle) {
		[self calculateFDam:damages
				fdamKeyPath:@"api_data.api_kouku2.api_stage3.api_fdam"];
		
		self.calcSecondFleet = YES;
		[self calculateFDam:damages
				fdamKeyPath:@"api_data.api_kouku.api_stage3_combined.api_fdam"];
		[self calculateFDam:damages
				fdamKeyPath:@"api_data.api_kouku2.api_stage3_combined.api_fdam"];
		self.calcSecondFleet = NO;
	} else {
		[self calculateFDam:damages
				fdamKeyPath:@"api_data.api_kouku2.api_stage3.api_fdam"];
	}
	
	// opening attack
	self.calcSecondFleet = self.isCombinedBattle;
	[self calculateFDam:damages
			fdamKeyPath:@"api_data.api_opening_atack.api_fdam"];
	self.calcSecondFleet = NO;
	
	// hougeki1
	self.calcSecondFleet = self.isCombinedBattle && self.battleType == typeCombinedAir;
	[self calculateHougeki:damages
			targetsKeyPath:@"api_data.api_hougeki1.api_df_list"
			 damageKeyPath:@"api_data.api_hougeki1.api_damage"];
	self.calcSecondFleet = NO;
	
	// hougeki2
	[self calculateHougeki:damages
			targetsKeyPath:@"api_data.api_hougeki2.api_df_list"
			 damageKeyPath:@"api_data.api_hougeki2.api_damage"];
	
	// hougeki3
	self.calcSecondFleet = self.isCombinedBattle && self.battleType == typeCombinedWater;
	[self calculateHougeki:damages
			targetsKeyPath:@"api_data.api_hougeki3.api_df_list"
			 damageKeyPath:@"api_data.api_hougeki3.api_damage"];
	self.calcSecondFleet = NO;

	// raigeki
	self.calcSecondFleet = self.isCombinedBattle;
	[self calculateFDam:damages
			fdamKeyPath:@"api_data.api_raigeki.api_fdam"];
	self.calcSecondFleet = NO;
	
	[self.store saveAction:nil];
}

- (void)calculateMidnightBattle
{
	// Damage 取得
	NSMutableArray *damages = [self damages];
	
	// hougeki
	self.calcSecondFleet = self.isCombinedBattle;
	[self calculateHougeki:damages
			targetsKeyPath:@"api_data.api_hougeki.api_df_list"
			 damageKeyPath:@"api_data.api_hougeki.api_damage"];
	self.calcSecondFleet = NO;
	
	[self.store saveAction:nil];
}

- (NSArray *)deckWithNumber:(NSNumber *)number
{
	return [NSArray array];
}
- (void)applyDamage
{
	// Damage 取得
	NSArray *damages = nil;
	
	NSError *error = nil;
	NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"id" ascending:YES];
	damages = [self.store objectsWithEntityName:@"Damage"
								sortDescriptors:@[sortDescriptor]
									  predicate:nil
										  error:&error];
	if(error) {
		[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
		return;
	}
	
	if(damages.count != 12) {
		[self log:@"Damage is invalid. count %lx", damages.count];
		return;
	}
	
	error = nil;
	NSArray *array = [self.store objectsWithEntityName:@"Battle"
										predicate:nil
											error:&error];
	if(error) {
		[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
		return;
	}
	if(array.count == 0) {
		[self log:@"Battle is invalid. %s", __PRETTY_FUNCTION__];
		return;
	}
	
	HMServerDataStore *serverStore = [HMServerDataStore oneTimeEditor];
	NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %@", [array[0] valueForKey:@"deckId"]];
	BOOL firstRun = YES;
	for(NSInteger i = 0; i < 2; i++) {
		// 艦隊メンバーを取得
		error = nil;
		NSArray *decks = [serverStore objectsWithEntityName:@"Deck"
												  predicate:predicate
													  error:&error];
		if(error) {
			[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
			return;
		}
		
		if(decks.count == 0) {
			[self log:@"Deck is invalid. %s", __PRETTY_FUNCTION__];
			return;
		}
		id deck = decks[0];
		NSMutableArray *shipIds = [NSMutableArray new];
		[shipIds addObject:[deck valueForKey:@"ship_0"]];
		[shipIds addObject:[deck valueForKey:@"ship_1"]];
		[shipIds addObject:[deck valueForKey:@"ship_2"]];
		[shipIds addObject:[deck valueForKey:@"ship_3"]];
		[shipIds addObject:[deck valueForKey:@"ship_4"]];
		[shipIds addObject:[deck valueForKey:@"ship_5"]];
		
		NSMutableArray *ships = [NSMutableArray new];
		for(id shipId in shipIds) {
			error = nil;
			NSArray *ship = [serverStore objectsWithEntityName:@"Ship"
														 error:&error
											   predicateFormat:@"id = %@", @([shipId integerValue])];
			if(error) {
				[self log:@"%s error: %@", __PRETTY_FUNCTION__, error];
			}
			if(ship.count != 0 && ![ship[0] isEqual:[NSNull null]]) {
				[ships addObject:ship[0]];
			}
		}
		
		NSUInteger shipCount = ships.count;
		NSUInteger offset = (self.isCombinedBattle && !firstRun) ? 6 : 0;
		for(NSInteger i = 0; i < shipCount; i++) {
			id ship = ships[i];
			NSInteger damage = [[damages[i + offset] valueForKey:@"damage"] integerValue];
			NSInteger nowhp = [[ship valueForKey:@"nowhp"] integerValue];
			nowhp -= damage;
			[ship setValue:@(nowhp) forKeyPath:@"nowhp"];
		}
		
		predicate = [NSPredicate predicateWithFormat:@"id = %@", @2];
		firstRun = NO;
		
		if(!self.isCombinedBattle) break;
	}
	
	[serverStore saveAction:nil];
}

- (void)execute
{
	if([self.api isEqualToString:@"/kcsapi/api_req_map/start"]) {
		[self resetBattle];
		[self startBattle];
		return;
	}
	if([self.api isEqualToString:@"/kcsapi/api_req_map/next"]) {
		[self nextCell];
		return;
	}
	
	if([self.api isEqualToString:@"/kcsapi/api_req_sortie/battle"]) {
		[self calculateBattle];
		return;
	}
	if([self.api isEqualToString:@"/kcsapi/api_req_sortie/airbattle"]) {
		[self calculateBattle];
		return;
	}
	if([self.api isEqualToString:@"/kcsapi/api_req_battle_midnight/battle"]
	   || [self.api isEqualToString:@"/kcsapi/api_req_battle_midnight/sp_midnight"]) {
		[self calculateMidnightBattle];
		return;
	}
	
	/*
	 /kcsapi/api_req_combined_battle/battle_water
	 /kcsapi/api_req_combined_battle/midnight_battle
	 /kcsapi/api_req_combined_battle/battleresult
	 */
	// combined battle
	if([self.api isEqualToString:@"/kcsapi/api_req_combined_battle/battle"]
	   || [self.api isEqualToString:@"/kcsapi/api_req_combined_battle/airbattle"]) {
		self.battleType = typeCombinedAir;
		[self calculateBattle];
		return;
	}
	if([self.api isEqualToString:@"/kcsapi/api_req_combined_battle/battle_water"]) {
	   self.battleType = typeCombinedWater;
		[self calculateBattle];
		return;
	}
	
	if([self.api isEqualToString:@"/kcsapi/api_req_combined_battle/midnight_battle"]
	   || [self.api isEqualToString:@"/kcsapi/api_req_combined_battle/sp_midnight"]) {
		[self calculateMidnightBattle];
		return;
	}
	
	if([self.api isEqualToString:@"/kcsapi/api_req_sortie/battleresult"]
	   || [self.api isEqualToString:@"/kcsapi/api_req_combined_battle/battleresult"]) {
		[self applyDamage];
		[self resetDamage];
		return;
	}
	
	NSLog(@"undefined battle type");
}

@end
