<?php
App::uses('ModelBehavior', 'Model');

App::uses('Modifier', 'Model');
App::uses('Safeguard', 'Model');
App::uses('ProgramChildPlacement', 'Model');

class AddRaceBehavior extends ModelBehavior
{
	private $_taperLength = 4;
	private $_race;
	private $_raceId;
	private $_sportId;
	private $_heartRates;
	private $_programId;

	public function setup(Model $Model, $settings = array())
	{
		$this->ProgramChildPlacement = new ProgramChildPlacement();
		$this->Safeguard = new Safeguard();
		$this->Modifier = new Modifier();
	}

	public function addRaceWeek(Model $Model, $programId, $programChildId, $taperLength = 4, $date, $heartRates, $raceId, $sportId = 0)
	{
		$this->_programId = $programId;
		$this->_heartRates = $heartRates;
		$this->_race = new DateTime($date);
		$this->_taperLength = $taperLength;
		$this->_raceId = $raceId;
		$this->_sportId = $sportId;

		$date = clone $this->_race;

		$edWorkouts = $this->_generate10DaysEdWorkouts($Model, $date->format('Y-n-j'));
		$placementWorkouts = $this->_generatePlacementWorkouts($Model);

		$upWorkouts = $this->_generateUpWorkouts($Model);
		$spWorkouts = $this->_generateSpWorkouts($Model, $this->getTrainingweek($Model, $this->_race->format('Y-n-j'), $this->_programId));



		$workouts = $spWorkouts;

		foreach($edWorkouts as $edWorkout)
		{
			array_push($workouts, $edWorkout);
		}

		foreach($placementWorkouts as $placementWorkout)
		{
			array_push($workouts, $placementWorkout);
		}

		foreach($upWorkouts as $upWorkout)
		{
			array_push($workouts, $upWorkout);
		}

		foreach($workouts as $key => $workout)
		{
			$workouts[$key]['no'] = $key + 1;
			$workouts[$key]['program_child_id'] = $programChildId;
		}

		return $workouts;
	}


	private function _day1(Model $Model, $date, $day)
	{
		$sport = $Model->Sport->find('first', array(
			'conditions' => array(
				'Sport.id' => $this->_sportId,
			),
			'recursive' => -1,
		));

		$taperRace = 'addrace-taper-race';

		if(!empty($sport['Sport']['value']))
		{
			switch ($sport['Sport']['value'])
			{
				case 'bike' :
					$taperRace = 'addrace-taper-race-bike';
					breaK;
				case 'run' :
					$taperRace = 'addrace-taper-race-run';
					breaK;
			}
		}

		$placements = $this->ProgramChildPlacement->find('all', array(
			'conditions' => array(
				'ProgramChildPlacementType.value' => $taperRace,
				'ProgramChildPlacement.taper_length' => $this->_taperLength,
				'ProgramChildPlacement.race_id' => $this->_raceId,
				'ProgramChildPlacement.day' => $day,
			),
			'contain' => array(
				'ProgramChildPlacementType',
				'TassPlacementType' => array(
					'Zone',
				),
				'Sport',
			),
		));

		foreach($placements as $key => $placement)
		{
			$placements[$key]['ProgramChildPlacement']['date'] = $date;
		}

		return $placements;
	}

	private function _days(Model $Model, $date, $day)
	{
		if ($day == 1)
		{
			throw new NotFoundException(__('Invalid Day'));
		}

		$placements = $this->ProgramChildPlacement->find('all', array(
			'conditions' => array(
				'ProgramChildPlacementType.value' => 'addrace-taper-race',
				'ProgramChildPlacement.day' => $day,
				'ProgramChildPlacement.race_id' => $this->_raceId,
				'ProgramChildPlacement.taper_length' => $this->_taperLength,
			),
			'contain' => array(
				'ProgramChildPlacementType',
				'TassPlacementType',
				'Sport',
			),
		));

		foreach($placements as $key => $placement)
		{
			$placements[$key]['ProgramChildPlacement']['date'] = $date;
		}

		return $placements;
	}

	public function getSportId(Model $Model, $sportValue = null)
	{
		$sport = $Model->Sport->find('first', array(
			'conditions' => array(
				'Sport.value' => $sportValue,
			),
			'recursive' => -1,
		));

		if (!empty($sport))
		{
			return $sport['Sport']['id'];
		}
		else
		{
			return 0;
		}
	}

	public function getRaceId(Model $Model, $raceValue = null, $raceDistance = 1)
	{
		$race = $Model->Race->find('first', array(
			'conditions' => array(
				'Race.value' => $raceValue,
			),
			'recursive' => -1,
		));

		if (!empty($race))
		{
				return $race['Race']['id'];
		}
		else
		{
			$sport = $Model->Sport->find('first', array(
				'conditions' => array(
					'Sport.value' => $raceValue,
				),
				'recursive' => -1,
			));

			if(!empty($sport))
			{
				$safeguard = $this->Safeguard->find('first', array(
					'conditions' => array(
						'SafeguardType.value' => 'addrace',
						'Safeguard.value <=' => $raceDistance,
						'Safeguard.sport_id' => $sport['Sport']['id'],
					),
					'order' => array(
						'Safeguard.value' => 'desc',
					),
				));

				if(!empty($safeguard))
				{
					return $safeguard['Race']['id'];
				}
				else
				{
					throw new NotFoundException(__('Invalid Safeguard'));
				}
			}
			else
			{
				throw new NotFoundException(__('Invalid Sport'));
			}
		}
	}

	private function _generatePlacementWorkouts(Model $Model)
	{
		$placementWorkouts = array();
		$days = array();

		$date = clone $this->_race;

		for($day = 1 ; $day <= $this->_taperLength ; $day++)
		{
			$date->modify('-1 day');

			if($day === 1)
			{
				$days[$day] = $this->_day1($Model, $date->format('Y-n-j'), $day);
			}
			elseif($day <= 4)
			{
				$days[$day] = $this->_days($Model, $date->format('Y-n-j'), $day);
			}
			elseif($day <= 6 && $this->_taperLength == 10)
			{
				$days[$day] = $this->_days($Model, $date->format('Y-n-j'), $day);
			}
		}

		foreach($days as $placements)
		{
			foreach($placements as $placement)
			{
				$zoneId = $placement['TassPlacementType']['zone_id'];
				$date = $placement['ProgramChildPlacement']['date'];
				$heartMin = $zoneId ? $this->_heartRates['Zone'][$zoneId]['min'] : null;
				$heartMax = $zoneId ? $this->_heartRates['Zone'][$zoneId]['max'] : null;


				$placementWorkout = array(
					'trainingweek' => $this->getTrainingweek($Model, $date, $this->_programId),
					'heart_min' => $heartMin,
					'heart_max' => $heartMax,
					'value' => $placement['ProgramChildPlacement']['value'],
					'visible' => 1,
					'sport_id' => $placement['ProgramChildPlacement']['sport_id'],
					'zone_id' => ($placement['Sport']['value'] != 'strength') ? $zoneId : null,
					'tass_placement_type_id' => $placement['ProgramChildPlacement']['tass_placement_type_id'],
					'taper' => 1,
					'date' => $date,
				);

				array_push($placementWorkouts, $placementWorkout);
			}
		}

		return $placementWorkouts;
	}

	private function _generateUpWorkouts(Model $Model)
	{
		$trainingweek = $this->getTrainingweek($Model, $this->_race->format('Y-n-j'), $this->_programId);
		$nextTrainingweek = $trainingweek + 1;

		$program = $Model->Program->find('first', array(
			'conditions' => array(
				'Program.id' => $this->_programId,
			),
			'recursive' => -1,
		));

		//if comming out of race taper up will happen on main program race week don't generate any
		if ($program['Program']['training_length'] <= $nextTrainingweek)
		{
			return array();
		}

		$staticUpWorkouts = $this->_staticUpWorkouts($Model);

		//check if the second week of comming out of race taper up needs to be calulated
		if ($program['Program']['training_length'] == ($trainingweek + 2))
		{
			$dynamicUpWorkouts = $this->_dynamicUpWorkouts($Model, false);
		}
		else
		{
			$dynamicUpWorkouts = $this->_dynamicUpWorkouts($Model);
		}


		//wednesday is a very complicated day for taper up since dynamic and static are happening at this very day at the same time,
		//this code is only here to search and remove double entries for this very day.
		$workouts = array();
		$nextWednesday = clone $this->_race; $nextWednesday->modify('next wednesday');
		$dynamicDate = clone $this->_race;
		$staticDate = clone $this->_race;

		foreach($dynamicUpWorkouts as $key => $dynamicUpWorkout)
		{
			$dynamicDate->modify($dynamicUpWorkout['date']);

			if($dynamicDate == $nextWednesday)
			{
				foreach($staticUpWorkouts as $staticUpWorkout)
				{
					$staticDate->modify($staticUpWorkout['date']);

					if($dynamicDate == $staticDate && $dynamicUpWorkout['sport_id'] == $staticUpWorkout['sport_id'])
					{
						unset($dynamicUpWorkouts[$key]);
					}
				}
			}
		}

		$workouts = $staticUpWorkouts;

		foreach($dynamicUpWorkouts as $dynamicUpWorkout)
		{
			array_push($workouts, $dynamicUpWorkout);
		}

		$workouts = $this->_addStaticTaperUpAttribute($Model, $workouts, $trainingweek + 1);

		return $workouts;
	}

	private function _addStaticTaperUpAttribute($Model, $workouts, $trainingweek)
	{
		$nextWeek = clone $this->_race; $nextWeek->modify('next monday');
		$index = clone $nextWeek;

		$race = $Model->Race->find('first', array(
			'conditions' => array(
				'Race.id' => $this->_raceId,
			),
			'recursive' => -1,
		));

		$sports = $Model->Sport->find('all', array(
			'conditions' => array(
			),
			'recursive' => -1,
		));

		$rest = $Model->Program->Workout->TassPlacementType->find('first', array(
			'conditions' => array(
				'TassPlacementType.value' => 'rest',
			),
		));

		//only the first 2 days should get the static taper up attribute
		if($race['Race']['value'] == 'sprint' | $race['Race']['value'] == 'olympic')
		{
			$end = clone $nextWeek; $end->modify('+1 day');
		}
		//the first 3 days
		else
		{
			$end = clone $nextWeek; $end->modify('+2 days');
		}

		$workoutDay = new DateTime($index->format('Y-n-j'));

		while($index <= $end)
		{
			$sportIds = array();

			foreach($workouts as $key => $workout)
			{
				$workoutDay->modify($workout['date']);

				if($index == $workoutDay)
				{
					$workouts[$key]['taper'] = 1;
					array_push($sportIds, $workout['sport_id']);
				}
			}

			//check if on this day we are missing a sport workout
			if(count($sportIds) < 4)
			{
				foreach($sports as $sport)
				{
					if(in_array($sport['Sport']['id'], $sportIds))
					{
						array_push($workouts, array(
							'trainingweek' => $trainingweek,
							'visible' => 1,
							'sport_id' => $sport['Sport']['id'],
							'tass_placement_type_id' => $rest['TassPlacementType']['id'],
							'date' => $index->format('Y-m-d'),
						));
					}
				}
			}

			$index->modify('next day');
		}

		return $workouts;
	}

	private function _dynamicAsWorkouts(Model $Model, $edWorkouts, $trainingweek)
	{
		$workouts = array();

		$asModifiers = $this->Modifier->find('all', array(
			'conditions' => array(
				'ModifierType.value' => 'zone',
			 	'Zone.value' => 'aerobic_stimulation',
			),
		));

		$speedTempoWorkouts = $Model->Program->Workout->find('all', array(
			'conditions' => array(
				'Workout.trainingweek' => $trainingweek,
				'Workout.program_id' => $this->_programId,
				'or' => array(
					array('Zone.value' => 'speed'),
					array('Zone.value' => 'tempo'),
				),
			),
		));

		foreach($speedTempoWorkouts as $speedTempoWorkout)
		{
			foreach($asModifiers as $asModifier)
			{
				if($speedTempoWorkout['Sport']['value'] == $asModifier['Sport']['value'])
				{
					foreach($edWorkouts as $edWorkout)
					{
						if($speedTempoWorkout['Sport']['value'] == $edWorkout['Sport']['value'])
						{
							$workout = $this->_cloneWorkout($speedTempoWorkout);
							$workout['Workout']['value'] = $this->_roundDown($edWorkout['Workout']['value'], $asModifier['Modifier']['down'], $speedTempoWorkout['Sport']['round_at']);
							$workout['Workout']['zone_id'] = $asModifier['Zone']['id'];
						}
					}

					if(!isset($workouts[$workout['Workout']['date']]))
					{
						$workouts[$workout['Workout']['date']] = array();
					}

					array_push($workouts[$workout['Workout']['date']], $workout['Workout']);
				}
			}
		}

		return $workouts;
	}

	private function _dynamicAdReWorkouts(Model $Model, $trainingweek)
	{
		$workouts = array();

		$adReWorkouts = $Model->Program->Workout->find('all', array(
			'conditions' => array(
				'Workout.trainingweek' => $trainingweek,
				'Workout.program_id' => $this->_programId,
				'or' => array(
					array('Zone.value' => 'recovery'),
					array('Zone.value' => 'aerobic_development'),
				),
			),
		));

		foreach($adReWorkouts as $key => $adReWorkout)
		{
			$adReWorkout = $this->_cloneWorkout($adReWorkout);

			if(!isset($workouts[$adReWorkout['Workout']['date']]))
			{
				$workouts[$adReWorkout['Workout']['date']] = array();
			}

			array_push($workouts[$adReWorkout['Workout']['date']], $adReWorkout['Workout']);
		}

		return $workouts;
	}

	private function _dynamicEdWorkouts(Model $Model, $edWorkouts, $edModifiers, $trainingweek)
	{
		$workouts = array();

		foreach($edWorkouts as $key => $edWorkout)
		{
			foreach($edModifiers as $edModifier)
			{
				if($edWorkout['Sport']['value'] == $edModifier['Sport']['value'] && $edWorkout['Workout']['trainingweek'] == $trainingweek)
				{
					$edWorkouts[$key]['Workout']['value'] = $this->_roundDown($edWorkout['Workout']['value'], $edModifier['Modifier']['down'], $edModifier['Sport']['round_at']);
					$edWorkouts[$key] = $this->_cloneWorkout($edWorkouts[$key]);

					if(!isset($workouts[$edWorkouts[$key]['Workout']['date']]))
					{
						$workouts[$edWorkouts[$key]['Workout']['date']] = array();
					}

					array_push($workouts[$edWorkouts[$key]['Workout']['date']], $edWorkouts[$key]['Workout']);
				}
			}
		}

		return $workouts;
	}

	private function _dynamicTaperDown(Model $Model, $workoutsByDates, $modifiers)
	{
		foreach($workoutsByDates as $date => $workouts)
		{
			foreach($workouts as $key => $workout)
			{
				foreach($modifiers as $modifier)
				{
					if($workout['sport_id'] == $modifier['Modifier']['sport_id'])
					{
						$value = $this->_roundDown($workout['value'], $modifier['Modifier']['down'], $modifier['Sport']['round_at']);

						if($value)
						{
							$workout['value'] = $value;
						}

						$workoutsByDates[$date][$key] = $workout;
					}
				}
			}
		}

		return $workoutsByDates;
	}

	private function _dynamicMerge(Model $Model, $dynamicWorkoutsByDates, $workoutsByDates)
	{
		foreach($workoutsByDates as $date => $workouts)
		{
			foreach($workouts as $workout)
			{
				if(!isset($dynamicWorkoutsByDates[$date]))
				{
					$dynamicWorkoutsByDates[$date] = array();
				}

				array_push($dynamicWorkoutsByDates[$date], $workout);
			}
		}

		return $dynamicWorkoutsByDates;
	}

	private function _dynamicStrengthWorkouts(Model $Model, $trainingweek)
	{
		$workouts = array();

		$race = $Model->Race->find('first', array(
			'conditions' => array(
				'Race.id' => $this->_raceId,
			),
			'recursive' => -1,
		));

		$tustr1 = $Model->Program->Workout->TassPlacementType->find('first', array(
			'conditions' => array(
				'TassPlacementType.value' => 'tustr1',
			),
		));

		$rest = $Model->Program->Workout->TassPlacementType->find('first', array(
			'conditions' => array(
				'TassPlacementType.value' => 'rest',
			),
		));

		if($race['Race']['value'] == 'sprint' || $race['Race']['value'] == 'olympic')
		{

			$strengthWorkouts = $Model->Program->Workout->find('all', array(
				'conditions' => array(
					'Workout.trainingweek' => $trainingweek,
					'Workout.program_id' => $this->_programId,
					'Sport.value' => 'strength',
				),
				'order' => array(
					'Workout.date' => 'asc',
				),
			));

			foreach ($strengthWorkouts as $key => $strengthWorkout)
			{
				$strengthWorkout = $this->_cloneWorkout($strengthWorkout);
				unset($strengthWorkout['Workout']['strength_type_id']);

				if(!isset($workouts[$strengthWorkout['Workout']['date']]))
				{
					$workouts[$strengthWorkout['Workout']['date']] = array();
				}

				//first strength workout in trainingweek for spring and olympic needs to be rest
				if($key === 0)
				{
					$strengthWorkout['Workout']['tass_placement_type_id'] = $rest['TassPlacementType']['id'];

				}
				else
				{
					$strengthWorkout['Workout']['tass_placement_type_id'] = $tustr1['TassPlacementType']['id'];
					$strengthWorkout['Workout']['zone_id'] = $tustr1['Zone']['id'];
				}

				array_push($workouts[$strengthWorkout['Workout']['date']], $strengthWorkout['Workout']);
			}
		}
		elseif($race['Race']['value'] == 'half-ironman')
		{
			$tustr2 = $Model->Program->Workout->TassPlacementType->find('first', array(
				'conditions' => array(
					'TassPlacementType.value' => 'tustr2',
				),
			));

			$strengthWorkouts = $Model->Program->Workout->find('all', array(
				'conditions' => array(
					'or' => array(
						array('Workout.trainingweek' => $trainingweek),
						array('Workout.trainingweek' => $trainingweek + 1),
					),
					'Workout.program_id' => $this->_programId,
					'Sport.value' => 'strength',
				),
				'order' => array(
					'Workout.date' => 'asc',
				),
			));

			foreach ($strengthWorkouts as $key => $strengthWorkout)
			{
				$strengthWorkout = $this->_cloneWorkout($strengthWorkout);
				unset($strengthWorkout['Workout']['strength_type_id']);

				if(!isset($workouts[$strengthWorkout['Workout']['date']]))
				{
					$workouts[$strengthWorkout['Workout']['date']] = array();
				}

				//first strength workout in trainingweek for spring and olympic needs to be rest
				if ($key === 0 || $key === 1)
				{
					$strengthWorkout['Workout']['tass_placement_type_id'] = $rest['TassPlacementType']['id'];
				}
				elseif ($key === 2)
				{
					$strengthWorkout['Workout']['tass_placement_type_id'] = $tustr1['TassPlacementType']['id'];
					$strengthWorkout['Workout']['zone_id'] = $tustr1['Zone']['id'];
				}
				else
				{
					$strengthWorkout['Workout']['tass_placement_type_id'] = $tustr2['TassPlacementType']['id'];
					$strengthWorkout['Workout']['zone_id'] = $tustr2['Zone']['id'];
				}

				array_push($workouts[$strengthWorkout['Workout']['date']], $strengthWorkout['Workout']);
			}
		}

		return $workouts;
	}

	private function _dynamicUpWorkouts(Model $Model, $secondWeek = true)
	{
		$nextWeek = clone $this->_race; $nextWeek->modify('next monday');
		$nextTrainingweek = $this->getTrainingweek($Model, $nextWeek->format('Y-n-j'), $this->_programId);

		$dynamicAs = array();
		$dynamicAdRe = array();
		$dynamicEd = array();
		$dynamicEdWeek2 = array();

		$edWorkouts = $Model->Program->Workout->find('all', array(
			'conditions' => array(
				'or' => array(
					array('Workout.trainingweek' => $nextTrainingweek),
					array('Workout.trainingweek' => $nextTrainingweek + 1),
				),
				'Workout.program_id' => $this->_programId,
				'Zone.value' => 'endurance_development',
			),
		));

		if(count($edWorkouts) < 3)
		{
			throw new NotFoundException(__('Not enough Endurance Development Workouts for this trainingweek'));
		}

		$modifiers = $this->Modifier->find('all', array(
			'conditions' => array(
				'ModifierType.value' => 'comming-out-of-addrace',
				'Race.id' => $this->_raceId,
			),
		));

		$dynamicAs = $this->_dynamicAsWorkouts($Model, $edWorkouts, $nextTrainingweek);
		$dynamicStrength = $this->_dynamicStrengthWorkouts($Model, $nextTrainingweek);

		//if olympic or half-ironman
		if(!empty($modifiers))
		{
			$dynamicAdRe = $this->_dynamicAdReWorkouts($Model, $nextTrainingweek);


			$dynamicAdRe = $this->_dynamicTaperDown($Model, $dynamicAdRe, $modifiers);
			$dynamicAs = $this->_dynamicTaperDown($Model, $dynamicAs, $modifiers);

			if($modifiers[0]['Race']['value'] == 'olympic')
			{
				$dynamicEd = $this->_dynamicEdWorkouts($Model, $edWorkouts, $modifiers, $nextTrainingweek);
			}
			elseif($modifiers[0]['Race']['value'] == 'half-ironman')
			{
				$edModifiers = $this->Modifier->find('all', array(
					'conditions' => array(
						'ModifierType.value' => 'comming-out-of-addrace-ed',
						'Race.id' => $this->_raceId,
					),
				));


				$dynamicEd = $this->_dynamicEdWorkouts($Model, $edWorkouts, $modifiers, $nextTrainingweek);

				if($secondWeek)
				{
					$dynamicEdWeek2 = $this->_dynamicEdWorkouts($Model, $edWorkouts, $edModifiers, $nextTrainingweek + 1);
				}
			}
		}

		$dynamicWorkouts = array();

		$dynamicWorkouts = $this->_dynamicMerge($Model, $dynamicWorkouts, $dynamicAs);
		$dynamicWorkouts = $this->_dynamicMerge($Model, $dynamicWorkouts, $dynamicAdRe);
		$dynamicWorkouts = $this->_dynamicMerge($Model, $dynamicWorkouts, $dynamicEd);
		$dynamicWorkouts = $this->_dynamicMerge($Model, $dynamicWorkouts, $dynamicEdWeek2);



		$check = clone $nextWeek;
		$staticStart = clone $nextWeek;
		//$staticEnd = clone $nextWeek; $staticEnd->modify('next wednesday');
		$staticEnd = clone $nextWeek; $staticEnd->modify('next tuesday');
		#$dynamicStart = clone $nextWeek; $dynamicStart->modify('next thursday');
		$dynamicEnd = clone $nextWeek;
		$end = clone $nextWeek; $end->modify('next week'); $end->modify('next sunday');


		//if not set, sprint since there are no sprint modifiers for addrace
		if(!empty($modifiers))
		{
			if($modifiers[0]['Race']['value'] == 'half-ironman')
			{
				$dynamicEnd->modify('next week');  $dynamicEnd->modify('next wednesday');
			}
			else
			{
				$dynamicEnd->modify('next sunday');
			}
		}
		else
		{
			$dynamicEnd->modify('next wednesday');
		}

		while($check <= $end)
		{
			$trainingweek = $this->getTrainingweek($Model, $check->format('Y-n-j'), $this->_programId);


			//remove every workout from monday to wednesday because those days we have static workouts
			if($check >= $staticStart && $check <= $staticEnd)
			{
				unset($dynamicWorkouts[$check->format('Y-m-d')]);
			}
			else
			{
				// remove every workout from the dynamic range
				if($check > $dynamicEnd && $check <= $end)
				{
					unset($dynamicWorkouts[$check->format('Y-m-d')]);
				}
			}

			$check->modify('+1 day');
		}

		$dynamicWorkouts = $this->_dynamicMerge($Model, $dynamicWorkouts, $dynamicStrength);

		$workouts = array();

		foreach($dynamicWorkouts as $dynamicWorkout)
		{
			foreach($dynamicWorkout as $workout)
			{
				array_push($workouts, $workout);
			}
		}

		return $workouts;
	}

	private function _staticUpWorkouts(Model $Model)
	{
		$staticUpWorkouts = array();

		$nextWeek = clone $this->_race; $nextWeek->modify('next monday');
		$nextDay = clone $this->_race; $nextDay->modify('next day');

		$placements = $this->ProgramChildPlacement->find('all', array(
			'conditions' => array(
				'ProgramChildPlacementType.value' => 'addrace-taper-up',
			),
			'contain' => array(
				'ProgramChildPlacementType',
				'TassPlacementType' => array(
					'Zone',
				),
				'Sport',
			),
		));


        if ($this->_race->format('l') !== 'Monday') {
    		//fill between raceday and next week monday with rest workouts
    		while($nextDay < $nextWeek)
    		{
    			foreach ($placements as $placement)
    			{
    				if($placement['ProgramChildPlacement']['day'] == 1)
    				{
    					$staticUpWorkout = array(
    						'trainingweek' => $this->getTrainingweek($Model, $nextDay->format('Y-n-j'), $this->_programId),
    						'visible' => 1,
    						'sport_id' => $placement['Sport']['id'],
    						'tass_placement_type_id' => $placement['ProgramChildPlacement']['tass_placement_type_id'],
    						'date' => $nextDay->format('Y-n-j'),
    					);

    					array_push($staticUpWorkouts, $staticUpWorkout);
    				}

    			}

    			$nextDay->modify('next day');
    		}
        }

		//real static
		foreach($placements as $placement)
		{
			$dayInterval = $placement['ProgramChildPlacement']['day'] - 1;
			$date = clone $nextWeek;
			$date->modify('+ ' . $dayInterval . ' days');

			if($placement['TassPlacementType']['value'] == 'rest')
			{
				$staticUpWorkout = array(
					'trainingweek' => $this->getTrainingweek($Model, $date->format('Y-n-j'), $this->_programId),
					'visible' => 1,
					'sport_id' => $placement['Sport']['id'],
					'tass_placement_type_id' => $placement['ProgramChildPlacement']['tass_placement_type_id'],
					'date' => $date->format('Y-n-j'),
				);

				array_push($staticUpWorkouts, $staticUpWorkout);
			}
			else
			{
				$zoneId = $placement['TassPlacementType']['Zone']['id'];

				$staticUpWorkout = array(
					'trainingweek' => $this->getTrainingweek($Model, $date->format('Y-n-j'), $this->_programId),
					'visible' => 1,
					'value' => $placement['ProgramChildPlacement']['value'],
					'heart_min' => ($placement['Sport']['value'] != 'strength') ? $this->_heartRates['Zone'][$zoneId]['min'] : null,
					'heart_max' => ($placement['Sport']['value'] != 'strength') ? $this->_heartRates['Zone'][$zoneId]['max'] : null,
					'sport_id' => $placement['Sport']['id'],
					'zone_id' => $zoneId,
					'tass_placement_type_id' => $placement['ProgramChildPlacement']['tass_placement_type_id'],
					'date' => $date->format('Y-n-j'),
				);

				array_push($staticUpWorkouts, $staticUpWorkout);
			}
		}

		return $staticUpWorkouts;
	}

	private function _generateWeeklySpWorkouts(Model $Model, $trainingweek, $raceId = null)
	{
		$speedWorkout = $Model->Program->Workout->find('first', array(
			'conditions' => array(
				'Workout.program_id' => $this->_programId,
				'Workout.trainingweek' => $trainingweek,
				'Zone.value' => 'speed',
			),
		));

		//If they do not have speed in the week before race week, change the AS to speed workout
		if(!empty($speedWorkout))
		{
			$zones = array(
				array('Zone.value' => 'speed'),
				array('Zone.value' => 'tempo'),
			);

		}
		else
		{
			$zones = array(
				array('Zone.value' => 'aerobic_stimulation'),
				array('Zone.value' => 'tempo'),
			);
		}

		$workouts = $Model->Program->Workout->find('all', array(
			'conditions' => array(
				'Workout.program_id' => $this->_programId,
				'Workout.trainingweek' => $trainingweek,
				'or' => $zones,
			),
		));

		return $workouts;
	}

	private function _generateSpWorkouts(Model $Model, $trainingweek)
	{
		$spWorkouts = array();
		$workouts = array();

		if($this->_taperLength == 4)
		{
			$workouts = $this->_generateWeeklySpWorkouts($Model, $trainingweek - 1);
			$raceId = null;
		}
		else
		{
			$workoutsFirstWeek = $this->_generateWeeklySpWorkouts($Model, $trainingweek - 1, $this->_raceId);

			foreach($workoutsFirstWeek as $workoutFirstWeek)
			{
				array_push($workouts, $workoutFirstWeek);
			}

			$workoutsSecondWeek = $this->_generateWeeklySpWorkouts($Model, $trainingweek - 2, $this->_raceId);

			foreach($workoutsSecondWeek as $workoutSecondWeek)
			{
				array_push($workouts, $workoutSecondWeek);
			}

			$raceId = $this->_raceId;
		}

		$placements = $this->ProgramChildPlacement->find('all', array(
			'conditions' => array(
				'ProgramChildPlacementType.value' => 'addrace-taper-speed',
				'ProgramChildPlacement.race_id' => $raceId,
				'ProgramChildPlacement.taper_length' => $this->_taperLength,
			),
			'contain' => array(
				'ProgramChildPlacementType',
				'TassPlacementType',
				'Sport',
			),
		));

		//need zone id to apply heartrate later on
		$zone = $Model->Program->Workout->Zone->find('first', array(
			'conditions' => array(
				'Zone.value' => 'speed',
			),
			'recursive' => false
		));

		foreach($workouts as $workout)
		{
			foreach($placements as $placement)
			{
				if($workout['Sport']['value'] == $placement['Sport']['value'])
				{
					if($workout['Sport']['value'] == 'swim' && $this->_taperLength == 4)
					{
						if($workout['Workout']['value'] == $placement['ProgramChildPlacement']['value'])
						{
							$workout['Workout']['zone_id'] = $placement['TassPlacementType']['zone_id'];
							$workout['Workout']['tass_placement_type_id'] = $placement['TassPlacementType']['id'];
							$workout = $this->_cloneWorkout($workout);

							array_push($spWorkouts, $workout['Workout']);
						}
					}
					elseif($this->_taperLength == 4)
					{
						$workout['Workout']['zone_id'] = $placement['TassPlacementType']['zone_id'];
						$workout['Workout']['tass_placement_type_id'] = $placement['TassPlacementType']['id'];
						$workout = $this->_cloneWorkout($workout);
						unset($workout['Workout']['value']);

						array_push($spWorkouts, $workout['Workout']);
					}
					else
					{
						//check if placement and trainingweek are the same
						if(($placement['TassPlacementType']['value'] == '10ts1' && $workout['Workout']['trainingweek'] == ($trainingweek - 1)) || ($placement['TassPlacementType']['value'] == '10ts2' && $workout['Workout']['trainingweek'] == ($trainingweek - 2)))
						{
							//bike and run speed workouts has no values and swim speed workouts for 10 days taper length as well
							unset($workout['Workout']['value']);

							$workout['Workout']['zone_id'] = $placement['TassPlacementType']['zone_id'];
							$workout['Workout']['tass_placement_type_id'] = $placement['TassPlacementType']['id'];
							$workout = $this->_cloneWorkout($workout);

							array_push($spWorkouts, $workout['Workout']);
						}
					}
				}
			}

		}

		foreach ( $spWorkouts as $key => $spWorkout ) {
			$spWorkouts[$key]['heart_min'] = $this->_heartRates['Zone'][$zone['Zone']['id']]['min'];
			$spWorkouts[$key]['heart_max'] = $this->_heartRates['Zone'][$zone['Zone']['id']]['max'];
		}

		//$heartMin = $zoneId ? $this->_heartRates['Zone'][$zoneId]['min'] : null;


		return $spWorkouts;
	}

	private function _generate10DaysEdWorkouts(Model $Model)
	{
		if ($this->_taperLength != 10)
		{
			return array();
		}

		$edWorkouts = array();
		$date = clone $this->_race;

		$modifiers = $this->Modifier->find('all', array(
			'conditions' => array(
				'ModifierType.value' => 'addrace',
			),
		));

		for($day = 1 ; $day <= $this->_taperLength ; $day++)
		{
			$date->modify('-1 day');

			if($day > 6)
			{
				$workouts = $Model->Program->Workout->find('all', array(
					'conditions' => array(
						'Workout.program_id' => $this->_programId,
						'Workout.date' => $date->format('Y-n-j'),
						'Zone.value' => 'endurance_development',
					),
					'contain' => array(
						'Zone',
						'Sport',
					),
				));

				foreach($workouts as $workout)
				{
					foreach($modifiers as $modifier)
					{
						if($workout['Sport']['value'] == $modifier['Sport']['value'])
						{
							$workout['Workout']['value'] = $this->_roundDown($workout['Workout']['value'],$modifier['Modifier']['down'], $workout['Sport']['round_at']);
							$workout = $this->_cloneWorkout($workout);

							array_push($edWorkouts, $workout['Workout']);
						}
					}
				}
			}
		}
		return $edWorkouts;
	}

	private function _cloneWorkout($workout)
	{
		unset($workout['Workout']['id']);
		unset($workout['Workout']['no']);
		unset($workout['Workout']['brick']);
		unset($workout['Workout']['complete']);
		unset($workout['Workout']['program_id']);
		unset($workout['Workout']['created']);
		unset($workout['Workout']['modified']);
		unset($workout['Workout']['day']);
		unset($workout['Workout']['month']);
		unset($workout['Workout']['year']);

		return $workout;
	}

	public function getTrainingweek(Model $Model, $date, $programId)
	{
		$date = new DateTime($date);

		$program = $Model->Program->find('first', array(
			'conditions' => array(
				'Program.id' => $programId,
			),
		));

		$training = new DateTime($program['Program']['training']);
		$race = new DateTime($program['Program']['race']);

		if($training <= $date && $race > $date)
		{
			$weekStart = clone $training;
			$weekEnd = clone $training; $weekEnd->modify( '+1 week' );

			for ($index = 1; $index <= $program['Program']['training_length']; $index++)
			{
				if($weekStart <= $date && $weekEnd > $date)
				{
					return $index;
				}
				else
				{
					$weekStart->modify( '+1 week' );
					$weekEnd->modify( '+1 week' );
				}
			}
		}

		return false;
	}

	private function _roundDown($value, $modifier, $roundAt)
	{
		return floor( ( $value * $modifier ) / $roundAt) * $roundAt;
	}
}

?>
