#include "Project.h"
#include "Settings.h"

#include "FishIDEEditor.h"

using namespace astyle;

/////////////////////////////////////////////////////////////////////////////////////////
// CLASS ASStreamIterator
class ASStreamIterator : public ASSourceIterator
{
	protected:

		enum EolMode { crlf, lf, cr, lfcr } ;
		WString const &src;
		int p;
		EolMode eolMode;

	public:
		ASStreamIterator(WString const &Src);
		virtual ~ASStreamIterator() {}
		virtual bool hasMoreLines() const;
		virtual WString nextLine();

		WString getEol();

}; // END Class ASStreamIterator

// Constructor Class ASStreamIterator
ASStreamIterator::ASStreamIterator(WString const &Src) : ASSourceIterator(), src(Src)
{
	// initializes buffer pointer
	p = 0;

	// initializes Eol Mode
	eolMode = crlf;

} // END Constructor Class ASStreamIterator


WString ASStreamIterator::getEol()
{
	switch(eolMode)
	{
		case cr:
			return "\r";
		case lf:
			return "\n";
		case lfcr:
			return "\r\n";
		default:
			return "\n\r";
	}

} // END ASStreamIterator::getEol()

bool ASStreamIterator::hasMoreLines() const
{
	return src[p] != 0;

} // END ASStreamIterator::hasMoreLines()

WString ASStreamIterator::nextLine()
{
	int p2 = p;
	wchar c1, c2;
	while(src[p2] && src[p2] != '\n' && src[p2] != '\r')
		p2++;
	WString line = src.Mid(p, p2 - p);

	if( (c1 = src[p2]) != 0)
	{
		p2++;
		c2 = src[p2];
		if(c1 == '\r' && c2 == '\n')
		{
			p2++;
			eolMode = crlf;
		}
		else if(c1 == '\n' && c2 == '\r')
		{
			p2++;
			eolMode = lfcr;
		}
		else if(c1 == '\n')
			eolMode = lf;
		else
			eolMode = cr;
	}
	p = p2;

	return line;

} // END ASStreamIterator::nextLine()

/////////////////////////////////////////////////////////////////////////////////////////
// Formats a string of code with a given formatter
WString Project::FormatCodeString(WString const &Src, ASFormatter &Formatter)
{
	// Creates the output string
	WString Dest;

	// Creates the iterator for the formatter
	ASStreamIterator streamIterator(Src);

	// Initializes the formatter to work on selected stream
	Formatter.init(&streamIterator);

	// Processes all text in source file and put them in output file
	while (Formatter.hasMoreLines())
		Dest << Formatter.nextLine() << streamIterator.getEol();

	// returns output string
	return Dest;

} // END Ide::FormatCodeString()

/////////////////////////////////////////////////////////////////////////////////////////
// Formats editor's code with Ide format parameters
void Project::FormatCode()
{
	// get the global editor object
	auto &editor = FishIDEEditor;
	
	// do not format non-code stuffs
	if(editor.GetKind() != EditorKind::CODE)
		return;
	
	// Gets editor contents from editor
	WString Src;
	int l, h;
	bool sel = editor.GetSelection(l, h);
	if(sel)
		Src = editor.GetSelectionW();
	else
	 	Src = editor.GetW();

	// Instantiate the formatter object
	ASFormatter Formatter;

	// Sets up the formatter for C++ formatting
	Formatter.setCStyle();

	///////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Sets up astyle options
	Formatter.setBracketIndent(Settings.GetAstyleBracketIndent());
	Formatter.setNamespaceIndent(Settings.GetAstyleNamespaceIndent());
	Formatter.setBlockIndent(Settings.GetAstyleBlockIndent());
	Formatter.setCaseIndent(Settings.GetAstyleCaseIndent());
	Formatter.setClassIndent(Settings.GetAstyleClassIndent());
	Formatter.setLabelIndent(Settings.GetAstyleLabelIndent());
	Formatter.setSwitchIndent(Settings.GetAstyleSwitchIndent());
	Formatter.setPreprocessorIndent(Settings.GetAstylePreprocessorIndent());
	Formatter.setMaxInStatementIndentLength(Settings.GetAstyleMaxInStatementIndentLength());
	Formatter.setMinConditionalIndentLength(Settings.GetAstyleMinInStatementIndentLength());
	Formatter.setBreakClosingHeaderBracketsMode(Settings.GetAstyleBreakClosingHeaderBracketsMode());
	Formatter.setBreakElseIfsMode(Settings.GetAstyleBreakElseIfsMode());
	Formatter.setBreakOneLineBlocksMode(Settings.GetAstyleBreakOneLineBlocksMode());
	Formatter.setSingleStatementsMode(Settings.GetAstyleSingleStatementsMode());
	Formatter.setBreakBlocksMode(Settings.GetAstyleBreakBlocksMode());
	Formatter.setBreakClosingHeaderBlocksMode(Settings.GetAstyleBreakClosingHeaderBlocksMode());
	Formatter.setBracketFormatMode((astyle::BracketMode)Settings.GetAstyleBracketFormatMode());
	switch(Settings.GetAstyleParensPaddingMode())
	{
		case PAD_INSIDE :
			Formatter.setParensInsidePaddingMode(true);
			Formatter.setParensOutsidePaddingMode(false);
			break;
		case PAD_OUTSIDE :
			Formatter.setParensInsidePaddingMode(false);
			Formatter.setParensOutsidePaddingMode(true);
			break;
		case PAD_BOTH :
			Formatter.setParensInsidePaddingMode(true);
			Formatter.setParensOutsidePaddingMode(true);
			break;
		default :
			Formatter.setParensOutsidePaddingMode(false);
			Formatter.setParensInsidePaddingMode(false);
			break;
	}
	Formatter.setParensUnPaddingMode(Settings.GetAstyleParensUnPaddingMode());
	Formatter.setOperatorPaddingMode(Settings.GetAstyleOperatorPaddingMode());
	Formatter.setEmptyLineFill(Settings.GetAstyleEmptyLineFill());
	Formatter.setTabSpaceConversionMode(Settings.GetAstyleTabSpaceConversionMode());

/* @@
	if(!indent_spaces)
		Formatter.setTabIndentation(editortabsize, true);
	else
		Formatter.setSpaceIndentation(indent_amount);
*/
	Formatter.setTabIndentation(4, true);

	WString Dest = FormatCodeString(Src, Formatter);

	editor.NextUndo();
	if(sel) {
		editor.Remove(l, h - l);
		editor.SetSelection(l, l + editor.Insert(l, Dest));
	}
	else {
		editor.Remove(0, editor.GetLength());
		editor.Insert(0, Dest);
	}
}
