/**
 * @since Fri Oct 05 2018
 * @author Charles Gouldmann - Klean
 */

// Core imports
import { Injectable } from '@angular/core'
import { Subject } from 'rxjs/Subject'

// Settings
import { store } from '@k-settings/app-setup'

// Services
import { HelperService } from '@k-core/services/svc.helper'


// Interfaces
interface Steps {
    name: string;
    active: boolean;
	reached: boolean;
	navigatable: boolean;
}


@Injectable()
export class StepNavigationService {
	
	// ---- Variables ---- \\
	private steps: Steps[] = []
	private currentStep: string



	constructor(
		private _helper: HelperService
	) {
		// Subscribe to steps
		this.steps$.subscribe((resp) => {
			console.log('subscribe change in svc: ', resp)
            this.steps = resp
		})
		
		// Subscribe to current step
		this.currentStep$.subscribe((resp:string) => {
			this.currentStep = resp
		})
	}



	// ---- Observables ---- \\

	// Steps observable
	private stepsSource = new Subject<any>()
	public steps$ = this.stepsSource.asObservable()

	private currentStepSource = new Subject()
	public currentStep$ = this.currentStepSource.asObservable()




	// ---- Functions ---- \\

	// * SETUP


	registerSteps(stepsToRegister:string[] = []) {
		let steps = []

		// set steps
		for(let step of stepsToRegister) {
			// set first to active and rest default
			if(steps.length < 1) {
				this.currentStepSource.next(step)
				steps.push({name: step, active: true, reached: true, navigatable: true})
			}
			else {
				steps.push({name: step, active: false, reached: false, navigatable: false})
			}
		}

		// set observable 
		this.stepsSource.next(steps)
	}


	// * Navigation
	


	stepInDirection(direction:string = 'next') {
		let current = this.getCurrentStep()
		let steps = this.getSteps()
		let index = 0
		let stepPosition: number = 0

		console.log('steps in stepInDirection: ', steps)

		console.log('step in direction: ', direction)

		// Find the index of current
		for(let step of steps) {
			if(step.name === current) {
				console.log('current step: ', step.name)
				console.log('index of current: ', index)
				stepPosition = index
			}
			index++
		}

		// Find the desired step
		let desiredStep = 0

		if(direction === 'next') {
			console.log('stepPosition + 1 < steps.length', stepPosition + 1 < steps.length)
			console.log('stepPosition+1', stepPosition+1)
			desiredStep = stepPosition + 1 < steps.length ? stepPosition + 1 : steps.length -1
		}
		else {
			desiredStep = stepPosition - 1 >= 0 ? stepPosition - 1 : 0
		}

		console.log('desiredStep: ', desiredStep)
		console.log('steps[desiredStep]: ', steps[desiredStep])

		
		// Set new current
		if(!!steps[desiredStep]) {
			this.canNavigateTo(steps[desiredStep].name, true)
			this.goToStep(steps[desiredStep].name)
		}
	}



	/**
	 * Go to step with given `name` but only if navigatable
	 * 
	 * @param name 
	 */
	goToStep(name: string) {
		let steps = this.getSteps()
		let update: boolean = false
		let updateName: string

		// Only navigate if step exists and is navigatable
		for(let step of steps) {
			if(step.name === name && step.navigatable) {
				// Set flag
				update = true

				// Update values
				step.active = true
				updateName = step.name

				// Set current
				this.currentStepSource.next(name)
			}
		}

		// Update steps observable
		if(update) {
			console.log('updating steps in goToStep()', steps)
			this.stepsSource.next(steps)
			if(!!updateName)
				this.updateReached(updateName)

			// Scroll to top on navigation
			let timout = store === 'bus' ? 400 : 10
			console.log('store: ', store)
			setTimeout(()=> {
				this._helper.scrollTo('#stepNavTop')
			}, timout)
		}
	}


	
	/**
	 * Sets the navigatable state of the given step
	 * 
	 * @param name 
	 * @param canNavigate 
	 */
	canNavigateTo(name: string, canNavigate: boolean) {
		let steps = this.getSteps()

		for(let step of steps) {
			if(step.name === name) {
				step.navigatable = canNavigate
			}
		}

		this.stepsSource.next(steps)
	}
	
	
	/**
	 * Loop through steps and change value of `reached` depending on
	 * `target` position.
	 * 
	 * @param target 
	 */
	updateReached(target: string) {
		console.log('updateReached()')
		let steps 		= [].concat(this.getSteps()) // get a clone of the array
		let current 	= this.getCurrentStep()

		console.log('steps before manipulation: ', steps)

		let reachedTarget: boolean 	= false
		let reversed: boolean 		= false

		// Is the target in front of or behind current?
		let currentPos: number
		let targetPos: number
		let index: number = 0
		for(let step of steps) {
			if(step.name === current) {
				currentPos = index
			}

			if(step.name === target) {
				targetPos = index
			}

			index++
		}

		// Change the order of steps depending on direction
		if(currentPos > targetPos) {
			steps = steps.reduceRight(function (arr, last, index, coll) {
				return (arr = arr.concat(last))
			}, [])
			reversed = true

			console.log('reversing steps: ', steps)
		}


		// Set reached
		let animationTime: number = 300
		let animationIndex: number = 0

		for(let step of steps) {
			if(step.name === target) {
				reachedTarget = true
			}

			console.log('reversed: ', reversed)
			console.log('index: ', animationIndex)
			console.log('steps: ', steps)

			if(!reachedTarget || !reversed && step.name === target) {
				console.log('not reached or not reversed and target')
				console.log('reached: ', reversed ? false : true)
				step.reached = reversed ? false : true
			}
			else {
				console.log('reached or reversed and not target')
				console.log('reached: ', reversed ? true : false)
				step.reached = reversed ? true : false
			}
			
			// Change order of steps back to normal if reversed
			let updatedSteps = steps
			if(reversed) {
				console.log('updatedSteps before reverse: ', updatedSteps)
				console.log('! reversing updatedSteps')
				updatedSteps = updatedSteps.reduceRight(function (arr, last, index, coll) {
					console.log(last, index);
					return (arr = arr.concat(last))
				}, [])
				console.log('updatedSteps after reverse: ', updatedSteps)
			}

			console.log('updatedSteps: ', updatedSteps)

			// Update steps on a delay
			let that = this
			setTimeout(function(updatedSteps, animationTime, animationIndex) {
				console.log('updatedSteps in timeout:', updatedSteps)
				console.log('timeout value', animationTime * animationIndex)
				that.stepsSource.next(updatedSteps)
			}.bind(this, updatedSteps, animationTime, animationIndex), animationTime * animationIndex)

			animationIndex++
		}

	}
	
	
	
	// * Info
	

	/**
	 * Returns current step
	 */
	getCurrentStep() {
		return this.currentStep
	}
	
	/**
	 * Returns all steps
	 */
	getSteps() {
		return this.steps
	}
}