import StringDiff from "./../../util/string_diff";
import { LocalizedStrings } from './../../services/localizedStringsService'

export class OutputAssessmentResult {

  static get PAGE_TEMPLATE () {
    return `<!DOCTYPE html>
    <html>
      <head>
        <meta http-equiv='content-type' content='text/html; charset=UTF-8'>
        <link rel='stylesheet' href='css/ivprog-assessment.css' type='text/css'/>
      </head>
    <body>
      :assessment-result:
    </body>
    </html>`;
  }

  static get DETAIL_TEMPLATE () {
    return `<div class='details-body'>
        <div class='details-header'>
          <h2>:test-name:</h2>
          <p>:time-label:: <span>:time:ms</span></p>
          <p>:grade-label:: <span>:grade:%</span></p>
        </div>
        <div>
          <h3>:input-label:</h3>
          <ul>
            <li>:input-list:</li>
          </ul>
        </div>
        <div>
          <h3>:output-label:</h3>
          :output-result:
        </div>
      </div>`;
  }

  static get OUPUT_TABLE_TEMPLATE () {
    return `<div class='detaisl-div-table'>
        <table class='assessment-output-table'>
          <tr>
            <th>:expected-label:</th>
            <th>:generated-label:</th>
            <th>:result-label:</th>
          </tr>
          :results:
        </table>
      </div>`;
  }

  static get OUTPUT_TEMPLATE () {
    return `<tr><td class=':class-expected:'>$0</td>
            <td class=':class-generated:'>$1</td>
            <td class=':class-result:'>$2</td></tr>`;
  }

  static get EMPTY_OUTPUT_TEMPLATE () {
    return `<div class='assessment-popup'><img class='assessment-empty-output' src='img/empty.svg'>
      <span class='assessment-popuptext'>$0</span></div>`;
  }

  static get FAILED_TEMPLATE () {
    return `<p class='assessment-failed-execution'><span class='assessment-failed-case'>✗</span>$0</p>`;
  }

  static get INPUT_INFO_TEMPLATE () {
    return `<span class='$0'>$1</span>`;
  }

  // Status code - it is not grade related!
  // 0 - Succesful execution
  // 1 - failed execution
  constructor (name, status, inputs, result, store, time, error_msg = '') {
    this.name = name;
    this.status = status;
    this.inputs = inputs;
    this.results = result;
    this.store = store;
    this.time = time;
    this.error_msg = error_msg;
  }

  get grade () {
    if(this.results == null) {
      return 0;
    }
    return this.results.reduce((prev, val) => prev + val.grade, 0) / this.results.length;
  }

  prepareResults () {
    let template = OutputAssessmentResult.DETAIL_TEMPLATE;
    const grade = (this.grade * 100).toFixed(2);
    const time = this.time || "-";
    template = template.replace(':test-name:', LocalizedStrings.getUI('assessment-detail-title', [this.name + 1]));
    template = template.replace(':time-label:', LocalizedStrings.getUI('assessment-detail-time-label'));
    template = template.replace(':time:', time);
    template = template.replace(':grade-label:', LocalizedStrings.getUI('assessment-detail-grade-label'));
    template = template.replace(':grade:', grade);
    const input_spans = this.prepareInputList(this.inputs);
    template = template.replace(':input-label:', LocalizedStrings.getUI('assessment-detail-input-label'));
    template = template.replace(':input-list:', input_spans);
    template = template.replace(':output-label:', LocalizedStrings.getUI('assessment-detail-output-label'));
    if(this.status == 0) {
      const output_rows = this.results.map(result => {
        if(result.type == "string") {
          return this.formatString(result);
        } else if (result.type == "number") {
          return this.formatNumber(result);
        } else {
          return this.formatBool(result);
        }
      }, this);
      template = template.replace(':output-result:', this.prepareOutputTable(output_rows));
    } else {
      let failed_text =  OutputAssessmentResult.FAILED_TEMPLATE;
      failed_text = failed_text.replace("$0", this.error_msg);
      template = template.replace(":output-result:", failed_text);
    }
    return template;
  }

  prepareInputList (input_list) {
    const list = input_list.map(input => {
      let template = OutputAssessmentResult.INPUT_INFO_TEMPLATE;
      template = template.replace("$1", input.value);
      if(input.read) {
        template = template.replace("$0", "assessment-input-read");
      } else {
        template = template.replace("$0", "assessment-input-unread");
      }
      return template;
    }, this);
    return list.join(LocalizedStrings.getUI('text_join_assessment_outputs'));
  }

  prepareOutputTable (output_rows) {
    let template = OutputAssessmentResult.OUPUT_TABLE_TEMPLATE;
    template = template.replace(':expected-label:', LocalizedStrings.getUI('assessment-detail-expected-label'));
    template = template.replace(':generated-label:', LocalizedStrings.getUI('assessment-detail-generated-label'));
    template = template.replace(':result-label:', LocalizedStrings.getUI('assessment-detail-result-label'));
    template = template.replace(':results:', output_rows.join(''));
    return template;
  }

  generateOutput () {
    const assessment_result =  this.prepareResults();
    let page = OutputAssessmentResult.PAGE_TEMPLATE;
    page = page.replace(':assessment-result:', assessment_result);
    page = page.replace(/(\r|\n|\t)/gm,'').replace(/> *</g, '><');
    return page;
  }

  formatNumber (result) {
    const result_class = result.grade == 1 ? 'assessment-number-result' : 'assessment-number-result-failed'; 
    let template = this.formatOutput('assessment-number-expected',
      'assessment-number-generated', result_class, result);
    return template
  }

  formatBool (result) {
    const result_class = result.grade == 1 ? 'assessment-bool-result' : 'assessment-bool-result-failed';
    let template = this.formatOutput('assessment-bool-expected',
      'assessment-bool-generated', result_class, result);
    return template
  }

  formatOutput (expected_class, generated_class, result_class, result) {
    let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
    template = template.replace(":class-expected:", expected_class);
    template = template.replace(":class-generated:", generated_class);
    template = template.replace(":class-result:", result_class);
    let expected_tmpl = result.expected;
    let generated_tmpl = result.generated;
    if(expected_tmpl == null) {
      expected_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace('$0',
        LocalizedStrings.getMessage('assessment-empty-expected-tooltip'));
    } else if(generated_tmpl == null) {
      generated_tmpl = OutputAssessmentResult.EMPTY_OUTPUT_TEMPLATE.replace('$0',
        LocalizedStrings.getMessage('assessment-empty-generated-tooltip'));
    }
    template = template.replace("$0", expected_tmpl);
    template = template.replace("$1", generated_tmpl);
    const final_result = result.grade == 1 ? "✓" : "✗"
    template = template.replace("$2", final_result);
    return template
  }

  formatString (result) {
    const expected_class = 'assessment-string-expected';
    const generated_class = 'assessment-string-generated';
    //const result_class = 'assessment-string-result';

    let template = OutputAssessmentResult.OUTPUT_TEMPLATE;
    template = template.replace(":class-expected:", expected_class);
    template = template.replace(":class-generated:", generated_class);
    //template = template.replace(":class-result:", result_class);

    const g_string = result.generated || "";
    const e_string = result.expected || "";
    template = template.replace("$0", result.expected);
    template = template.replace("$1", result.generated);
    if(result.grade == 1) {
      template = template.replace("$2", "✓");
      template = template.replace(":class-result:", 'assessment-string-result');
    } else {
      const diff = StringDiff(g_string, e_string);
      const diff_vec = diff.map(part => this.getDiffStringStyle(part[1], part[0]), this);
      const diff_string = diff_vec.reduce((prev, actual) => prev + actual, "");
      template = template.replace("$2", "<span class='assessment-failed-case'>✗</span>" + diff_string);
      template = template.replace(":class-result:", "assessment-string-diff");
    }
    return template;
  }

  getDiffStringStyle (text, action) {
    const template = "<span class='$0'>$1</span>"
    switch(action) {
      case StringDiff.INSERT:
        return template.replace("$0", "stringdiff-insert").replace("$1", text);
      case StringDiff.DELETE:
        return template.replace("$0", "stringdiff-delete").replace("$1", text);
      case StringDiff.EQUAL:
        return template.replace("$0", "stringdiff-equal").replace("$1", text);
    }
  }
}