#include <ctype.h>
#include <string.h>
#include "eruta.h"
#include "str.h"
#include "bad.h"
#include "mem.h"

/* str contains utf-8 enables strings. It's currently nothing 
but a thin layer over Allegro's string library since 
that's pretty nice and UTF-8 enabled, unlike bstring (and 
it uses bsytrig internallly, so, no sense in having 2 string libraries :) 
The idea of the thin wrapper is
a) To limit typing, strings are used everywhere so I want short names for the functions
b) To enable me to replace the string functions in case I want to reuse 
them in something that doesn't use Allegro. 
*/

/** Strdup is not ANSI standard so have to make my own version. */
char * cstr_dup(char * str) {
  size_t len = strlen(str);
  char * result = malloc(len + 1);
  if (!result) return NULL;
  strncpy(result, str, len);
  result[len] = '\0';
  return result;
}


/** Converts an US string to a number as per atoi. */
int ustr_atoi(USTR * str) {
  return atoi(ustr_c(str));
}

/* Appends N characters from the C string str. 
The C string is first converted to a USTR, and the ntne characters are counted. 
XXX: todo how to do this efficiently without losing any unicode data? 
*/


/** Converts a ustr to s double by using strtod. Return 0 and sets OK to
NULL if conversion failed for some reason. If OK is NULL, it is ignored.  */
double ustr_tod(USTR * ustr, int * ok) {
  char * aid = NULL;
  const char * cstr;
  double res;
  if(!ustr) goto error; 
  errno = 0;
  cstr  = ustr_c(ustr);
  res   = strtod(cstr, &aid);
  if (aid   == cstr) goto error;
  if (errno == ERANGE) goto error;
  
  // all was fine here, return ok 
  if(ok) *ok = TRUE;
  return res;
  error: // if ze get here there was a conversion error
  if(ok) *ok = FALSE; 
  return 0;
}

/** Converts a ustr to s double by using strtol. Return 0 and sets OK to
NULL if conversion failed for some reason. If OK is NULL, it is ignored.  */
long ustr_tol(USTR * ustr, int * ok, int base) {
  char * aid = NULL;
  const char * cstr;
  long res;
  if(!ustr) goto error; 
  errno = 0;
  cstr  = ustr_c(ustr);
  res   = strtol(cstr, &aid, base);
  if (aid   == cstr) goto error;
  if (errno == ERANGE) goto error;
  
  // all was fine here, return ok 
  if(ok) *ok = TRUE;
  return res;
  error: // if ze get here there was a conversion error
  if(ok) *ok = FALSE; 
  return 0;
}

/** Converts an ustr to a double without doing any error checking. */
double ustr_atod(USTR * ustr) {
  return ustr_tod(ustr, NULL);
}

/** Converts an ustr to a long without doing any error checking. 
    Uses the decimal base. 
*/
double ustr_atol(USTR * ustr) {
  return ustr_tol(ustr, NULL, 10);
}

/** Makes a new ustr from the double. Must be freed with ustr_free */
USTR * ustr_newdouble(double d) {
  return ustr_newf("%lf", d);
}

/** Makes a new ustr from a long. Must be freed with ustr_free */
USTR * ustr_newlong(long l) {
  return ustr_newf("%ld", l);
}


/** Helper for calling any of the isXXX functions 
based on the first character expression string
a call isalnum on ch and return the result.
A call isalpha on ch and return the result.
b call isblank on ch and return the result.
c call iscntrl on ch and return the result.
d call isdigit on ch and return the result.
g call isgraph on ch and return the result.
l call islower on ch and return the result.
p call isprint on ch and return the result.
P call ispunct on ch and return the result.
u call isspace on ch and return the result.
x call isxdigit on ch and return the result.
any other first character: return FALSE.
*/
int cstr_charis(const char * expression, int ch) {
  char first;
  if (!expression) return FALSE;
  first = expression[0];
  switch(first) {
    case 'a': return isalnum(ch);
    case 'A': return isalpha(ch);
    case 'b': return isblank(ch);
    case 'c': return iscntrl(ch);
    case 'd': return isdigit(ch);
    case 'g': return isgraph(ch);
    case 'l': return islower(ch);
    case 'p': return isprint(ch);
    case 'P': return ispunct(ch);
    case 's': return isspace(ch);
    case 'u': return isupper(ch);
    case 'x': return isxdigit(ch);
    default: return FALSE;
  }
  // cannot reach this place
}



/** Ultra-simple matching of a single character ch
versus a string expression of allowed characters or instructions. 
The expression is interpreted as follows: 
$a call isalnum on ch and return the result.
$A call isalpha on ch and return the result.
$b call isblank on ch and return the result.
$c call iscntrl on ch and return the result.
$d call isdigit on ch and return the result.
$g call isgraph on ch and return the result.
$l call islower on ch and return the result.
$p call isprint on ch and return the result.
$P call ispunct on ch and return the result.
$s call isspace on ch and return the result.
$u call isupper on ch and return the result.
$x call isxdigit on ch and return the result.
@ must be followed by a list of characters, strchr is called on that list.
^ must be followed by a list of characters, strchr is called on that list,
  and it's logical opposite is returned.
. matches any character and always returns true.
empty string or NULL: matches nothing and always returns false.
A ! prefix to any of these means to apply the 
C ! operator to the rest of the expression's result.
If expression starts with any other character, FALSE is returned as well.
*/
int cstr_simplematch(const char * expression, int ch) {
  char first;
  if (!expression) return FALSE;
  first = expression[0];
  switch(first) {
    case '\0': return FALSE;
    case '!' : return !cstr_simplematch(expression+1, ch);
    case '@' : return (strchr(expression+1, ch) != NULL);
    case '^' : return (strchr(expression+1, ch) == NULL);
    case '.' : return TRUE;
    case '$' : return cstr_charis(expression+1, ch);
    default: return FALSE;
  }
  // can't reach here    
}

USTR * ustrlistnode_ustr(USTRListNode * self) {
  if(!self) return NULL;
  return self->ustr;
}


USTRListNode * ustrlistnode_alloc(void) {
  return STRUCT_ALLOC(USTRListNode);
}

USTRListNode * ustrlistnode_init(USTRListNode * self, const USTR * ustr) {
  if(!self) return NULL;
  self->ustr = ustr_dup(ustr);
  badlistnode_initempty(&self->list);
  return self;
}

USTRListNode * ustrlistnode_initcstr(USTRListNode * self, const char * cstr) {
  if(!self) return NULL;
  self->ustr = ustr_new(cstr);
  badlistnode_initempty(&self->list);
  return self;
}

USTRListNode * ustrlistnode_new(const USTR * ustr) {
  return ustrlistnode_init(ustrlistnode_alloc(), ustr);
}

USTRListNode * ustrlistnode_newcstr(const char * cstr) {
  return ustrlistnode_initcstr(ustrlistnode_alloc(), cstr);
}


USTRListNode * ustrlistnode_done(USTRListNode * self) {
  if(!self) return NULL;
  ustr_free(self->ustr);
  badlistnode_initempty(&self->list);
  return self;
}


USTRListNode * badlistnode_ustrlistnode(BadListNode * elem) {
  if(!elem) return NULL;
  return bad_container(elem, USTRListNode, list);
}

USTRListNode * ustrlistnode_next(USTRListNode * self) {
  BadListNode * bnode;
  if (!self) return NULL;
  bnode = badlistnode_next(&self->list);
  return badlistnode_ustrlistnode(bnode);
}

USTRListNode * ustrlistnode_prev(USTRListNode * self) {
  BadListNode * bnode;
  if (!self) return NULL;
  bnode = badlistnode_prev(&self->list);
  return badlistnode_ustrlistnode(bnode);
}


USTRListNode * ustrlistnode_free(USTRListNode * self) {
  if(!self) return NULL;
  ustrlistnode_done(self);
  return mem_free(self);
}

USTRListNode * ustrlist_head(USTRList * self) {
  if(!self) return NULL;
  return badlistnode_ustrlistnode(self->head);
}

USTRListNode * ustrlist_tail(USTRList * self) {
  if(!self) return NULL;
  return badlistnode_ustrlistnode(self->tail);
}


USTRList * ustrlist_alloc(void) {
  USTRList * self;
  self = STRUCT_ALLOC(USTRList);
  return self;
}

USTRList * ustrlist_init(USTRList * self) {
  if (!self) return self;
  badlist_init(self);
  return self;
}

USTRList * ustrlist_new() {
  return ustrlist_init(ustrlist_alloc());
}

USTRList * ustrlist_done(USTRList * self) {
  BadListNode      * elem;
  USTRListNode * node;
  if(!self) return NULL;
  elem = badlist_head(self); 
  while(elem) {
    node = badlistnode_ustrlistnode(elem);
    elem = badlistnode_next(elem); 
    // here, elem is already pointing to next so node is safe to free
    ustrlistnode_free(node);
  }
  ustrlist_init(self);
  return self;
}


USTRList * ustrlist_free(USTRList * self) {
  if(!ustrlist_done(self)) return NULL;
  mem_free(self);
  return NULL;
}

USTRList * ustrlist_addnode(USTRList * self, USTRListNode * node) {
  if(!self) return NULL;
  badlist_add(self, &node->list);
  return self;
}

USTRList * ustrlist_shiftnode(USTRList * self, USTRListNode * node) {
  if(!self) return NULL;
  badlist_shift(self, &node->list);
  return self;
}


/* Removes the node from the list and frees it too. */
USTRList * ustrlist_removenode(USTRList * self, USTRListNode * node) {
  if(!self) return NULL;
  badlist_remove(self, &node->list);
  ustrlistnode_free(node);
  return self;
}


USTRListNode * ustrlist_addustr(USTRList * self, const USTR * ustr) {
  USTRListNode * node = ustrlistnode_new(ustr);
  if(!node) return NULL;
  ustrlist_addnode(self, node);
  return node;
}

USTRListNode * ustrlist_addcstr(USTRList * self, const char * cstr) {
  USTRListNode * node = ustrlistnode_newcstr(cstr);
  if(!node) return NULL;
  ustrlist_addnode(self, node);
  return node;
}

USTRListNode * ustrlist_shiftustr(USTRList * self, const USTR * ustr) {
  USTRListNode * node = ustrlistnode_new(ustr);
  if(!node) return NULL;
  ustrlist_shiftnode(self, node);
  return node;
}

USTRListNode * ustrlist_shiftcstr(USTRList * self, const char * cstr) {
  USTRListNode * node = ustrlistnode_newcstr(cstr);
  if(!node) return NULL;
  ustrlist_shiftnode(self, node);
  return node;
}


int ustrlist_size(USTRList * self) {
  return badlist_size(self);
}


USTRList * ustrlist_droplast(USTRList * self) {
  USTRListNode * node;
  node = ustrlist_tail(self);
  ustrlist_removenode(self, node);
  return self;
}

/* Joins the list to a newly allocated string. The result must be freed after 
use with (al_)ustr_free(). */
USTR * ustrlist_join(USTRList * self) {
  USTRListNode * node;
  USTR * result;
  result   = ustr_new("");
  for(node = ustrlist_head(self); node; node = ustrlistnode_next(node)) {
     ustr_append(result, ustrlistnode_ustr(node));
  }
  return result;
}

/* Joins the list to a newly allocated string, using sep as the separator.
The result must be freed after use with (al_)ustr_free(). */
USTR * ustrlist_joinwithcstr(USTRList * self, const char * sep) {
  int first = true;
  USTRListNode * node;
  USTR * result;
  result   = ustr_new("");
  for(node = ustrlist_head(self); node; node = ustrlistnode_next(node)) {
    if(first) { 
      first  = false;
    } else {
      ustr_appendcstr(result, sep);
    }
    ustr_append(result, ustrlistnode_ustr(node));
  }
  return result;
}

/* Joins the list to a newly allocated string, using ch as the separator.
The result must be freed after use with (al_)ustr_free(). */
USTR * ustrlist_joinwithch(USTRList * self, const char ch) {
  int first = true;
  USTRListNode * node;
  USTR * result;
  result   = ustr_new("");
  for(node = ustrlist_head(self); node; node = ustrlistnode_next(node)) {
    if(first) { 
      first  = false;
    } else {
      ustr_appendch(result, ch);
    }
    ustr_append(result, ustrlistnode_ustr(node));
  }
  return result;
}


/* Joins the list to a newly allocated string, using sep as the separator.
The result must be freed after use with (al_)ustr_free(). */
USTR * ustrlist_joinwithustr(USTRList * self, USTR * sep) {
  int first = true;
  USTRListNode * node;
  USTR * result;
  result = ustr_new("");
  for(node = ustrlist_head(self); node; node = ustrlistnode_next(node)) {
    if(first) { 
      first  = false;
    } else {
      ustr_append(result, sep);
    }
    ustr_append(result, ustrlistnode_ustr(node));
  }
  return result;
}

/* Retruns drom the list the node reached after skipping skip nodes from head. 
* Returns NULL if there sis no such node or on other failures.
*/
USTRListNode * ustrlist_skipnode(USTRList * self, int skip) { 
  USTRListNode * now ;
  // skip skip lines
  for (now = ustrlist_head(self); now && (skip > 0); skip --) {
    now = ustrlistnode_next(now); // move to next line.
  }
  return now;
}


typedef int USTRGetWidth(USTR const * ustr, void * data);

/** Splits the incoming c string into a list of ustrings, each which have a width 
less than width. The callback get_width is called to determine 
the size of the string as it is puzzled together. The string will be split on
spaces. Tabs are non-braking but will be replaced by spaces. Newlines will force a new line. 
*/
USTRList * ustrlist_splitcstr(const char * cstr, double width, 
                              USTRGetWidth *  get_width, void * data) {
  return NULL; /* Look for widget textinfo in stead. */
}


