/*
  Copyright 2025 Matthias-Sebastian Schröder <matthias.schroeder@dolanghub.de>

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#pragma once

#include "dobase/namespace.hpp"
#include "dobase/charset.hpp"

#include <filesystem>
#include <list>

__NAMESPACE_DO_START

class stringbuffer;
struct string_type_st
{
  char* value;
  const Charset* cs;
  stringbuffer* buffer;
  size_t size;
};

class String;
typedef bool(*string_scan_callback)(const String& subject, const char* beg, const char* end,
  size_t codepoint, size_t needle, size_t needle_len, void* context);

typedef bool(*string_char_qulifies_callback)(const char32_t examine);

// Enum list of strings
typedef const String* (*enum_string_first)(void* user);
typedef const String* (*enum_string_next)(void* user);

enum class ConFlags : int
{
  NONE = 0,
  // Creaete null-terminated String
  TERM_NULL = 1,
  // Create String with non shared exclusive
  // memory (Writable)
  EXCLUSIVE = 2,
  // Calculate target buffer size instead of guessing
  // it, which is slower but consumes less memory
  EXACT = 4,
};

__forceinline
ConFlags operator | (ConFlags a, ConFlags b)
{
  return static_cast<ConFlags>(static_cast<int>(a) | static_cast<int>(b));
}

__forceinline
ConFlags operator & (ConFlags a, ConFlags b)
{
  return static_cast<ConFlags>(static_cast<int>(a) & static_cast<int>(b));
}

struct const_string_st
{
  const char* value;
  const char* end;
  const Charset* cs;
};

class Collation;
struct scan_string_st;
// Non virtual exported functions only
class DOBASE_EXPORT String : public_internal string_type_st
{
public:
  // Default constructors
  String();
  String(const String& string);
  String& operator = (const String& p);

  // Initialize a new instance of String with the
  // given size in bytes and the defined charset
  String(size_t size, const Charset* charset);

  // Initialize a new instance of String from null-terminated
  // const utf8 pointer
  String(const char* u8value);
  String(const wchar_t* u16levalue);

  // Initialize a new instance of String from null-terminated
  // const char32_t pointer
  String(const char32_t* u32value);

  // Take a window out of a existing intance of String
  // and produce a new instance of String from it
  // using the same internal buffer.
  String(const String& string, const char* const start,
    const char* const end);

  ~String();

  // Get readonly pointer begin / end
  const char* read() const;
  const char* end() const;

  // Get readwrite pointer, may
  // trigger copy on write action on shared buffer
  char* write();

  // Get size of string in bytes
  size_t size() const;

  // Get size of string in number of chars,
  // can take a considerable amount of compution-time
  // for multi-byte character sets
  size_t length() const;

  // Get strings character set
  const Charset* get_charset() const;

  // Changes character set of string
  void convert(const Charset* cs, ConFlags flags = ConFlags::NONE,
    CstranRecovery cer = CstranRecovery::SUBSTITUTE);

  // Changes parts of this string
  String strtran(String needle, String repl) const;

  // Changes the size of the string (bytes)
  void resize(size_t size);

  // Cut someting out of string starting at index 0
  String substr(size_t start, size_t count) const;

  // Find substring in string
  bool strstr(const String& needle, size_t* offset,
    size_t* start, size_t* end, size_t skip);

  // Factories
  static String fromBytes(const char* value,
    size_t value_len, const Charset* charset);
  static String fromBytes(const char* value,
    const Charset* charset);

  // Find char in string and return start, end byte offsets
  bool chrstr(char32_t c, int* offset,
    size_t* start, size_t* end) const;

  String strmultitran(const_string_st* needles, size_t num_needles,
    const_string_st* replacements) const;

  // Transformers
  String upper() const;

  String lower() const;

public_internal:
  // Frequently used conversion between
  // path and String
  String(const std::filesystem::path& path);
  operator std::filesystem::path() const;

  // FIXME: Need documentaion
  size_t align(const size_t limit);

  int cmp(const String* b, const Collation* collation) const;
  int cmp(const String& b, const Collation* collation) const;
  bool is_empty() const;
  int strcpy(char* buf, size_t buf_size, const Charset* tcs,
    CstranRecovery rec);
  void add(String b);
  void add(const String* b);

  // Find char in string (case insensitive)
  bool christr(char32_t c, int* offset, size_t* start, size_t* end);

  // Find multiple occurences of needles use callback to
  // push results
  int scan(const const_string_st* needle, size_t anz_needle, size_t& num_codepoints_scanned,
    string_scan_callback callback, void* context) const;

  // Find multiple occurences of single needle, use callback
  // to push results
  int scan(const const_string_st& needle, size_t& num_codepoints_scanned,
    string_scan_callback callback, void* context) const;

  // Generic rtrim implementation uses a callback function
  // to check if char32_t shall be trimmed
  void rtrim(string_char_qulifies_callback cb);
  // Remove whitespace at end of string or 0 if charset binary
  void rtrim();
  void rtrim(const Collation* collation, enum_string_first enum_first,
    enum_string_next enum_next, void* user);
  // Remove endl from end of string
  void rtrim_endl();

  // Generic ltrim implementation uses a callback function
  // to check if char32_t shall be trimmed
  void ltrim(string_char_qulifies_callback cb);
  // Trim space (unicode mode)
  void ltrim();
  void ltrim(const Collation* collation, enum_string_first enum_first,
    enum_string_next enum_next, void* user);
  // Classic trim space (non unicode mode)
  void ltrim_classic_space();
  // Classic trim tab (non unicode mode)
  void ltrim_classic_tab();
  // Classic trim tab (non unicode mode)
  void ltrim_classic_space_or_tab();
  // Trim specific trim_char
  void ltrim(const String& trim_char);

  void alltrim();
  void explode(const const_string_st* needles,
    size_t anz_needles, bool keep_needle, std::list<String>* erg) const;
  std::string u8str() const;
  std::u16string u16str() const;
#ifdef _WINDOWS
  int to_bstr(__wchar_t** to);
  int to_bstr(unsigned short** to);
#endif
  static String create_from_scan_string(const scan_string_st& scan);
  static String create_from_path(const std::filesystem::path& u8path, const Charset* cs);

private:
  String(const char* value, size_t value_len, const Charset* charset);
  void do_copy_on_write();
};

DOBASE_EXPORT int HashSHA256(const unsigned char* msg, size_t size, unsigned char* out_hash, size_t out_size);

DOBASE_EXPORT int HashSHA1(const unsigned char* msg, size_t size, unsigned char* out_hash, size_t out_size);

DOBASE_EXPORT int AllocArgsW(const wchar_t* const argv[], int argc, char*** out_argv);

DOBASE_EXPORT int FreeArgsW(char* argv);

DOBASE_EXPORT const Charset* DetectCharset(const String& string);

DOBASE_EXPORT String FileToString(const String& filename,
  const Charset* charset, bool autodetect);

DOBASE_EXPORT size_t StrToFile(const String& content, const String& filename,
  int flags);

__NAMESPACE_END
