/*
*/
#include <algorithm>
#include "dpstringlist.h"
#include <regex/regex_sr.h>
#include "osdir/osdir.h"

// TODO: Replace by std::filesystem, e.g.
/*

https://stackoverflow.com/questions/612097/how-can-i-get-the-list-of-files-in-a-directory-using-c-or-c

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
    std::string path = "/path/to/directory";
    for (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}

 */

//#ifdef WIN
//#define _WINSOCK2API_
//#endif
#include <dpuser_utils.h>

#ifdef WIN
#pragma warning (disable: 4018) // disable warning for comparing signed and unsigned
#include <direct.h>
#else
#include <unistd.h>
#endif

extern char *bopat[];

int lenFileName = 256,
    lenColumnName = 72;

//dpStringList::dpStringList() {
//    fileName = (char *)calloc(lenFileName + 1, sizeof(char));
//    strcpy(fileName, "");

//    columnName = (char*)calloc(lenColumnName + 1, sizeof(char));
//    strcpy(columnName, "");
//}

//dpStringList::dpStringList(const dpStringList &d) : std::vector<dpString>(d) {
//    fileName = (char *)calloc(lenFileName + 1, sizeof(char));
//    strcpy(fileName, d.getFileName());

//    columnName = (char*)calloc(lenColumnName + 1, sizeof(char));
//    strcpy(columnName, d.getColumnName());
//}

//dpStringList::~dpStringList() {
//    if (fileName != NULL) {
//        free(fileName); fileName = NULL;
//    }
//    if (columnName != NULL) {
//        free(columnName); columnName = NULL;
//    }
//}

//dpStringList &dpStringList::operator=(const dpStringList &d) {
//    *static_cast<std::vector<dpString>*>(this) = d;
//    strcpy(fileName, d.getFileName());
//    strcpy(columnName, d.getColumnName());

//    return *this;
//}

dpint64 dpStringList::count() const {
	return size();
}

void dpStringList::append(const dpString &s) {
	push_back(s);
}

void dpStringList::prepend(const dpString &s) {
	insert(begin(), s);
}

void dpStringList::remove(dpStringList::iterator pos) {
    erase(pos);
}

void dpStringList::operator +=(const dpStringList &l) {
    // remember original length since l.size() is dynamic
    size_type lsize = l.size();
    for (int i = 0; i < lsize; i++) {
		push_back(l[i]);
	}
}

void dpStringList::operator +=(const char *s) {
	push_back(s);
}

dpString dpStringList::first() {
    dpString rv = (*this)[0];
	return rv;
}

dpStringList dpStringList::split(const char sep, dpString &source) {
    dpStringList rv;
    dpint64 pos = 0,
            oldpos = 0;

    while (pos < source.size()) {
        oldpos = pos;
        pos = source.find(sep, pos);
        if (pos < source.size()) {
            if (pos > oldpos) {
                rv.push_back(source.mid(oldpos, pos-oldpos));
                dp_debug("pos > oldpos: pushed %s\n", source.mid(oldpos, pos-oldpos).c_str());
            }
            if (pos==oldpos) {
                rv.push_back(dpString(""));
                dp_debug("pos==oldpos:  pushed %s\n", dpString("").c_str());
            }
            pos++;
        }
    }

    // print rest of source (only when delimiter is not last character in source!)
    if (pos != source.size()){
        rv.push_back(source.mid(oldpos, source.size()-oldpos+1));
        dp_debug("last:         pushed %s\n", source.mid(oldpos, source.size()-oldpos+1).c_str());
    }

    return rv;
}

dpStringList dpStringList::splitcrlf(dpString &source) {
    dpStringList rv;
    char sep[3];
    dpint64 pos = 0,
            oldpos = 0;

    sep[0] = '\r'; sep[1] = '\n'; sep[2] = 0;
    
    while (pos < source.size()) {
        oldpos = pos;
        pos = source.find_first_of(sep, pos);
        if (pos < source.size()) {
            if (pos > oldpos) {
                rv.push_back(source.mid(oldpos, pos-oldpos));
                dp_debug("pos > oldpos: pushed %s\n", source.mid(oldpos, pos-oldpos).c_str());
            }
            if (pos==oldpos) {
                rv.push_back(dpString(""));
                dp_debug("pos==oldpos:  pushed %s\n", dpString("").c_str());
            }
	    if (source.c_str()[pos] == '\n') {
	        if (source.c_str()[pos+1] == '\r') pos++;
	    } else if (source.c_str()[pos] == '\r') {
	        if (source.c_str()[pos+1] == '\n') pos++;
	    }
            pos++;
        }
    }

    // print rest of source (only when delimiter is not last character in source!)
    if (pos != source.size()){
        rv.push_back(source.mid(oldpos, source.size()-oldpos+1));
        dp_debug("last:         pushed %s\n", source.mid(oldpos, source.size()-oldpos+1).c_str());
    }

    return rv;
}

const char* dpStringList::getFileName() const {
//@    return fileName;
    return fileName.c_str();
}

void dpStringList::setFileName(const char *fname) {
//@    strncpy(fileName, fname, lenFileName);
    fileName = fname;
}

const char* dpStringList::getColumnName() const {
//@    return columnName;
    return (const char *)(columnName.c_str());
}

void dpStringList::setColumnName(const char *cname) {
//@    strncpy(columnName, cname, lenColumnName);
    columnName = cname;
}

bool dpStringList::readFile(const dpString &fname) {
    FILE *fd = NULL;
    char *newinput = NULL;

    if ((fd = fopen(fname.c_str(), "rb")) == NULL) {
        dp_output("Could not open file %s for reading\n", fname.c_str());
        return false;
    }

    fseek(fd, 0L, SEEK_END);
    dpint64 flength = ftell(fd);
    rewind(fd);
    if ((newinput = (char *)calloc((flength+2), sizeof(char))) == NULL) {
        fclose(fd);
        dp_output("Memory allocation error\n");
        return false;
    }

    fread(newinput, sizeof(char), flength, fd);
    dpString nnn = newinput;
    nnn.replace("\r", "");
    *this = dpStringList::split('\n', nnn);

    fclose(fd);
    free(newinput);

    return true;
}

dpString dpDir::currentDirPath() {
	char _tmp[1024];
    char *crv = NULL;
	
#ifdef WIN
	crv = _getcwd(_tmp, 1023);
#else
	crv = getcwd(_tmp, 1023);
#endif

    if (crv == NULL) {
        return dpString();
    } else {
        return dpString(crv);
    }
}

dpStringList dpDir::findfile(const dpString &filter) {
    // filter isn't a regular expression, but a  text pattern to find
    dpStringList rv;

	dpString cmp;
    cmp += '^' + filter;                         // search only occurences at beginning of string
    cmp.replace(dpString("."), dpString("\\.")); // escape . (keyword in regular expressions...)
    cmp.replace(dpString("?"), dpString("."));   // ? in text pattern means any character at this place
    cmp.replace(dpString("*"), dpString(".*"));  // * in text pattern means any, none or many characters at this place
    cmp += '$';                                  // ignore further characters at the end

	dpDir::walk(".", "", cmp, rv);

    for (dpint64 i = 0; i < rv.size(); i++) {
        rv[i].replace("./", "");
    }

// now get rid of duplicates, if there are any
    std::sort(rv.begin(), rv.end());
    rv.erase(std::unique(rv.begin(), rv.end()), rv.end());

    return rv;
}

dpStringList dpDir::dir(const dpString &filter) {
    // filter isn't a regular expression, but a  text pattern to find
    CRegExp r;
    dpString res;
	dpStringList rv;
    int npos = 0;

	dpString cmp;
	if (filter == "") {
        cmp = "*";
    } else {
        cmp += '^' + filter;                         // search only occurences at beginning of string
        cmp.replace(dpString("."), dpString("\\.")); // escape . (keyword in regular expressions...)
        cmp.replace(dpString("?"), dpString("."));   // ? in text pattern means any character at this place
        cmp.replace(dpString("*"), dpString(".*"));  // * in text pattern means any, none or many characters at this place
        cmp += '$';                                  // ignore further characters at the end
    }

    if (r.RegComp(cmp.c_str()) != NULL) {
        oslink::directory cwd(".");
        while (cwd) {
            res = cwd.next();
            npos = r.RegFind(res.c_str());
            if (npos >= 0) {
                if ((res != ".") && (res != "..")) {
                    rv.push_back(res);
                }
            }
        }
    }

// now get rid of duplicates, if there are any
    std::sort(rv.begin(), rv.end());
    rv.erase(std::unique(rv.begin(), rv.end()), rv.end());

	return rv;
}

void dpDir::walk(const dpString& aDir, const dpString &aName, const dpString &filter, dpStringList &result) {
    CRegExp r;
    dpString Full = aDir + '/' + aName;
    if (aName == "") {
        Full = aDir;
    }

    if (r.RegComp(filter.c_str()) != NULL) {
        if (r.RegFind(aName.c_str()) >= 0) {
            if ((aName != ".") && (aName != "..") && (Full != ".")) {
                result.push_back(Full);
            }
        }
    }

    if ((aName != ".") && (aName != "..")) {
        oslink::directory cur(Full);
        while (cur) {
            dpString aaa = cur.next();
            walk(Full, aaa, filter, result);
        }
    }
}
