#include <amsstring4/amsstring4.hpp>

namespace ams
{

//Updated convenience functions: updated for a more python style
amsarray<amsstring> splitlines(const amsstring &s)
{
    amsarray<amsstring> ret;

    int I,I1,I2;
    amsstring q;
    int mode;
    ams_chartype c;
  
    ret.resize(0);
  
    I1 = 0;
    I = 0;
    mode = 0;
    while(I<s.size())
    {
      c = s.at(I);
      if(mode==0 && c==ams_char_cr)
      {
        mode=1;
        I2 = I;
      }
      else if(mode==0 && c==ams_char_lf)
      {
        mode = 0;
        I2 = I;
        s.substring(I1,I2,&q);
        ret.push_back(q);
        I1 = I+1;
      }
      else if(mode==1 && c==ams_char_lf)
      {
        mode = 0;
        s.substring(I1,I2,&q);
        ret.push_back(q);
        I1 = I+1;
      }
      else if(mode==1)
      {
        mode = 0;
      }
  
      I = I + 1;
    }
    s.substring(I1,s.length,&q);
    ret.push_back(q);

    return ret;
}

amsarray<amsstring> split(const amsstring &s, const ams_chartype delimitchar)
{
    amsarray<amsstring> ret;
    amsstring q;
    ret.resize(0);
  
  
    int I = 0;
    int ind1;
    int ind2;
    //int cs;
    ind1 = 0;
    ams_chartype c;
    while(I<s.length)
    {
      c = s.cstring[I];
      //cs = 0;
      if(c==delimitchar)
      {
        ind2 = I;
        ind2 = max<int>(0,ind2);
        s.substring(ind1,ind2,&q);
        ret.push_back(q);
        ind1 = I+1;
        ind2 = I+1;
        ind1 = min<int>(s.length,ind1);
        ind2 = min<int>(s.length,ind2);
      }
  
      I = I + 1;
    }
    if(ind1<s.length&&s.cstring[ind1]!=delimitchar)
    {
      s.substring(ind1,s.length,&q);
      ret.push_back(q);
    }

    return ret;
}

// static int amsstring_strlen(const ams_chartype *c)
// {
//   int I;
//   if(c!=NULL)
//   {
//     I = 0;
//     while(c[I]!='\0')
//     {
//       I++;
//     }
//     return I;
//     //return strlen((const char*) c);
//   }
//   else
//     return 0;
// }

amsarray<amsstring> split(const amsstring &s, const ams_chartype *delimitstr)
{
    amsarray<amsstring> ret;

    amsstring q;
    int I;
    int ind1,ind2;
    int cs;
    int cnt;
    ams_chartype c;
    int N = amsstring_strlen(delimitstr);
    
    if(delimitstr!=NULL)
    {
      if(N>0)
      {
        ret.resize(0);
  
        ind1 = 0;
        I = 0;
        cs = 0;
        cnt = 0;
        while(I<s.size())
        {
          c = s.at(I);
          if(cs==0 && c==delimitstr[cnt])
          {
            cs=1;
            ind2 = I;
            cnt = cnt+1;
          }
          else if(cs==1 && c==delimitstr[cnt])
          {
            cs=1;
            cnt = cnt+1;
          }
          else if(cs==1 && c!=delimitstr[cnt])
          {
            cs = 0;
            ind2 = ind1;
            cnt = 0;
            I = I - 1; //step back so you can evaluate this char again
          }
  
          if(cnt>=N)
          {
            //cut string from (ind1,ind2)
            cs = 0;
  
            ind1 = min<int>(ind1,s.size());
            ind1 = max<int>(ind1,0);
            ind2 = min<int>(ind2,s.size());
            ind2 = max<int>(ind2,0);
            ind2 = max<int>(ind2,ind1);
  
            s.substring(ind1,ind2,&q);
            ret.push_back(q);
            
            ind1 = I + 1;
            ind2 = I + 1;
            cnt = 0;
          }
          I = I + 1;
        }
  
        if(ind1<s.size())
        {
          s.substring(ind1,s.size(),&q);
          ret.push_back(q);
        }
      } //N>0
      else
      {
        ret.push_back(s);
      }
    } //ret!=NULL

    return ret;
}

//splits into two guaranteed pieces
//if a delimitchar or delimitstr is not encountered, the first piece will contain the entire string and the second will be ""
amsarray<amsstring> splitfirst(const amsstring &s, const ams_chartype delimitchar)
{
    amsarray<amsstring> ret;
    int ind;

    ret.resize(2);
    ind = s.find(delimitchar);
    if(ind>=0)
    {
        ret[0] = s.substring(0,ind);
        ret[1] = s.substring(ind+1,s.length);
    }
    else
    {
        ret[0] = s;
        ret[1] = "";
    }

    return ret;
}

amsarray<amsstring> splitfirst(const amsstring &s, const ams_chartype *delimitstr)
{
    amsarray<amsstring> ret;
    int ind;

    ret.resize(2);

    ind = s.find(delimitstr);
    if(ind>=0)
    {
        ret[0] = s.substring(0,ind);
        ret[1] = s.substring(ind+amsstring_strlen(delimitstr),s.length);
    }
    else
    {
        ret[0] = s;
        ret[1] = "";
    }

    return ret;
}

//splits a string, not counting whitespaces between non-whitespace characters
amsarray<amsstring> splitwhitespace(const amsstring &s)
{
    amsarray<amsstring> ret;

    int I;
    int mode;
    ams_chartype c;
    int ind1 = 0;
    int ind2 = 0;
    I = 0;
    mode = 0;
    amsstring q;
    bool qf = 0;
  
    ret.resize(0);
    while(qf==0)
    {
        c = s.cstring[I];
        //if(!(isspace(c)||c==' '||c=='\t')&&mode==0)
        if(!(amsstring_isspace(c))&&mode==0)
        {
            mode = 1;
            ind1 = I;
            ind1 = ams::max<int>(0,ind1);
        }
        //if((isspace(c)||c==' '||c=='\t'||c=='\0')&&mode==1)
        if((amsstring_isspace(c))&&mode==1)
        {
            mode = 0;
            ind2 = I;
            ind2 = ams::min<int>(ind2,s.length);
            s.substring(ind1,ind2,&q);
            //printf("debug: %d, %s\n",I,q.cstring);
            ret.push_back(q);
        }
        if(I>=s.length)
        {
            qf = 1;
        }

        I = I + 1;
    }

    return ret;
}

amsstring stripwhitespace(const amsstring &s)
{
    amsstring ret;

    int I;
    int ind1;
    int ind2;
    ams_chartype c;
  
    ind1 = 0;
    ind2 = 0;
    for(I=0;I<s.length;I++)
    {
        c = s.cstring[I];
        //if(!(isspace(c)||c==' '||c=='\t'))
        if(!(amsstring_isspace(c)))
        {
            ind1 = I;
            break;
        }
        }
        for(I=s.length-1;I>=0;I--)
        {
        c = s.cstring[I];
        //if(!(isspace(c)||c==' '||c=='\t'))
        if(!(amsstring_isspace(c)))
        {
            ind2 = I+1;
            break;
        }
    }

    ind1 = min<int>(ind1,s.size());
    ind1 = max<int>(ind1,0);
    ind2 = min<int>(ind2,s.size());
    ind2 = max<int>(ind2,0);
    ind2 = max<int>(ind1,ind2);
  
    ret = s.substring(ind1,ind2);

    return ret;
}

amsstring stripallwhitespace(const amsstring &s)
{
    amsstring ret;

    ret.resize(s.length);
    //int count;
    int I;
    int J;
    J = 0;
    ams_chartype c;
    for(I=0;I<s.length;I++)
    {
      c = s.cstring[I];
      //if(!(isspace(c)||c==' '||c=='\t'||c=='\n'||c=='\r'||c=='\f'||c=='\v'||(int)c==9||(int)c==10||(int)c==11||(int)c==12))
      if(!(amsstring_isspace(c)))
      {
        ret.cstring[J] = s.cstring[I];
        J = J + 1;
      }
    }
    ret.resize(J);
    ret.cstring[ret.length]='\0';

    return ret;
}

//splits into two guaranteed pieces
//if an extension separator . is not encountered, the first piece will contain the entire string, the second ""
amsarray<amsstring> path_splitext(const amsstring &s)
{
    amsarray<amsstring> ret;
    ret.resize(2);

    int ind;
    
    ind = s.findright('.');
    if(ind<0)
    {
        ret[0] = s;
        ret[1] = "";
    }
    else
    {
        ret[0] = s.substring(0,ind);
        ret[1] = s.substring(ind,s.length);
    }
    
    return ret;
}

//splits into two guaranteed pieces
//if a path separator is not encountered, the second piece will contain the entire string, and the first ""
amsarray<amsstring> path_split(const amsstring &s)
{
    amsarray<amsstring> ret;
    ret.resize(2);
    int ind1,ind2;

    ind1 = s.findright('/');
    ind2 = s.findright('\\');
    if(ind1>=0)
    {
        if(ind1==0)
        {
            ret[0] = "/";
        }
        else
        {
            ret[0] = s.substring(0,ind1);
        }
        ret[1] = s.substring(ind1+1,s.length);
    }
    else if(ind2>=0)
    {
        ret[0] = s.substring(0,ind2);
        ret[1] = s.substring(ind2+1,s.length);
    }
    else
    {
        ret[0] = "";
        ret[1] = s;
    }
    

    return ret;
}

//selects between the outermost set of delimitleft and delimitright chars
amsstring select_between(const amsstring &s, const ams_chartype delimitleft, const ams_chartype delimitright)
{
    amsstring ret;

    int ind1,ind2;

    ind1 = s.find(delimitleft);
    ind2 = s.findright(delimitright);

    if(ind1<0) ind1=-1;
    if(ind2<0) ind2=s.length;

    ret = s.substring(ind1+1,ind2);

    return ret;
}

//selects between the outermost set of delimitleft and delimitright chars
amsstring select_between(const amsstring &s, const ams_chartype *delimitleft, const ams_chartype *delimitright)
{
    amsstring ret;

    int ind1,ind2;
    int slen1,slen2;

    slen1 = amsstring_strlen(delimitleft);
    slen2 = amsstring_strlen(delimitright);
    
    ind1 = -1;
    ind2 = -1;
    if(slen1>0)
      ind1 = s.find(delimitleft);
    if(slen2>0)
      ind2 = s.findright(delimitright);

    if(ind1<0) ind1=-slen1;
    if(ind2<0) ind2=s.length;

    ret = s.substring(ind1+slen1,ind2);

    return ret;
}

amsstring concatenate(const amsarray<amsstring> &slist)
{
    amsstring ret;

    int I;
    ret.resize(0);
    for(I=0;I<slist.length;I++)
    {
        ret.append(slist[I]);
    }

    return ret;
}

amsstring concat_lines(const amsarray<amsstring> &slist)
{
    amsstring ret;
    amsstring s;
    int I;
    ret.resize(0);
    for(I=0;I<slist.length;I++)
    {
        s = slist[I];
        s=s+"\n"; //maybe check to see if a newline already exists for this line?
        ret.append(s);
    }

    return ret;
}


//splits a string into contiguous sets of alphanumeric characters delimited by any non-alphanum chars
//for eventual conversion of space,tab,comma separated numbers into numeric tuples and arrays
//This step does not check that each string is a valid number.
amsarray<amsstring> splitalphanum(const amsstring &s)
{
  amsarray<amsstring> sa;
  int I0,I1,I;
  int mode;
  ams_chartype c;
  amsstring s2;

  mode = 0;
  for(I=0;I<s.length;I++)
  {
    c = s[I];
    if(mode==0 && amsstring_ischarnumeric(c))
    {
      mode = 1;
      I0 = I;
    }

    if(mode==1 && (!amsstring_ischarnumeric(c) || I>=(s.length-1)))
    {
      if(I>=(s.length-1))
        I1 = s.length;
      else
        I1 = I;
      s2 = s.substring(I0,I1);
      sa.append(s2);
      mode = 0;
    }
  }

  return sa;
}

//strips \r and \n from the right side of the string
amsstring stripnewlines(const amsstring &s)
{
  amsstring ret;
  int I0,I;
  I0 = s.length;
  for(I=s.length-1;I>=0;I--)
  {
    if(s[I]!='\n' && s[I]!='\r')
    {
      I0 = I+1;
      break;
    }
  }
  ret = s.substring(0,I0);
  return ret;
}

//strips \r and \n from the right side of the strings
void stripnewlines(amsarray<amsstring> *sa_inout)
{
  int I;
  for(I=0;I<sa_inout->length;I++)
  {
    sa_inout->at(I) = stripnewlines(sa_inout->at(I));
  }
  return;
}

///////////////////////////////////////////////////////
// Member Function Versions of Convenience Functions //
///////////////////////////////////////////////////////

amsarray<amsstring> amsstring::splitlines() const
{
  return ams::splitlines(*this);
}

amsarray<amsstring> amsstring::split(const ams_chartype delimitchar) const
{
  return ams::split(*this,delimitchar);
}

amsarray<amsstring> amsstring::split(const ams_chartype *delimitstr) const
{
  return ams::split(*this,delimitstr);
}

amsarray<amsstring> amsstring::splitfirst(const ams_chartype delimitchar) const
{ 
  return ams::splitfirst(*this,delimitchar);
}

amsarray<amsstring> amsstring::splitfirst(const ams_chartype *delimitstr) const
{
  return ams::splitfirst(*this,delimitstr);
}

amsarray<amsstring> amsstring::splitwhitespace() const
{
  return ams::splitwhitespace(*this);
}

amsstring amsstring::stripwhitespace() const
{
  return ams::stripwhitespace(*this);
}

amsstring amsstring::stripallwhitespace() const
{
  return ams::stripallwhitespace(*this);
}

amsstring amsstring::stripnewlines() const
{
  return ams::stripnewlines(*this);
}

amsarray<amsstring> amsstring::splitalphanum() const
{
  return ams::splitalphanum(*this);
}

amsarray<amsstring> amsstring::path_splitext() const
{
  return ams::path_splitext(*this);
}

amsarray<amsstring> amsstring::path_split() const
{
  return ams::path_split(*this);
}

amsstring amsstring::select_between(const ams_chartype *delimitleft, const ams_chartype *delimitright) const
{
  return ams::select_between(*this,delimitleft,delimitright);
}


/////////////
// Testing //
/////////////

void amsstring4_test_findright()
{
  amsstring s1,s2;
  int I;
  int I1,I2,I3,I4;

  s1 = "sdkfjskdfksd";
  for(I=-1;I<s1.length+2;I++)
  {
    s2 = s1;
    s2.insert(I,".");
    I1 = s2.find('.');
    I2 = s2.find(".");
    I3 = s2.findright('.');
    I4 = s2.findright(".");

    printf("s = '%s'\n",s2.cstring);
    printf("Found . at (%d,%d,%d,%d)\n",I1,I2,I3,I4);
  }

  printf("\n\n");
  for(I=-1;I<s1.length+2;I++)
  {
    s2 = s1;
    s2.insert(I,".");
    s2.insert(I+3,".");
    I1 = s2.find('.');
    I2 = s2.find(".");
    I3 = s2.findright('.');
    I4 = s2.findright(".");

    printf("s = '%s'\n",s2.cstring);
    printf("Found . at (%d,%d,%d,%d)\n",I1,I2,I3,I4);
  }

  printf("\n\n");
  for(I=-1;I<s1.length+2;I++)
  {
    s2 = s1;
    s2.insert(I,"FND");
    I1 = s2.find("FND");
    I2 = s2.findright("FND");

    printf("s = '%s'\n",s2.cstring);
    printf("Found . at (%d,%d)\n",I1,I2);
  }

  printf("\n\n");
  for(I=-1;I<s1.length+2;I++)
  {
    s2 = s1;
    s2.insert(I,"FND");
    s2.insert(I+5,"FND");
    I1 = s2.find("FND");
    I2 = s2.findright("FND");
    I3 = s2.find("FNF");
    I4 = s2.findright("FNF");

    printf("s = '%s'\n",s2.cstring);
    printf("Found . at (%d,%d,%d,%d)\n",I1,I2,I3,I4);
  }
}

void amsstring4_test_convenience1a()
{
  amsstring s1,s2;
  amsarray<amsstring> sa;
  int I;

  s1 = "a string\nwith multiple\nlines\n\r";
  sa = splitlines(s1);
  for(I=0;I<sa.length;I++)
  {
    printf("%d:%d: %s\n",I,sa[I].length,sa[I].cstring);
  }

  printf("\n\n");
  s1 = " some, comma, separated,variables,,";
  sa = split(s1,',');
  for(I=0;I<sa.length;I++)
  {
    printf("%d:%d: %s\n",I,sa[I].length,sa[I].cstring);
  }

  printf("\n\n");
  s1 = " some:: oddly:: separated:variables::";
  sa = split(s1,"::");
  for(I=0;I<sa.length;I++)
  {
    printf("%d:%d: %s\n",I,sa[I].length,sa[I].cstring);
  }

  printf("\n\n");
  s1 = "  a string  with lots\tof\t\twhitespace ";
  sa = splitwhitespace(s1);
  for(I=0;I<sa.length;I++)
  {
    printf("%d:%d: %s\n",I,sa[I].length,sa[I].cstring);
  }

  s2 = stripwhitespace(s1);
  printf("s1 = '%s'\ns2='%s'\n",s1.cstring,s2.cstring);
  s2 = stripallwhitespace(s1);
  printf("s3 = '%s'\n",s2.cstring);
  
  s1 = "astring.with.separatorchars";
  printf("'%s' split: '%s' '%s'\n",s1.cstring,splitfirst(s1,'.')[0].cstring, splitfirst(s1,'.')[1].cstring);
  s1 = "astring_without_separatorchars";
  printf("'%s' split: '%s' '%s'\n",s1.cstring,splitfirst(s1,'.')[0].cstring, splitfirst(s1,'.')[1].cstring);
  
  s1 = "astring::with::separator:strs";
  printf("'%s' split: '%s' '%s'\n",s1.cstring,splitfirst(s1,"::")[0].cstring, splitfirst(s1,"::")[1].cstring);
  s1 = "astring_without_separatorstrs";
  printf("'%s' split: '%s' '%s'\n",s1.cstring,splitfirst(s1,"::")[0].cstring, splitfirst(s1,"::")[1].cstring);

  s1 = "somefilewithoutextension";
  sa = path_splitext(s1);
  printf("path_splitext('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "somefilewithalittleext.";
  sa = path_splitext(s1);
  printf("path_splitext('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "somefilewith.anextension";
  sa = path_splitext(s1);
  printf("path_splitext('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "somefilewith.many.extensions";
  sa = path_splitext(s1);
  printf("path_splitext('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "somepathnoseparators";
  sa = path_split(s1);
  printf("path_split('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "\\someotherpath";
  sa = path_split(s1);
  printf("path_split('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "/someotherpath";
  sa = path_split(s1);
  printf("path_split('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "C:\\someotherpath";
  sa = path_split(s1);
  printf("path_split('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "C:\\some\\other\\path";
  sa = path_split(s1);
  printf("path_split('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);

  s1 = "/some/other/path";
  sa = path_split(s1);
  printf("path_split('%s') = ['%s','%s']\n",s1.cstring,sa[0].cstring,sa[1].cstring);
}   

void amsstring4_test_convenience1b()
{
  amsstring s1,s2;
  amsarray<amsstring> sa;
  int I;

  s1 = "1e2e3,45, 6.7E8,-9.13E24 ";
  sa = s1.splitalphanum();
  for(I=0;I<sa.length;I++)
  {
    printf("s[%d] = '%s'\n",I,sa[I].cstring);
  }

  s1 = "arandomtuple = (apples,1.2,pi);";
  s2 = s1.select_between("(",")");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "arandomtuple = {apples,1.2,pi);";
  s2 = select_between(s1,"(",")");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "arandomtuple = (apples,1.2,pi};";
  s2 = select_between(s1,"(",")");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "arandomtuple) = (apples,1.2,pi};";
  s2 = select_between(s1,"(",")");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "arandomtuple) = (apples,1.2,pi};";
  s2 = select_between(s1,'(',')');
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "kernellaunch<<<nblocks,nthreads>>>(apples,1.2,pi);";
  s2 = select_between(s1,"<<<",">>>");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "kernellaunch<<<nblocks,nthreads>>>(apples,1.2,pi);";
  s2 = select_between(s1,"<<<","");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  s1 = "kernellaunch<<<nblocks,nthreads>>>(apples,1.2,pi);";
  s2 = s1.select_between("",">>>");
  printf("s1='%s'\ns2='%s'\n",s1.cstring,s2.cstring);

  sa.resize(0);
  sa.append("this");
  sa.append(" is");
  sa.append("a ");
  sa.append("series\n");
  sa.append("of\r\n");
  sa.append("strings.");

  s1 = concatenate(sa);
  printf("\nconcatenate(sa) = '%s'\n---\n",s1.cstring);
  
  s1 = concat_lines(sa);
  printf("\nconcat_lines(sa) = '%s'\n----\n",s1.cstring);

  stripnewlines(&sa);
  s1 = concat_lines(sa);
  printf("\nconcat_lines(sa) = '%s'\n----\n",s1.cstring);
}

void amsstring4_test_convenience1c()
{
  amsstring s1,s2;
  amsarray<amsstring> sa;
  int I;

  s1 = "1.2,   1.3, 6.21E7, 1.1 ";
  sa = s1.splitalphanum();
  for(I=0;I<sa.length;I++)
  {
    printf("sa[%d]='%s'\n",I,sa[I].cstring);
  }

}

};//end namespace ams



/*
Examples of numeric literals categorized by type:

    Integer Literals (Decimal): 0, 123, 45678, -99
    Floating-Point Literals (Decimal): 0.0, 1.0, 3.14159, -0.01, 123.456
    Scientific Notation (Exponential): 1.2E3, 1.2e-3, -1.2E3, -1.2E-3 (equivalent to 

, etc.)
Hexadecimal Literals (Base 16): 0x7B, 0x1A, 0xCAFEBABE (prefixed with 0x or 0X)
Binary Literals (Base 2): 0b1111011, 0B101 (prefixed with 0b or 0B)
Octal Literals (Base 8): 0173, 0755 (prefixed with 0 in many languages)
Long/Typed Literals: 12345L, 100u, 50LL (suffixes define specific data types)
Formatted Literals: 1_000_000, 232_45_4519 (underscores for readability, e.g., in Java/Scala) 
*/