#ifndef __AMSSTRING4_HPP__
#define __AMSSTRING4_HPP__

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <vector>
#include <string>
#include <limits>
#include <locale>

#include <amsmathutil25/amsmathutil25.hpp>


namespace ams
{

static const int amsstring_success = 1;
static const int amsstring_failure = -1;

//wraps the functions strcpy_s and strncpy in a portable manner
//between linux and microsoft standard C libraries.
int amsstrcpy_s(char *dest, int size, const char *src);

//wraps the functions sprintf_s and snprintf in a portable manner
//between linux and microsoft standard C libraries.
int amssprintf_s(char *s, int n, const char *format, ...);

//Using the C library's sscanf function is more robust
//than atod or atof. It returns valid numbers for infs and nans
//Returns nan for any uninterpretable string
double amsstrtonum(const char *str);

typedef char ams_chartype;
static const ams_chartype ams_char_cr = (ams_chartype) '\r'; //carriage return
static const ams_chartype ams_char_lf = (ams_chartype) '\n'; //newline
static const ams_chartype ams_char_tb = (ams_chartype) '\t'; //tab
static const ams_chartype ams_char_nt = (ams_chartype) '\0'; //null terminator
static const ams_chartype ams_char_dq = (ams_chartype) '"'; //doublequote
static const ams_chartype ams_char_sq = (ams_chartype) '\''; //singlequote

ams_chartype tolower(ams_chartype c);
ams_chartype toupper(ams_chartype c);
bool amsstring_isspace(ams_chartype c);
int amsstring_strlen(const ams_chartype *c);
bool amsstring_ischarnumeric(const ams_chartype c);


class amsstring
{
public:
    ams_chartype *cstring;
    ams_chartype blank; // null terminator returned for accessing index out of bounds
    int length;
    //length will be set to the length of the cstring not including the null terminating char

    //Basic functions
    amsstring();
    ~amsstring();

    amsstring(const amsstring &other);
    amsstring& operator=(const amsstring& other);
    amsstring(amsstring &&other) noexcept;
    amsstring& operator=(amsstring &&other) noexcept;
    amsstring(const ams_chartype *other);
    const amsstring& operator=(const ams_chartype *other);
    
    //amsstring(int length);
    //amsstring(int length, const ams_chartype initchar);

    int resize(const int newlen);
    int size() const;

    ams_chartype& operator[](const int ind);
    const ams_chartype& operator[](const int ind) const;
    ams_chartype& at(const int ind);
    const ams_chartype& at(const int ind) const;

    void clear();
    void setall(const ams_chartype val, const int newlen);
    void shrinktofit();

    //string comparisons
    bool operator==(const amsstring &other) const;
    bool operator==(const char *other) const;
    bool operator!=(const amsstring &other) const;
    bool operator!=(const char *other) const;

    //string ordering comparison
    //alphebetizes strings by ASCII character
    //longer strings compare larger than shorter ones
    bool operator<(const amsstring &other) const;
    bool operator>(const amsstring &other) const;
    bool operator<(const ams_chartype *other) const;
    bool operator>(const ams_chartype *other) const;

    //Insert, Remove, and Substring
    void insert(const int ind, const amsstring other);
    void insert(const int ind, const ams_chartype *other);
    void remove(const int ind);
    void remove(const int ind1, const int ind2);
    void substring(const int ind1, const int ind2, amsstring *sout) const;
    amsstring substring(const int ind1, const int ind2) const;

    //Append
    void append(const amsstring &other);
    void append(const ams_chartype *other);
    void append(const ams_chartype other);

    amsstring operator+(const amsstring &other) const;
    amsstring operator+(const ams_chartype *other) const;
    amsstring operator+(const ams_chartype other) const;

    //Find
    int find(const amsstring findstr, const int indstart=0, const bool casesens=1) const;
    int find(const ams_chartype *findstr, const int indstart=0, const bool casesens=1) const;
    int find(const ams_chartype c, const int indstart=0, const bool casesens=1) const;

    //todo
    // finds first instance starting from length-indstart-1 from the right
    int findright(const amsstring findstr, const int indstart=0, const bool casesens=1) const;
    int findright(const ams_chartype *findstr, const int indstart=0, const bool casesens=1) const;
    int findright(const ams_chartype c, const int indstart=0, const bool casesens=1) const;

    //formatted input
    int sprintf(int bufflen, const ams_chartype *formatstring, ...);

    void tolower();
    void toupper();
    amsstring lower() const;
    amsstring upper() const;

    bool isvalidnumber() const;
    double strtonum() const;

    //updated convenience functions as class members
    amsarray<amsstring> splitlines() const;
    amsarray<amsstring> split(const ams_chartype delimitchar) const;
    amsarray<amsstring> split(const ams_chartype *delimitstr) const;
    amsarray<amsstring> splitfirst(const ams_chartype delimitchar) const; //guaranteed size 2 array
    amsarray<amsstring> splitfirst(const ams_chartype *delimitstr) const; //guaranteed size 2 array
    amsarray<amsstring> splitwhitespace() const;
    amsstring stripwhitespace() const;
    amsstring stripallwhitespace() const;
    amsstring stripnewlines() const;
    amsarray<amsstring> splitalphanum() const;
    amsarray<amsstring> path_splitext() const;
    amsarray<amsstring> path_split() const;
    amsstring select_between(const ams_chartype *delimitleft, const ams_chartype *delimitright) const;
    


};

//needs work
//void splitlines(const amsstring *s, std::vector<amsstring> *lns);
void splitlines(const amsstring *s, ams::amsarray<amsstring> *lns);

//void split(const amsstring *s, const ams_chartype delimitchar, std::vector<amsstring> *lns);
void split(const amsstring *s, const ams_chartype delimitchar, ams::amsarray<amsstring> *lns);

//void split(const amsstring *s, const ams_chartype *delimitstr, std::vector<amsstring> *lns);
void split(const amsstring *s, const ams_chartype *delimitstr, ams::amsarray<amsstring> *lns);

//void split(const amsstring *s, amsstring *delimitstr, std::vector<amsstring> *lns);
void split(const amsstring *s, amsstring *delimitstr, ams::amsarray<amsstring> *lns);

//splits a string, not counting whitespaces between non-whitespace characters
//void splitwhitespace(const amsstring *s, std::vector<amsstring> *lns);
void splitwhitespace(const amsstring *s, ams::amsarray<amsstring> *lns);

//removes all whitespace characters '\t','\r','\n' included
//to the left and right of the string (but not in the middle)
void stripwhitespace(amsstring *s);

//completely removes all whitespace entirely
void stripallwhitespace(amsstring *s);


//Updated convenience functions: updated for a more python style
amsarray<amsstring> splitlines(const amsstring &s);
amsarray<amsstring> split(const amsstring &s, const ams_chartype delimitchar);
amsarray<amsstring> split(const amsstring &s, const ams_chartype *delimitstr);

//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> splitfirst(const amsstring &s, const ams_chartype *delimitstr);

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

//strips \r and \n from the right side of the string
amsstring stripnewlines(const amsstring &s);

//strips \r and \n from the right side of the strings
void stripnewlines(amsarray<amsstring> *sa_inout);

//splits a string into contiguous sets of alphanumeric characters ignoring non-alphanums
amsarray<amsstring> splitalphanum(const amsstring &s); 


//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);

//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);

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

//selects between the outermost set of delimitleft and delimitright strings
//if delimitleft or delimitright strings are empty, selects the left or right end of the array
amsstring select_between(const amsstring &s, const ams_chartype *delimitleft, const ams_chartype *delimitright); 

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

//adds \n between each line in the concatenated string
amsstring concat_lines(const amsarray<amsstring> &lns);

void freadline(FILE *fp, amsstring *s);
void freadlines(FILE *fp, amsarray<amsstring> *lines);
void fwritelines(FILE *fp, amsstring *s);
void fwritelines(FILE *fp, amsarray<amsstring> *lines);
void freadtxtfile(FILE *fp, amsstring *s);

int readtextfile(const amsstring fname, amsstring *s);

}; //end namespace ams

#include <amsstring4/amsstring4_unicode.hpp>
#include <amsstring4/amsstring4_bintextencoding.hpp>
#include <amsstring4/amsstring4_tests.hpp>


#endif