#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif

#include "Elementary.h"

#include "elm_code_private.h"

EAPI const char *
elm_code_line_text_get(Elm_Code_Line *line, unsigned int *length)
{
   if (!line)
     {
        if (length)
          *length = 0;
        return NULL;
     }

   if (length)
     *length = line->length;

   if (!line->length)
     return "";

   if (line->modified)
     return line->modified;
   return line->content;
}

EAPI void
elm_code_line_text_set(Elm_Code_Line *line, const char *chars, unsigned int length)
{
   Elm_Code_File *file;
   char *newtext, *oldtext = NULL;

   if (!line)
     return;

   if (line->modified)
     oldtext = line->modified;

   newtext = malloc(sizeof(char) * length);
   strncpy(newtext, chars, length);
   line->modified = newtext;
   line->length = length;

   if (oldtext)
     free(oldtext);

   file = line->file;
   if (file->parent)
     {
        _elm_code_parse_line(file->parent, line);
        elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
     }
}

EAPI int
elm_code_text_strnpos(const char *content, unsigned int length, const char *search, int offset)
{
   unsigned int searchlen, c;
   char *ptr;

   searchlen = strlen(search);
   ptr = (char *) content;

   if (searchlen > length)
     return ELM_CODE_TEXT_NOT_FOUND;

   ptr += offset;
   for (c = offset; c <= length - searchlen; c++)
     {
        if (!strncmp(ptr, search, searchlen))
          return c;

        ptr++;
     }

   return ELM_CODE_TEXT_NOT_FOUND;
}

EAPI int
elm_code_line_text_strpos(Elm_Code_Line *line, const char *search, int offset)
{
   unsigned int length = 0;
   const char *content;

   content = elm_code_line_text_get(line, &length);
   return elm_code_text_strnpos(content, length, search, offset);
}

EAPI Eina_Bool
elm_code_line_text_contains(Elm_Code_Line *line, const char *search)
{
   return elm_code_line_text_strpos(line, search, 0) != ELM_CODE_TEXT_NOT_FOUND;
}

EAPI char *
elm_code_line_text_substr(Elm_Code_Line *line, unsigned int position, int length)
{
   const char *content;

   if (!line || length < 1)
     return strdup("");

   if (position + length > line->length)
     length = line->length - position;

   content = elm_code_line_text_get(line, NULL);
   return eina_strndup(content + position, length);
}

static void
_elm_code_line_tokens_move_right(Elm_Code_Line *line, int position, int move)
{
   Eina_List *item;
   Elm_Code_Token *token;

   EINA_LIST_FOREACH(line->tokens, item, token)
     {
        if (token->end >= position)
          token->end += move;

        if (token->start > position)
          token->start += move;
     }
}

static void
_elm_code_line_tokens_move_left(Elm_Code_Line *line, int position, int move)
{
   Eina_List *item, *next;
   Elm_Code_Token *token;

   EINA_LIST_FOREACH_SAFE(line->tokens, item, next, token)
     {
        if (token->end >= position)
          token->end -= move;

        if (token->start > position)
          token->start -= move;

        if (token->end < token->start)
          line->tokens = eina_list_remove_list(line->tokens, item);
     }
}

EAPI void
elm_code_line_text_insert(Elm_Code_Line *line, unsigned int position, const char *string, int length)
{
   Elm_Code_File *file;
   char *inserted;

   if (!line)
     return;

   inserted = malloc(sizeof(char) * line->length + length);
   if (position > line->length)
     position = line->length;

   _elm_code_line_tokens_move_right(line, position, length);

   if (line->modified)
     {
        strncpy(inserted, line->modified, position);
        strncpy(inserted + position, string, length);
        strncpy(inserted + position + length, line->modified + position, line->length - position);

        free(line->modified);
     }
   else
     {
        strncpy(inserted, line->content, position);
        strncpy(inserted + position, string, length);
        strncpy(inserted + position + length, line->content + position, line->length - position);
     }

   line->modified = inserted;
   line->length += length;

   file = line->file;
   if (file->parent)
     {
        _elm_code_parse_line(file->parent, line);
        elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
     }
}

EAPI void
elm_code_line_text_remove(Elm_Code_Line *line, unsigned int position, int length)
{
   Elm_Code_File *file;
   char *removed;

   if (!line || !line->length)
     return;

   removed = malloc(sizeof(char) * line->length - length);
   if (position > line->length)
     position = line->length;

   _elm_code_line_tokens_move_left(line, position, length);

   if (line->modified)
     {
        strncpy(removed, line->modified, position);
        strncpy(removed + position, line->modified + position + length, line->length - position - length);

        free(line->modified);
     }
   else
     {
        strncpy(removed, line->content, position);
        strncpy(removed + position, line->content + position + length, line->length - position - length);
     }

   line->modified = removed;
   line->length -= length;

   file = line->file;
   if (file->parent)
     {
        _elm_code_parse_line(file->parent, line);
        elm_code_callback_fire(file->parent, &ELM_CODE_EVENT_LINE_LOAD_DONE, line);
     }
}

Eina_Bool
_elm_code_text_char_is_whitespace(char c)
{
   return c == ' ' || c == '\t';
}

static unsigned int
_elm_code_text_trailing_whitespace_length(const char *text, unsigned int length)
{
   unsigned int count = 0;
   char *ptr;

   if (length == 0)
     return 0;

   ptr = (char *)text + length - 1;
   while (count < length)
     {
        if (!_elm_code_text_char_is_whitespace(*ptr))
          break;

        count++;
        ptr--;
     }

   return count;
}

EAPI void elm_code_line_text_leading_whitespace_strip(Elm_Code_Line *line)
{
   unsigned int length, count = 0;
   const char *content;
   char *ptr;

   content = elm_code_line_text_get(line, &length);
   if (length == 0)
     return;

   ptr = (char *)content;
   while (count < length)
     {
        if (!_elm_code_text_char_is_whitespace(*ptr))
          break;

        count++;
        ptr++;
     }

   elm_code_line_text_remove(line, 0, count);
}

EAPI void elm_code_line_text_trailing_whitespace_strip(Elm_Code_Line *line)
{
   unsigned int length = 0;
   unsigned int trailing;
   const char *content;

   content = elm_code_line_text_get(line, &length);
   trailing = _elm_code_text_trailing_whitespace_length(content, length);
   if (trailing == 0)
     return;

   elm_code_line_text_remove(line, length - trailing, trailing);
}

/* generic text functions */

EAPI int
elm_code_text_newlinenpos(const char *text, unsigned int length, short *nllen)
{
   int lfpos, crpos;
   int check;

   if (nllen)
     *nllen = 1;
   lfpos = elm_code_text_strnpos(text, length, "\n", 0);
   check = length;
   if (lfpos != ELM_CODE_TEXT_NOT_FOUND)
     check = lfpos + 1;
   crpos = elm_code_text_strnpos(text, check, "\r", 0);

   if (lfpos == ELM_CODE_TEXT_NOT_FOUND && crpos == ELM_CODE_TEXT_NOT_FOUND)
     return ELM_CODE_TEXT_NOT_FOUND;

   if (crpos == ELM_CODE_TEXT_NOT_FOUND)
     return lfpos;
   if (lfpos == ELM_CODE_TEXT_NOT_FOUND)
     return crpos;

   if (nllen)
     *nllen = 2;
   if (lfpos < crpos)
     return lfpos;
   return crpos;
}

EAPI unsigned int
elm_code_text_is_whitespace(const char *text, unsigned int length)
{
   unsigned int leading;

   leading = _elm_code_text_trailing_whitespace_length(text, length);

   return leading == length;
}

