//
// tardy - a tar post-processor
// Copyright (C) 1991-1999, 2002, 2004, 2008, 2009, 2011 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
//

#ifndef LIBTARDY_RCSTRING_H
#define LIBTARDY_RCSTRING_H

#include <libtardy/ac/stddef.h>
#include <libtardy/ac/stdarg.h>
#include <libtardy/main.h>

typedef unsigned long str_hash_ty;

typedef struct string_ty string_ty;
struct string_ty
{
    str_hash_ty     str_hash;
    string_ty       *str_next;
    long            str_references;
    size_t          str_length;
    char            str_text[1];
};

void str_initialize(void);
string_ty *str_from_c(const char *);
string_ty *str_n_from_c(const char *, size_t);
string_ty *str_copy(string_ty *);
void str_free(string_ty *);
string_ty *str_catenate(string_ty *, string_ty *);
string_ty *str_cat_three(string_ty *, string_ty *, string_ty *);
int str_bool(string_ty *);

void str_dump(void);
string_ty *str_field(string_ty *str, int sep, int fldnum);
string_ty *str_format(const char *, ...) ATTR_PRINTF(1, 2);
string_ty *str_vformat(const char *, va_list) ATTR_PRINTF(1, 0);

bool str_equal(string_ty *s1, string_ty *s2);


/**
  * The rcstring class is used to represent a reference counted string
  * class.  It has optinal string equality time charactgerstics.
  */
class rcstring
{
public:
    ~rcstring() { if (p) str_free(p); }

    rcstring() : p(0) { }

    rcstring(const rcstring &arg) : p(str_copy(arg.p)) {}

    rcstring(const char *arg) : p(str_from_c(arg)) {}

    rcstring(const char *arg, size_t len) : p(str_n_from_c(arg, len)) {}

    rcstring &operator = (const rcstring &arg) { if (p) str_free(p);
        arg.bind(); p = str_copy(arg.p); return *this; }

    const char *c_str(void) const { return (p ? p->str_text : ""); }

    size_t size(void) const { return (p ? p->str_length : 0); }

    bool empty(void) const { return (!p || p->str_length == 0); }

    str_hash_ty hash(void) const { bind(); return p->str_hash; }

    int operator[] (int n) const { if (n < 0) return 0; bind();
        if (n >= (int)p->str_length) return 0;
        return (unsigned char)p->str_text[n]; }

    rcstring &operator += (const rcstring &arg) { bind(); arg.bind();
        string_ty *tmp = str_catenate(p, arg.p); str_free(p);
        p = tmp; return *this; }

    friend bool operator == (const rcstring &, const rcstring &);
    friend bool operator != (const rcstring &, const rcstring &);
    friend bool operator < (const rcstring &, const rcstring &);
    friend bool operator <= (const rcstring &, const rcstring &);
    friend bool operator > (const rcstring &, const rcstring &);
    friend bool operator >= (const rcstring &, const rcstring &);
    friend rcstring operator + (const rcstring &, const rcstring &);

    /**
      * The downcase method may be used to create a new string, from
      * this string, with all upper case characters converted to lower
      * case.
      */
    rcstring downcase(void) const;

    /**
      * The upcase method may be used to create a new string, from
      * this string, with all lower case characters converted to upper
      * case.
      */
    rcstring upcase(void) const;

    /**
      * The ends_with method is used to determine whether or not this
      * strings ends with the given string.
      *
      * @param suffix
      *     The suffix to search for.
      * @returns
      *     true if the string ends with the given suffix, false if not.
      */
    bool ends_with(const rcstring &suffix) const;

    /**
      * The substring method is used to create a new string by
      * extracting a substring fron this string.
      *
      * @param begin
      *     The offset to begin from.
      * @param nbytes
      *     The number of bytes (<b>not</b> characters) to extract.
      *     Will be silently truncated if too long.
      */
    rcstring substring(size_t begin, size_t nbytes) const;

    /**
      * The hexdump class method is sued to build a hexadecimal dump
      * from a data buffer.  This is useful for debugging.
      *
      * @param data
      *     Pointer to the base of the memory to be dumped.
      * @param data_size
      *     The size of the memory to be dumpted, in bytes.
      */
    static rcstring hexdump(const void *data, size_t data_size);

    /**
      * The dirname method is used to create a new string from this
      * string, treating it as a file name, and extracting the directory
      * components.
      */
    rcstring dirname(void) const;

    /**
      * The basename method is used to create a new string from this
      * string, treating it as a file name, and extracting the final
      * component.
      */
    rcstring basename(void) const;

    /**
      * The quote_c method is used to create a new string from this
      * string, quoting the string using C string quoting conventions.
      */
    rcstring quote_c(void) const;

    /**
      * The clear method is used to replace the contents of this string
      * with the empty string.
      */
    void clear(void);

    /**
      * The printf class method may be used to create a new string,
      * according to a format string.
      *
      * @param fmt
      *     The text of the string, and it also controls the number and
      *     types of the remaining arguments.  See printf(3) for more
      *     information.
      */
    static rcstring printf(const char *fmt, ...) ATTR_PRINTF(1, 2);

    /**
      * The vprintf class method may be used to create a new string,
      * according to a format string.
      *
      * @param fmt
      *     The text of the string, and it also controls the number and
      *     types of the remaining arguments.  See vprintf(3) for more
      *     information.
      * @param ap
      *     an opaque reference to the remaining arguments.
      */
    static rcstring vprintf(const char *fmt, va_list ap) ATTR_PRINTF(1, 0);

    /**
      * The substitute method is used to create a new string from this
      * string, with all 'from' substrings replaced with 'to' substrings.
      *
      * @param from
      *     The string to be substituted
      * @param to
      *     The string to replace it with
      * @param how_many_times
      *     The number of times to do this, from the left.
      *     Defaults to "all of them".
      */
    rcstring substitute(const rcstring &from, const rcstring &to,
        size_t how_many_times = -1) const;

private:
    mutable string_ty *p;
    void bind(void) const { if (!p) p = str_from_c(""); }
};


inline bool
operator == (const rcstring &lhs, const char *rhs)
{
    return (lhs == rcstring(rhs));
}


inline bool
operator == (const char *lhs, const rcstring &rhs)
{
    return (rcstring(lhs) == rhs);
}


inline bool
operator != (const rcstring &lhs, const char *rhs)
{
    return (lhs != rcstring(rhs));
}


inline bool
operator != (const char *lhs, const rcstring &rhs)
{
    return (rcstring(lhs) != rhs);
}

bool operator < (const rcstring &, const rcstring &);
bool operator <= (const rcstring &, const rcstring &);
bool operator > (const rcstring &, const rcstring &);
bool operator >= (const rcstring &, const rcstring &);

inline rcstring
operator + (const rcstring &lhs, const rcstring &rhs)
{
    lhs.bind();
    rhs.bind();
    rcstring result;
    result.p = str_catenate(lhs.p, rhs.p);
    return result;
}

#endif // LIBTARDY_RCSTRING_H
