/*jslint browser: true*/

"use strict";

/*jshint esversion: 6 */

class html2tedi {
	
	constructor() {
		this._is_converting_table_row = false;
		this._div_array = [];
		this._open_blocks = 0;

	}
	
	found(position) { return (position != -1); }
		
	/*
	 * Split string into array with lines
	 *
	 */
	get_lines(text) {
		let lines_array = [];
		if(this.found(text.indexOf('\n'))) {
			lines_array = text.split('\n');
			
			//JS when splits strings with \n at end
			//creates a fake newline in array
			
			if(text[text.size() - 1] == '\n') {
				//Delete last fake line
				lines_array.pop();
			}
		} else
			if(text.size() > 0)
				lines_array = [text];
		
		return lines_array;	
	}
	/*
	 * Add header, convert line by line through text and finish with footer
	 *
	 */
	convert(text) {
		let index = this.get_lines(text);
		this._is_unordered_list = 0;
		var return_text = "";
		for(var i = 0; i < index.length; i++) {
			this._div_array = [];
			return_text += this.convert_line(index[i]);
		}
		return return_text;
	}
	
	/*
	 * Main logic converting line. 
	 *
	 */
	convert_line(line) {
		if(line != "<div>" && line != "</div>") {
			line = this.delete_line_divs(line);
			// Convert comments if there is
			let comment = line.regexIndexOf("&lt;!");
			if(comment == 5)
				return this.convert_line_comment(line, comment);
			
			
			// Convert embed if there is
			var metatag = line.indexOf("<div class=\"embed\">");
			if(this.found(metatag)) {
				let last_position = line.length;
				if(line.substring(line.length - 6, line.length) == "</div>")
					last_position = line.length - 6;
				else if(line.substring(line.length - 7, line.length - 1) == "</div>")
					last_position = line.length - 7;
				
				return "&lt;&gt;" + this.escape_html_chars(line.substring(metatag + 19, last_position)) + '\n';
			}
			
			let start_embed = line.regexIndexOf("&lt;&gt;");
			if(start_embed == 5)
				return line + "\n";
			
			
			// Convert metatag with line break if there is
			if(this.found(line.regexIndexOf("<pre>")) || this.found(line.regexIndexOf("</pre>")))
				return this.convert_line_metatag_with_line_break(line);
			
			// Convert comments if there is
			let start_code = line.regexIndexOf("<code>");
			if(start_code == 5)
				return this.convert_line_metatag(line);
			
			
			if(this.found(start_code))
				return this.convert_line_with_code_tags(line);
			else
				return this.convert_complete_line_check_special_chars(line);

		}else
			return "";
	}
	
	/*
	 * Delete div that defines a div
	 */
	delete_line_divs(line) {
		let line_div = line.regexIndexOf("<div");
		while(this.found(line_div)){
			
			let end_tag_position = line.regexIndexOf(">", line_div);
			if(line[line_div + 4] == '>') {
				this._div_array.push(0);
				line = line.erase(line_div, 5);

				line_div = line.regexIndexOf("<div", line_div);
			} else {
				this._div_array.push(1);
				line_div = line.regexIndexOf("<div", line_div + 1);
			}

		}
		let end_div = line.regexIndexOf("</div>");
		while(this.found(end_div) && this._div_array.length > 0){
			if(this._div_array.pop() == 0){
				line = line.insert(end_div, " ");
				line = line.erase(end_div + 1, 6);
			}
			end_div = line.regexIndexOf("</div>", end_div + 1);
		}
		return line;
	}
	
	/*
	 * Split metatag lines from not metatagged lines
	 * and convert them
	 */
	convert_line_with_code_tags(line) {
		let return_text = "";
		let start_code = line.regexIndexOf("<code>");
		while(this.found(start_code)) {

			return_text += this.convert_line_check_special_chars(line.substring(0, start_code));
			let end_code = line.regexIndexOf("</code>");
			
			let metatag = "&lt;";
			if(end_code == line.size() - 7)
				metatag = "&lt;+";

			return_text += metatag + this.escape_html_chars(line.substring(start_code + 6, end_code)) + "\n";
			line = line.substring(end_code + 7, line.length);
			start_code = line.regexIndexOf("<code>");
		}
		if(line.length > 0)
			return_text += this.convert_line_check_special_chars(line.substring(0, line.length));
		
		return return_text;
	}
	
	/*
	 * Keep comments but escape html chars
	 *
	 */
	convert_line_comment(line, comment) {
		return "&lt;!" + this.escape_html_chars(line.substring(comment + 5, line.length)) + '\n';
	}
	
	/*
	 * Delete pre tag to metatag line break
	 *
	 */
	convert_line_metatag_with_line_break(line) {
		line = line.replace("<pre>", '');
		let has_end_pre = line.regexIndexOf("</pre>");
		if(this.found(has_end_pre)) {
			line = line.replace("</pre>", '');
		}
		return "&lt;+ " + this.escape_html_chars(line) + '\n';
	}
	
	/*
	 * Delete code tag to inline metatag
	 *
	 */
	convert_line_metatag(line) {
		line = line.replace("<code>", '');
		let has_end_code = line.regexIndexOf("</code>");
		if(this.found(has_end_code)) {
			line = line.replace("</code>", '');
		}
		return "&lt;+ " + this.escape_html_chars(line.substring(5, line.length)) + '\n';
	}
	
	/*
	 * Use metatag when line has special chars and escaped them
	 *
	 */
	convert_line_check_special_chars(line) {

		let escaped_line = this.escape_characters(line);
		if(this.has_special_characters(escaped_line))
			return "&lt;+ " + this.escape_html_chars(escaped_line);
		else
			return this.convert_line_all_tags(escaped_line);
		
	}
	
	/*
	 * Use metatag when line has special chars and escaped them
	 *
	 */
	convert_complete_line_check_special_chars(line) {

		let escaped_line = this.escape_characters(line);
		if(this.has_special_characters(escaped_line))
			return "&lt;+ " + this.escape_html_chars(escaped_line) + '\n';
		else
			return this.convert_line_all_tags(escaped_line);
		
	}
	
	/*
	 * Escape < and >
	 *
	 */
	escape_html_chars(line) {
		line = line.replace(/</g, "&lt;");
		line = line.replace(/>/g, "&gt;");
		return line;
	}
	
	/*
	 * Escape tedi tags {} [] () y \
	 *
	 */
	escape_characters(line) {

		line = line.replace(/\\/g, "\\\\");
		line = line.replace(/\[/g, "\\[");
		line = line.replace(/\]/g, "\\]");
		line = line.replace(/\{/g, "\\{");
		line = line.replace(/\}/g, "\\}");
		line = line.replace(/\)/g, "\\)");
		line = line.replace(/\(/g, "\\(");

		line = this.delete_whitespaces(line);

		return line;
	}
	
	delete_whitespaces(line) {
		var i = 0;
		while(i < line.length && (line[i] == ' ' || line[i] == ' ')) {
			++i;
		}
		return line.substring(i, line.length);
	}
	
	/*
	 * Convert heading, blocks, links, images and table
	 *
	 */
	convert_line_all_tags(line) {
		line = line.replace(/<br>/g, '');
		let position = line.regexIndexOf("<h");
		if(this.found(position) && this.is_first_tag(line, position))
			return this.convert_line_heading(line, position);
		else {
			if(line.regexIndexOf("<tr") == -1 && line.regexIndexOf("<tbody") == -1 && 
			line.regexIndexOf("<table") == -1 && line.regexIndexOf("</tbody") == -1 &&
			line.regexIndexOf("</table") == -1) {
				line = this.convert_line_all_blocks(line);
			
				line = this.convert_line_all_links(line);

				line = this.convert_line_all_images(line);
				
				line = this.convert_line_all_ending_blocks(line);

				line = this.convert_line_list(line);
				let position = line.search("</li>");
				if(this.found(position) && this.is_first_tag(line, position))
					return "";
				return this.convert_line_table_row(line);
				
			} else
				return "";
			

		}
	}
	
	/*
	 * Convert heading
	 * h1 -> #
	 * h2 -> ##
	 * h3 -> ###
	 * h4 or more -> ###
	 */
	convert_line_heading(line, position) {
		if(position != 0)
			line = line.substring(position, line.length);
		
		line = line.replace("<h1>", "#");
		line = line.replace("<h2>", "##");
		line = line.replace("<h3>", "###");
		line = line.replace(/<h[1-9]{1}>/, "###");

		line = line.replace("<\/h1>", "");
		line = line.replace("<\/h2>", "");
		line = line.replace("<\/h3>", "");
		line = line.replace(/<\/h[1-9]{1}>/, "");
		return line + "\n";
	}
	
	/*
	 * Convert table row
	 *
	 */
	convert_line_table_row(line) {
		let pipe = line.search("<td");
		if(this.found(pipe))
			return this.convert_line_table_data(line, pipe);
		else {
			let end_pipe;
			if(this._is_converting_table_row) {
				end_pipe = line.regexIndexOf("</tr>");
				let end_data = line.regexIndexOf("</td>");
				if(this.found(end_data)) {

					if(!this._is_converting_table_row)
						return ' | ' + line.substring(pipe, end_data);
					else {
						this._is_converting_table_row = false;
						return line.substring(pipe, end_data);
					}
				}

				if(this.found(end_pipe)) {
					this._is_converting_table_row = false;
					return ' |\n';
				}
				
			} else {
				end_pipe = line.regexIndexOf("</tr>");
				if(this.found(end_pipe))
					return ' |\n';
				else
					return this.erase_html_tags(line) + '\n';
				
			}
		}
	}
	
	/*
	 * Convert table data line
	 *
	 */
	convert_line_table_data(line, pipe) {
		this._is_converting_table_row = true;
		pipe = line.regexIndexOf('>', pipe + 1);
		if(this.found(pipe)) {
			let end_pipe = line.regexIndexOf("</td>", pipe + 1);
			pipe += 1;
			if(this.found(end_pipe)) {
				this._is_converting_table_row = false;
				return ' | ' + line.substring(pipe, end_pipe);
				
			} else
				return ' | ' + line.substring(pipe, line.length) + " ";
			
		} else {
			return "";
		}
	}
	
	/*
	 * Convert all block tags in line
	 *
	 */
	convert_line_all_blocks(line) {
		let start_tag = line.search("<div ");
		while(this.found(start_tag)) {
			let block_class = this.get_attribute_html(line, "class", start_tag);
			let end_start_tag = line.regexIndexOf(">", start_tag);
			if(this.found(end_start_tag)) {

				line = line.erase(start_tag, end_start_tag + 1 - start_tag);
				line = line.insert(start_tag, "{(" + block_class + ") ");
				let end_tag = line.regexIndexOf("</div>", start_tag);
				if(this.found(end_tag)) {
					line = line.erase(end_tag, 6);
					line = line.insert(end_tag, "}");
				}else
					this._open_blocks++;
			}
			start_tag = line.regexIndexOf("<div ", start_tag + 1);
		}
		return line; 
	}
	
	/*
	 * Convert all ending block tags in line
	 *
	 */
	convert_line_all_ending_blocks(line) {
		let end_tag = line.search("</div>");
		while(this.found(end_tag) && this._open_blocks) {
			line = line.replace("</div>", "}");
			this._open_blocks--;
			end_tag = line.regexIndexOf("</div>", end_tag + 1);
		}
		return line;
	}
	
	/*
	 * Convert all links tags in line
	 *
	 */
	convert_line_all_links(line) {
		let start_tag = line.search("<a ");
		let end_tag = line.regexIndexOf("</a>", start_tag);
		while(this.found(start_tag) && this.found(end_tag)) {
			let href = this.get_attribute_html(line, "href", start_tag);
			let end_start_tag = line.regexIndexOf(">", start_tag);
			if(this.found(end_start_tag)) {
				end_start_tag += 1;
				line = line.erase(start_tag, end_start_tag - start_tag);
				line = line.insert(start_tag, "[(" + href + ") ");
				let end_tag = line.regexIndexOf("</a>", start_tag);
				if(this.found(end_tag)) {
					line = line.erase(end_tag, 4);
					line = line.insert(end_tag, "]");
				}
			}
			start_tag = line.search("<a ");
		}
		return line;
	}
	
	/*
	 * Convert all image tags in line
	 *
	 */
	convert_line_all_images(line) {
		let start_tag = line.regexIndexOf("<img ");
		let img_end = line.regexIndexOf(">", start_tag);
		let new_tag_start = line.regexIndexOf("<", start_tag + 1);
		while(this.found(start_tag) && this.found(img_end) && ((this.found(new_tag_start) && new_tag_start > img_end) || !this.found(new_tag_start))) {

			let src = this.get_attribute_html(line, "src", start_tag);
			let alt = this.get_attribute_html(line, "alt", start_tag);
			img_end = line.regexIndexOf(">", start_tag);
			if(this.found(img_end)) {
				line = line.substring(0, start_tag) + "([" + alt + "] "+ (src) + ")" + line.substring(img_end + 1, line.length);
			}

			start_tag = line.regexIndexOf("<img ");
		}
		return line;
	}
	
	/*
	 * Convert list and nested lists
	 *
	 */
	convert_line_list(line) {
		if(line.regexIndexOf("<ul>") >= 0){

			line = line.replace(/<ul>/, "__");
			if(this._is_unordered_list > 0) {
				line = line.replace("__", "\n__ ");
			}
			this._is_unordered_list++;
		}
		if(this._is_unordered_list > 0){
			line = line.replace(/<li>/, "--");
			if(line.regexIndexOf("<\/ul>") >= 0){
				line = line.replace(/<\/ul>/, ",,");
				this._is_unordered_list--;
			}
		}
		
		return line;
	}
	
	/*
	 * Check if line has HTML, tedi or LaTeX characters.
	 *
	 */
	has_special_characters(line) {
		return (this.has_html_special_characters(line)
		 || this.has_latex_special_characters(line)
		 || this.has_tedi_special_characters(line));
	}
	
	/*
	 * Check HTML special chars &lt; y &gt;
	 *
	 */
	has_html_special_characters(line) {
		return (this.found(line.regexIndexOf('(&lt;)|(&gt;)')));
	}
	
	/*
	 * Check LateX $ special char
	 *
	 */
	has_latex_special_characters(line) {

		let found = line.search('/(\$)/');
		return (this.found(found) && found < (line.length - 6));
	}

	/*
	 * Check tedi tags {} [] () y \
	 *
	 */
	has_tedi_special_characters(line) {
		var start_link = line.indexOf('[(');
		var end_link = -1;
		if(this.found(start_link)) {
			var end_first = line.indexOf("\)", start_link);
			if(this.found(end_first)) {
				end_link = line.indexOf("\]", end_first);
			}
		}
		var start_div = line.indexOf("\{\(");
		var end_div = -1;
		if(this.found(start_div)) {
			var end_div_first = line.indexOf("\)", start_div);
			if(this.found(end_div_first)) {
				end_div = line.indexOf("}", end_div_first);
			}
		}
		var start_img = line.indexOf("\(\[");
		var end_img = -1;
		if(this.found(start_img)) {
			var end_img_first = line.indexOf("\]", start_img);
			if(this.found(end_img_first)) {
				end_img = line.indexOf("\)", end_img_first);
			}
		}

		return ((this.found(start_link) && this.found(end_link)) || (this.found(start_img) && this.found(end_img)) || (this.found(start_div) && this.found(end_div)));
	}

	is_first_tag(line, position) {
		var e = 0;
		while(e < position && (line[e] == ' ' || (line[e] == '\t') || (line[e] == '\n'))) {
			e += 1;
		}
		return (e == position);
	}
	
	erase_html_tags(line) {

		let start_tag = line.indexOf("<");
		while(this.found(start_tag)) {

			let end_tag = line.indexOf(">", start_tag);
			if(this.found(end_tag))
				line = line.substring(0, start_tag) + line.substring(end_tag + 1, line.length);
			else
				line = line.substring(0, start_tag - 1);
			start_tag = line.indexOf("<");
		}
		return line;
	}
	
	/*
	 * Get attribute in HTML tag from name and start position.
	 *
	 */
	get_attribute_html(line, attribute, start_position) {
		let attrib_start = line.regexIndexOf(attribute + "=\"", start_position + 3);
		if(this.found(attrib_start)) {
			let attrib_end = line.regexIndexOf("\"", attrib_start + attribute.length + 2);
			if(this.found(attrib_end))
				return line.substring(attrib_start + attribute.length + 2, attrib_end);
			else
				return "";
			
		}else
			return "";
		
	}
}
