// Scintilla source code edit control
/** @file LexObjC.cxx
 ** Lexer for Objective C language.
 **
 ** Written by Don HO
 **/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <fcntl.h>
#include <windows.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"


inline bool isSpecialChar(const int ch)
{
	return (ch == '_' || ch == '&' || ch == 'é' || ch == '°' ||
			   ch == '"' || ch == '\'' || ch == '~' || ch == '-' ||
			   ch == 'è' || ch == 'ç' || ch == 'à' || ch == '{' ||
			   ch == '|' || ch == '`' || ch == '^' || ch == '@' ||
			   ch == '}' || ch == '!' || ch == '#' || ch == '\\' ||
			   ch == '?' || ch == '.' || ch == '+' || ch == '§' ||
			   ch == 'ù' || ch == '%' || ch == 'µ' ||
			   ch == '¤' || ch == '£' || ch == '$' || ch == '¨' );
}

inline bool isWordChar(const int ch)
{
	return (isalnum(ch) || isSpecialChar(ch));
}

inline bool isHexChar(const int ch)
{
	return ((ch >= '0') && (ch <= '9')) || 
	          ((ch >= 'A') && (ch <= 'F')) || 
	          ((ch >= 'a') && (ch <= 'f')) || 
	          (ch == 'X') || (ch == 'x') ;
}

inline bool AtEOL(Accessor &styler, unsigned int i)
{
	return (	styler[i] == '\n') ||
				((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
}


inline bool isWordStart(const int ch) 
{
	return isalnum(ch) || isSpecialChar(ch);
}

inline bool isOperator(char ch)
{
	if (isalnum(ch))
		return false;

	if (	ch == '\\' || ch == '(' || ch == ')' || ch == '=' ||
		    ch == '[' || ch == ']' || ch == ':' || ch == ';')
		return true;
	return false;
}

inline bool isNcFormatMacro(char *s)
{
	return ((s[0] == '#') && (strlen(s) == 3) && 
	           (isHexChar(s[1]) || (s[1] == 'n') || (s[1] == 'N')) && 
	           ((s[2] == 'c') || (s[1] == 'C')));
}

inline void getCurrentStr(unsigned int start, unsigned int end, Accessor &styler, char *s, int len)
{
	unsigned int i = 0;
	while ((i < end - start + 1) && (i < (unsigned int)(len-1)))
	{
		s[i] = styler[start + i];
		i++;
	}
	s[i] = '\0';
}
/*
static void ColouriseObjCLine(char *lineBuffer, unsigned int lengthLine, unsigned int startLine,
					unsigned int endPos, WordList *keywordlists[], Accessor &styler)
{
	//MessageBox(NULL, lineBuffer, "toto", MB_OK);
	WordList &directivesList	= *keywordlists[0];
	WordList &instrList		= *keywordlists[1];
	WordList &varTypeList		= *keywordlists[2];
	WordList &qualifierList		= *keywordlists[3];
	WordList &refDefList		= *keywordlists[4];
	WordList &userDefine1		= *keywordlists[5];
	WordList &userDefine2		= *keywordlists[6];

	unsigned int i = 0;
	int lastNonSpace = -1;
	unsigned int state = SCE_OBJC_DEFAULT;
	
	int startWordPre = 0;
	
	//bool hasParenthise = false;
	// Skip initial spaces
	while ((i < lengthLine) && isspacechar(lineBuffer[i])) 
	{
		i++;
	}

	//int currentLine = styler.GetLine(startPos);
	
	for ( ; i < lengthLine ; i++)
	{
		// Determine if the current state should terminate.
		switch (state)
		{
			case SCE_OBJC_NUMBER :
				if (!isHexChar(lineBuffer[i]))
				{
					if (isWordChar(lineBuffer[i]) && (!isOperator(lineBuffer[i])))
						state = SCE_OBJC_DEFAULT;
					styler.ColourTo(startLine + i - 1, state);
					state = SCE_OBJC_DEFAULT;
				}
				break;

			case SCE_OBJC_IDENTIFIER :
				if (!isWordChar(lineBuffer[i]) )
				{
					char s[100];
					getCurrentStr(styler.GetStartSegment(), startLine + i - 1, styler, s, sizeof(s));
					char *str = s + 1; // pour sauter '@'
					if ((s[0] == '@') && (directivesList.InList(str)))	state = (SCE_OBJC_DIRECTIVE);
					else if (instrList.InList(s))					state = (SCE_OBJC_INSTR);
					else if (varTypeList.InList(s))				state = (SCE_OBJC_VARTYPE);
					else if (qualifierList.InList(s))				state = (SCE_OBJC_QUALIFIER);
					else if (refDefList.InList(s))					state = (SCE_OBJC_REF_DEF);
					else if (userDefine1.InList(s))				state = (SCE_OBJC_UDERDEFINE1);
					else if (userDefine2.InList(s))				state = (SCE_OBJC_UDERDEFINE2);

					styler.ColourTo(startLine + i -1 , state);					
					state = (SCE_OBJC_DEFAULT);
				}
				break;
			case SCE_OBJC_STRING :
				
				//if ((lineBuffer[i] == '\"') && (i > 0) && (lineBuffer[i-1] != '@'))
				if ((lineBuffer[i] != '\"') && (lineBuffer[i-1] == '\"'))
				{
					//if ((i > 0) && (lineBuffer[i-1] != '@'))
					{
						styler.ColourTo(startLine + i - 1, state);
						state = (SCE_OBJC_DEFAULT);
					}
				}
				break;

			case SCE_OBJC_COMMENTLINE :
				if (lineBuffer[i+1] == '\0')
				{
					styler.ColourTo(startLine + i - 1, state);
					state = (SCE_OBJC_DEFAULT);
				}
				break;

			default:
			{}
		}

		// Determine if a new state should be entered.
		if (state == SCE_OBJC_DEFAULT)
		{	

			if (isWordStart(lineBuffer[i]))
			{
				styler.ColourTo(startLine + i - 1, state);
				if ((lineBuffer[i] == '@') && (lineBuffer[i + 1] == '\"'))
				{
					state = SCE_OBJC_STRING;
					i += 2;
				}
				else
					state = SCE_OBJC_IDENTIFIER;
			}


			else
			{
				state = (SCE_OBJC_DEFAULT);
			}
		}
	}
}
*/
static void ColouriseObjCDoc(	unsigned int startPos, int length, int initStyle,
					WordList *keywordlists[], Accessor &styler)
{
	//char lineBuffer[1024];
	WordList &directivesList	= *keywordlists[0];
	WordList &instrList		= *keywordlists[1];
	WordList &varTypeList		= *keywordlists[2];
	WordList &qualifierList		= *keywordlists[3];
	WordList &refDefList		= *keywordlists[4];
	WordList &userDefine1		= *keywordlists[5];
	WordList &userDefine2		= *keywordlists[6];

	styler.StartAt(startPos);
	styler.StartSegment(startPos);
	//unsigned int linePos = 0;
	unsigned int startLine = startPos;

	static unsigned int state = SCE_OBJC_DEFAULT;

	for (unsigned int i = startPos ;  i < startPos + length ; i++)
	{
		char charCourant = styler[i];
		//char charAvant = styler[i-1];
		char charProchain = styler[i+1];
		switch (state)
		{
			case SCE_OBJC_IDENTIFIER :
				if (!isWordChar(charCourant) )
				{
					char s[100];
					getCurrentStr(styler.GetStartSegment(), startPos + i - 1, styler, s, sizeof(s));
					char *str = s + 1; // pour sauter '@'
					if ((s[0] == '@') && (directivesList.InList(str)))	state = (SCE_OBJC_DIRECTIVE);
					else if (instrList.InList(s))					state = (SCE_OBJC_INSTR);
					else if (varTypeList.InList(s))				state = (SCE_OBJC_VARTYPE);
					else if (qualifierList.InList(s))				state = (SCE_OBJC_QUALIFIER);
					else if (refDefList.InList(s))					state = (SCE_OBJC_REF_DEF);
					else if (userDefine1.InList(s))				state = (SCE_OBJC_UDERDEFINE1);
					else if (userDefine2.InList(s))				state = (SCE_OBJC_UDERDEFINE2);

					styler.ColourTo(startPos + i -1 , state);					
					state = (SCE_OBJC_DEFAULT);
				}
				break;

			case SCE_OBJC_COMMENTLINE :
				if (styler[i-1] == '\n')
				{
					styler.ColourTo(startPos + i - 1, state);
					state = SCE_OBJC_DEFAULT;
				}
				break;

			case SCE_OBJC_COMMENT :
				if ((charCourant == '/') && (styler[i-1] == '*'))
				{
					styler.ColourTo(startPos + i, state);
					state = SCE_OBJC_DEFAULT;
				}
				break;
			default: {}
		}
		if (state == SCE_OBJC_DEFAULT)
		{
			if (charCourant == '/')
			{
				if (charProchain == '/')
				{
					styler.ColourTo(startPos + i - 1, state);
					state = SCE_OBJC_COMMENTLINE;
					i += 2;
				}
				else  if (isWordStart(charCourant))
				{
					styler.ColourTo(startPos + i - 1, state);
					state = SCE_OBJC_IDENTIFIER;
				}
/*
				else if(charProchain == '*')
				{
					styler.ColourTo(startPos + i - 1, state);
					state = SCE_OBJC_COMMENT;
					i += 2;
				}*/
			}
		}
	}
	styler.ColourTo(startPos + length - 1, state);
}

LexerModule lmObjectiveC(SCLEX_OBJC, ColouriseObjCDoc, "Objective-C");
