import { h, render, Component } from 'preact'; // eslint-disable-line no-unused-vars
// content and config:
import content from '../content/module.json';
import offlineQuestions from '../content/offline'; // two-dim. array questions[group][a=1|b=2|c=3]
import config from '../config';
// services and utilities:
import api from '../utilities/api';
import { fixedDigits } from '../utilities/formatter';
// screens and items:
import TitleScreen from './TitleScreen.jsx';
import FinalScreen from './FinalScreen.jsx';
import QuestionScreen from './QuestionScreen.jsx';

/**
 * titlescreen -> questionscreen (*n) -> finalscreen
 */
export default class App extends Component {

  // construct and initialize functions
  constructor (props) {

    super(props);

    this.state = {
      route: 'titlescreen',        // current screen
      chartMode: 'question',       // current chart mode: question || score
      currentGroup: 0,             // current group
      currentQuestion: 0,          // current question in group
      currentAnswerIndex: null,    // index in question pair of the currently set answer
      currentAnswerCorrect: false, // is the currently set question the correct one?
      isFetching: false,           // currently performing XHR?
      userId: null,                // current user id for posting
      token: null,                 // current access token
      voteRatios: null,            // get votes from other users
      points: {}                   // get correct and incorrect points
    };

    // context binding
    this.navigate = this.navigate.bind(this);
    this.toNextQuestion = this.toNextQuestion.bind(this);
    this.setAnswer = this.setAnswer.bind(this);
    this.setData = this.setData.bind(this);
    this.drawQuestions = this.drawQuestions.bind(this);
    this.getUserVotes = this.getUserVotes.bind(this);
    this.jumpTo = this.jumpTo.bind(this);
    this.setInitialState = this.setInitialState.bind(this);
    this.getFeedbackCoordinates = this.getFeedbackCoordinates.bind(this);
  }

  // initial setup, get accesToken and userId
  componentDidMount () {

    // register hashchange handler.
    // Needs more work: reset state, otherwise artefacts will appear.
    // Without event handler the site has to be reloaded after hash change
    // onhashchange = this.jumpTo;

    if (this.props.isOffline) {
      this.setState({ questions: offlineQuestions });
      this.jumpTo();
    } else {
      this.setState({ isFetching: true });
      api.getToken().then(accessToken => {
        this.setState({ token: accessToken });

        // create user
        api.createUser(accessToken)
          .then(user => {
            this.setState({
              userId: user.userId,
              isFetching: false
            });

            // draw first question
            this.drawQuestions();
          });
      });
    }
  }

  /**
   * Navigation using window.location.hash, else go to title screen
   *
   * @route: The state's route (string). See switch statement in render function
   * @currentGroup: 1-based index of current 'question group'
   * @currentQuestion: 1-based index of current question
   *
   * example: .../#question_2_1
   */
  jumpTo () {
    if (window.location.hash) {
      const hashInfo = window.location.hash.replace('#', '').split('_');

      this.setState({
        route: hashInfo[0],
        currentGroup: parseInt(hashInfo[1]) - 1 || 0,
        currentQuestion: parseInt(hashInfo[2]) - 1 || 0
      });
    }
  }

  // navigate to specific route
  navigate (route) {
    if (route === 'titlescreen') this.setInitialState();
    this.setState({ route });
  }

  // set data
  setData (isCorrect) {
    // handle continue button visibility
    this.setState({ currentAnswerIndex: isCorrect ? 1 : null });
  }

  // get number of questions
  getNumberQuestions () {
    let sum = 0;

    for (let i = 0; i < this.state.questions.length; i += 1) {
      sum += this.state.questions[i].length;
    }

    return sum;
  }

  // get question set from api
  drawQuestions () {
    const questionSet = [];

    api.get(config.api.list)
      .then(json => {
        [ ...json ].forEach(item => {
          if (questionSet.length < item.gruppe) {
            questionSet.push([]);
          }
          questionSet[item.gruppe - 1].push(item);
        });
        this.setState({ questions: questionSet });
        this.jumpTo();
      });
  }

  // update state depending on chart mode and current question
  toNextQuestion () {

    // Show "score screen" after each question of first and second question groups in online mode
    if (this.state.chartMode === 'question' && this.state.currentGroup < 2 && !this.props.isOffline) {

      this.setState({ chartMode: 'score', points: {} });
    // Else, if exists, display next question in group
    } else if (this.state.currentQuestion < this.state.questions[this.state.currentGroup].length - 1) {
      this.setState({
        route: 'question',
        chartMode: 'question',
        currentQuestion: this.state.currentQuestion + 1,
        currentAnswerIndex: null,
        points: {},
        voteRatios: null
      });
    // Else, if exists, jump to fist question in next group
    } else if (this.state.currentGroup < this.state.questions.length - 1) {

      this.setState({
        route: 'question',
        chartMode: 'question',
        currentGroup: this.state.currentGroup + 1,
        currentQuestion: 0,
        currentAnswerIndex: null,
        voteRatios: null
      });
    // Else show "final screen"
    } else {
      // go to finalscreen
      this.setState({ route: 'finalscreen' });
    }
  }

  // set initial state (for start and restart)
  setInitialState () {
    this.setState({
      route: 'titlescreen',
      chartMode: 'question',
      currentGroup: 0,
      currentQuestion: 0,
      currentAnswerIndex: null,
      voteRatios: null
    });
  }

  /**
   * Get coordinates for solution and, if applicable, incorrect answer.
   *
   * @param question: Data structure defining the question
   * @param answer: Answer selected by user
   * @return: Coordinates of solution, and answer, if incorrect
   */
  getFeedbackCoordinates (question, answer) {

    const points = { correct: null, incorrect: null };
    const index = question.antworten.indexOf(answer);

    let correctAnswer;
    if (!answer.korrekt) {
      correctAnswer = question.antworten.find(a => a.korrekt === true);
    } else {
      correctAnswer = answer;
    }

    points.correct = correctAnswer.koordinate;

    // If answer is not correct display both solution, and answer
    if (!answer.korrekt) {
      points.incorrect = question.antworten[index].koordinate;
    }

    return points;
  }

  /**
   * Submit answer to API, get percentage of correct answers by other users (multiple choice questions)
   * By convention (react / preact), the render function is automatically invoked after this.setState(...) has been called
   *
   * @param answer: Answer selected by user
   */
  setAnswer (answer) {
    const question = this.state.questions[this.state.currentGroup][this.state.currentQuestion];
    const cAnswerIndex = question.antworten.indexOf(answer);

    let prepareState = {
      currentAnswerIndex: cAnswerIndex,
      currentAnswerCorrect: answer.korrekt
    };

    if (this.state.currentGroup === 0) {  // only search for result coordinates for first question group.
      prepareState.points = this.getFeedbackCoordinates(question, answer);
    }

    if (!this.props.isOffline) {
      const payload = {
        userId: this.state.userId,
        aufgabeId: question.id,
        antwort: question.antworten[cAnswerIndex].antwort,
        korrekt: answer.korrekt
      };

      api.post(config.api.create, payload, this.state.token)
        .then(() => {
          this.getUserVotes(question.id).then(
            json => {
              prepareState.voteRatios = [ json.auswertung.korrekt, json.auswertung.inkorrekt ];
              this.setState(prepareState);
            }
          );
        });
    } else {
      this.setState(prepareState);
    }
  }

  // get user votes by question id
  getUserVotes (aufgabeId) {
    return api.get(config.api.proportions, { aufgabeId });
  }

  // RENDER
  render () {
    let outputContent;

    switch (this.state.route) {
      case 'question': {

        // calculate number of questions from nested questions array
        const total = this.state.questions.map(e => e.length).reduce((s, e) => (s + e));
        // calculate current question 'index' (positive number)
        let index = 0;
        for (let i = 0; i < this.state.currentGroup; i += 1) {
          index += this.state.questions[i].length;
        }
        index += this.state.currentQuestion + 1;

        const headerState = `${fixedDigits(index, 2)}/${total}`;

        outputContent = <QuestionScreen
          {...content}
          route={this.state.route}
          chartMode={this.state.chartMode}
          headerState={headerState}
          questions={this.state.questions[this.state.currentGroup]}
          currentQuestion={this.state.currentQuestion}
          currentAnswerIndex={this.state.currentAnswerIndex}
          currentAnswerCorrect={this.state.currentAnswerCorrect}
          toNextQuestion={this.toNextQuestion}
          currentGroup={this.state.currentGroup}
          getUserVotes={this.getUserVotes}
          voteRatios={this.state.voteRatios}
          points={this.state.points}
          setData={this.setData}
          setAnswer={this.state.currentAnswerIndex === null ? this.setAnswer : () => {} } />; // eslint-disable-line no-empty-function
        break; }

      case 'finalscreen':
        outputContent = <FinalScreen
          {...content}
          navigate={this.navigate}
          isFetching={this.state.isFetching} />;
        break;

      case 'titlescreen':
      default:
        outputContent = <TitleScreen
          {...content}
          navigate={ this.navigate }
          navigateTo='question'
          isFetching={this.state.isFetching} />;
        break;

    }

    return outputContent;
  }

}
