import { Injectable, Output, EventEmitter } from '@angular/core';
import _ from 'lodash';

export interface Tutorial {
  name: string
  currentPosition: number
  steps: number
}

@Injectable()
export class TutorialService {

  tutorials: Tutorial[] = [];

  // Outupts for tutorial flow
  @Output() onTutorialStart = new EventEmitter<Tutorial>();
  @Output() onTutorialEnd = new EventEmitter<Tutorial>();

  // Outputs for tutorial steps flow
  @Output() onTutorialNextStep = new EventEmitter<Tutorial>();
  @Output() onTutorialPreviousStep = new EventEmitter<Tutorial>();

  constructor() { }

  registerTutorailStep(tutorialName) {
    let tutorial = this.findOrCreateTutorialByName(tutorialName);
    tutorial.steps += 1;
  }

  unregisterTutorailStep(tutorialName) {
    let tutorial = this.findOrCreateTutorialByName(tutorialName);
    tutorial.steps -= 1;

    if (tutorial.steps === 0) {
      _.pull(this.tutorials, tutorial);
    }
  }

  /**
   * Starts a tutorial
   * @param tutorialName indicates the tutorial that should start
   */
  startTutorial(tutorialName) {
    let tutorial = this.findTutorialByName(tutorialName);

    try {
      tutorial.currentPosition = 0;
      let resp = { ...tutorial } as Tutorial;
      this.onTutorialStart.emit(resp);
      this.onTutorialNextStep.emit(resp);
    } catch (e) {
      console.error(e);
    }
  }

  /**
   * Ends a tutorial
   * @param tutorialName indicates the tutorial to end
   */
  endTutorial(tutorialName) {
    let tutorial = this.findTutorialByName(tutorialName);

    try {
      tutorial.currentPosition = null;
      this.onTutorialEnd.emit({ ...tutorial } as Tutorial);
    } catch (e) {
      console.error(e);
    }
  }

  /**
   * Makes active the next step on the given tutorial
   * @param tutorialName indicates the tutorial that needs to go to the
   * next step
   */
  nextStep(tutorialName) {
    let tutorial = this.findTutorialByName(tutorialName);

    try {
      if (tutorial.currentPosition == (tutorial.steps - 1)) {
        throw new Error(`the tutorial ${tutorialName} its already on its last step`);
      } else {
        tutorial.currentPosition += 1;
        this.onTutorialNextStep.emit({ ...tutorial } as Tutorial);
      }
    } catch (e) {
      console.error(e);
    }
  }

  /**
   * Makes active the previous step on the given tutorial
   * @param tutorialName indicates the tutorial that needs to go to the
   * previous step
   */
  previousStep(tutorialName) {
    let tutorial = this.findTutorialByName(tutorialName);

    try {
      if (tutorial.currentPosition === 0) {
        throw new Error(`The tutorial ${tutorialName} its already on its first step`);
      } else {
        tutorial.currentPosition -= 1;
        this.onTutorialPreviousStep.emit({ ...tutorial } as Tutorial);
      }
    } catch (e) {
      console.error(e);
    }
  }

  /**
   * Find a tutorial by its name
   * @param tutorialName Name of the tutorial you are looking for
   */
  private findTutorialByName(tutorialName): Tutorial {
    return this.tutorials.find(tutorial => tutorial.name == tutorialName);
  }

  /**
   * Finds or creates a tutorial by name
   * @param tutorialName Name of the tutorail to find or create
   */
  private findOrCreateTutorialByName(tutorialName) {
    let tutorial = this.findTutorialByName(tutorialName);

    if (!tutorial) {
      tutorial = { name: tutorialName, steps: 0, currentPosition: null } as Tutorial;
      this.tutorials.push(tutorial);
    }

    return tutorial;
  }
}
