// Scintilla source code edit control
/** @file LexMPT.cxx
 ** Lexer for MPT specific files. Based on LexOthers.cxx
 ** LOT = the text log file created by the MPT application while running a test program
 ** Other MPT specific files to be added later.
 **/
// Copyright 2003 by Marius Gheorghe <mgheorghe@cabletest.com>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>

#include <string>
#include <string_view>

#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"

#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "LexerModule.h"

using namespace Lexilla;

static int GetLotLineState(std::string &line) {
	if (line.length()) {
		// Most of the time the first non-blank character in line determines that line's type
		// Now finds the first non-blank character
		unsigned i; // Declares counter here to make it persistent after the for loop
		for (i = 0; i < line.length(); ++i) {
			if (!(IsASCII(line[i]) && isspace(line[i])))
				break;
		}

		// Checks if it was a blank line
		if (i == line.length())
			return SCE_LOT_DEFAULT;

		switch (line[i]) {
		case '*': // Fail measurement
			return SCE_LOT_FAIL;

		case '+': // Header
		case '|': // Header
			return SCE_LOT_HEADER;

		case ':': // Set test limits
			return SCE_LOT_SET;

		case '-': // Section break
			return SCE_LOT_BREAK;

		default:  // Any other line
			// Checks for message at the end of lot file
			if (line.find("PASSED") != std::string::npos) {
				return SCE_LOT_PASS;
			}
			else if (line.find("FAILED") != std::string::npos) {
				return SCE_LOT_FAIL;
			}
			else if (line.find("ABORTED") != std::string::npos) {
				return SCE_LOT_ABORT;
			}
			else {
				return i ? SCE_LOT_PASS : SCE_LOT_DEFAULT;
			}
		}
	}
	else {
		return SCE_LOT_DEFAULT;
	}
}

static void ColourizeLotDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
	styler.StartAt(startPos);
	styler.StartSegment(startPos);
	bool atLineStart = true;// Arms the 'at line start' flag
	char chNext = styler.SafeGetCharAt(startPos);
	std::string line("");
	line.reserve(256);	// Lot lines are less than 256 chars long most of the time. This should avoid reallocations

	// Styles LOT document
	Sci_PositionU i;			// Declared here because it's used after the for loop
	for (i = startPos; i < startPos + length; ++i) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		line += ch;
		atLineStart = false;

		// LOT files are only used on the Win32 platform, thus EOL == CR+LF
		// Searches for the end of line
		if (ch == '\r' && chNext == '\n') {
			line += chNext; // Gets the '\n'
			++i; // Advances past the '\n'
			chNext = styler.SafeGetCharAt(i + 1); // Gets character of next line
			styler.ColourTo(i, GetLotLineState(line));
			line = "";
			atLineStart = true; // Arms flag for next line
		}
	}

	// Last line may not have a line ending
	if (!atLineStart) {
		styler.ColourTo(i - 1, GetLotLineState(line));
	}
}

// Folds an MPT LOT file: the blocks that can be folded are:
// sections (headed by a set line)
// passes (contiguous pass results within a section)
// fails (contiguous fail results within a section)
static void FoldLotDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
	bool foldCompact = styler.GetPropertyInt("fold.compact", 0) != 0;
	Sci_PositionU endPos = startPos + length;
	int visibleChars = 0;
	Sci_Position lineCurrent = styler.GetLine(startPos);

	char chNext = styler.SafeGetCharAt(startPos);
	int style = SCE_LOT_DEFAULT;
	int styleNext = styler.StyleAt(startPos);
	int lev = SC_FOLDLEVELBASE;

	// Gets style of previous line if not at the beginning of the document
	if (startPos > 1)
		style = styler.StyleAt(startPos - 2);

	for (Sci_PositionU i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if (ch == '\r' && chNext == '\n') {
			// TO DO:
			// Should really get the state of the previous line from the styler
			int stylePrev = style;
			style = styleNext;
			styleNext = styler.StyleAt(i + 2);

			switch (style) {
/*
			case SCE_LOT_SET:
				lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
				break;
*/
			case SCE_LOT_FAIL:
/*
				if (stylePrev != SCE_LOT_FAIL)
					lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
				else
					lev = SC_FOLDLEVELBASE + 1;
*/
				lev = SC_FOLDLEVELBASE;
				break;

			default:
				if (lineCurrent == 0 || stylePrev == SCE_LOT_FAIL)
					lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
				else
					lev = SC_FOLDLEVELBASE + 1;

				if (visibleChars == 0 && foldCompact)
					lev |= SC_FOLDLEVELWHITEFLAG;
				break;
			}

			if (lev != styler.LevelAt(lineCurrent))
				styler.SetLevel(lineCurrent, lev);

			lineCurrent++;
			visibleChars = 0;
		}

		if (!isspacechar(ch))
			visibleChars++;
	}

	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, lev | flagsNext);
}

static const char * const emptyWordListDesc[] = {
	0
};

extern const LexerModule lmLot(SCLEX_LOT, ColourizeLotDoc, "lot", FoldLotDoc, emptyWordListDesc);
