// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Our base input/output stream classes.  These are is directly
 * inherited from iostreams, and includes any extra
 * functionality that we might need.
 *
 * Authors:
 *   Bob Jamison <rjamison@titan.com>
 *
 * Copyright (C) 2004 Inkscape.org
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <cstdlib>
#include "inkscapestream.h"

namespace Inkscape
{
namespace IO
{

//#########################################################################
//# U T I L I T Y
//#########################################################################

void pipeStream(InputStream &source, OutputStream &dest)
{
    for (;;)
        {
        int ch = source.get();
        if (ch<0)
            break;
        dest.put(ch);
        }
    dest.flush();
}

//#########################################################################
//# B A S I C    I N P U T    S T R E A M
//#########################################################################


/**
 *
 */ 
BasicInputStream::BasicInputStream(InputStream &sourceStream)
                   : source(sourceStream)
{
    closed = false;
}

/**
 * Returns the number of bytes that can be read (or skipped over) from
 * this input stream without blocking by the next caller of a method for
 * this input stream.
 */ 
int BasicInputStream::available()
{
    if (closed)
        return 0;
    return source.available();
}

    
/**
 *  Closes this input stream and releases any system resources
 *  associated with the stream.
 */ 
void BasicInputStream::close()
{
    if (closed)
        return;
    source.close();
    closed = true;
}
    
/**
 * Reads the next byte of data from the input stream.  -1 if EOF
 */ 
int BasicInputStream::get()
{
    if (closed)
        return -1;
    return source.get();
}
   


//#########################################################################
//# B A S I C    O U T P U T    S T R E A M
//#########################################################################

/**
 *
 */ 
BasicOutputStream::BasicOutputStream(OutputStream &destinationStream)
                     : destination(destinationStream)
{
    closed = false;
}

/**
 * Closes this output stream and releases any system resources
 * associated with this stream.
 */ 
void BasicOutputStream::close()
{
    if (closed)
        return;
    destination.close();
    closed = true;
}
    
/**
 *  Flushes this output stream and forces any buffered output
 *  bytes to be written out.
 */ 
void BasicOutputStream::flush()
{
    if (closed)
        return;
    destination.flush();
}
    
/**
 * Writes the specified byte to this output stream.
 */ 
int BasicOutputStream::put(char ch)
{
    if (closed)
        return -1;
    destination.put(ch);
    return 1;
}



//#########################################################################
//# B A S I C    R E A D E R
//#########################################################################
/**
 *
 */ 
BasicReader::BasicReader(Reader &sourceReader)
{
    source = &sourceReader;
}

/**
 * Returns the number of bytes that can be read (or skipped over) from
 * this reader without blocking by the next caller of a method for
 * this reader.
 */ 
int BasicReader::available()
{
    if (source)
        return source->available();
    else
        return 0;
}

    
/**
 *  Closes this reader and releases any system resources
 *  associated with the reader.
 */ 
void BasicReader::close()
{
    if (source)
        source->close();
}
    
/**
 * Reads the next byte of data from the reader.
 */ 
char BasicReader::get()
{
    if (source)
        return source->get();
    else
        return (char)-1;
}
   

/**
 * Reads a line of data from the reader.
 */ 
Glib::ustring BasicReader::readLine()
{
    Glib::ustring str;
    while (available() > 0)
        {
        char ch = get();
        if (ch == '\n')
            break;
        str.push_back(ch);
        }
    return str;
}
   
/**
 * Reads a line of data from the reader.
 */ 
Glib::ustring BasicReader::readWord()
{
    Glib::ustring str;
    while (available() > 0)
        {
        char ch = get();
        if (!std::isprint(ch))
            break;
        str.push_back(ch);
        }
    return str;
}
   

static bool getLong(Glib::ustring &str, long *val)
{
    const char *begin = str.raw().c_str();
    char *end;
    long ival = strtol(begin, &end, 10);
    if (str == end)
        return false;
    *val = ival;
    return true;
}

static bool getULong(Glib::ustring &str, unsigned long *val)
{
    const char *begin = str.raw().c_str();
    char *end;
    unsigned long ival = strtoul(begin, &end, 10);
    if (str == end)
        return false;
    *val = ival;
    return true;
}

static bool getDouble(Glib::ustring &str, double *val)
{
    const char *begin = str.raw().c_str();
    char *end;
    double ival = strtod(begin, &end);
    if (str == end)
        return false;
    *val = ival;
    return true;
}



const Reader &BasicReader::readBool (bool& val )
{
    Glib::ustring buf = readWord();
    if (buf == "true")
        val = true;
    else
        val = false;
    return *this;
}

const Reader &BasicReader::readShort (short& val )
{
    Glib::ustring buf = readWord();
    long ival;
    if (getLong(buf, &ival))
        val = (short) ival;
    return *this;
}

const Reader &BasicReader::readUnsignedShort (unsigned short& val )
{
    Glib::ustring buf = readWord();
    unsigned long ival;
    if (getULong(buf, &ival))
        val = (unsigned short) ival;
    return *this;
}

const Reader &BasicReader::readInt (int& val )
{
    Glib::ustring buf = readWord();
    long ival;
    if (getLong(buf, &ival))
        val = (int) ival;
    return *this;
}

const Reader &BasicReader::readUnsignedInt (unsigned int& val )
{
    Glib::ustring buf = readWord();
    unsigned long ival;
    if (getULong(buf, &ival))
        val = (unsigned int) ival;
    return *this;
}

const Reader &BasicReader::readLong (long& val )
{
    Glib::ustring buf = readWord();
    long ival;
    if (getLong(buf, &ival))
        val = ival;
    return *this;
}

const Reader &BasicReader::readUnsignedLong (unsigned long& val )
{
    Glib::ustring buf = readWord();
    unsigned long ival;
    if (getULong(buf, &ival))
        val = ival;
    return *this;
}

const Reader &BasicReader::readFloat (float& val )
{
    Glib::ustring buf = readWord();
    double ival;
    if (getDouble(buf, &ival))
        val = (float)ival;
    return *this;
}

const Reader &BasicReader::readDouble (double& val )
{
    Glib::ustring buf = readWord();
    double ival;
    if (getDouble(buf, &ival))
        val = ival;
    return *this;
}



//#########################################################################
//# I N P U T    S T R E A M    R E A D E R
//#########################################################################


InputStreamReader::InputStreamReader(InputStream &inputStreamSource)
                     : inputStream(inputStreamSource)
{
}

    

/**
 *  Close the underlying OutputStream
 */
void InputStreamReader::close()
{
    inputStream.close();
}
    
/**
 *  Flush the underlying OutputStream
 */
int InputStreamReader::available()
{
    return inputStream.available();
}
    
/**
 *  Overloaded to receive its bytes from an InputStream
 *  rather than a Reader
 */
char InputStreamReader::get()
{
    char ch = inputStream.get();
    return ch;
}



//#########################################################################
//# S T D    R E A D E R
//#########################################################################


/**
 *
 */
StdReader::StdReader()
{
    inputStream = new StdInputStream();
}

/**
 *
 */
StdReader::~StdReader()
{
    delete inputStream;
}

    

/**
 *  Close the underlying OutputStream
 */
void StdReader::close()
{
    inputStream->close();
}
    
/**
 *  Flush the underlying OutputStream
 */
int StdReader::available()
{
    return inputStream->available();
}
    
/**
 *  Overloaded to receive its bytes from an InputStream
 *  rather than a Reader
 */
char StdReader::get()
{
    char ch = inputStream->get();
    return ch;
}





//#########################################################################
//# B A S I C    W R I T E R
//#########################################################################

/**
 *
 */ 
BasicWriter::BasicWriter(Writer &destinationWriter)
{
    destination = &destinationWriter;
}

/**
 * Closes this writer and releases any system resources
 * associated with this writer.
 */ 
void BasicWriter::close()
{
    if (destination)
        destination->close();
}
    
/**
 *  Flushes this output stream and forces any buffered output
 *  bytes to be written out.
 */ 
void BasicWriter::flush()
{
    if (destination)
        destination->flush();
}
    
/**
 * Writes the specified byte to this output writer.
 */ 
void BasicWriter::put(char ch)
{
    if (destination)
        destination->put(ch);
}

/**
 * Provide printf()-like formatting
 */ 
Writer &BasicWriter::printf(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    gchar *buf = g_strdup_vprintf(fmt, args);
    va_end(args);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
    return *this;
}
/**
 * Writes the specified character to this output writer.
 */ 
Writer &BasicWriter::writeChar(char ch)
{
    put(ch);
    return *this;
}


/**
 * Writes the specified unicode string to this output writer.
 */ 
Writer &BasicWriter::writeUString(const Glib::ustring &str)
{
    writeStdString(str.raw());
    return *this;
}

/**
 * Writes the specified standard string to this output writer.
 */ 
Writer &BasicWriter::writeStdString(const std::string &str)
{
    for (char it : str) {
        put(it);
    }
    return *this;
}

/**
 * Writes the specified character string to this output writer.
 */ 
Writer &BasicWriter::writeString(const char *str)
{
    std::string tmp;
    if (str)
        tmp = str;
    else
        tmp = "null";
    writeStdString(tmp);
    return *this;
}




/**
 *
 */
Writer &BasicWriter::writeBool (bool val )
{
    if (val)
        writeString("true");
    else
        writeString("false");
    return *this;
}


/**
 *
 */
Writer &BasicWriter::writeShort (short val )
{
    gchar *buf = g_strdup_printf("%d", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
  return *this;
}



/**
 *
 */
Writer &BasicWriter::writeUnsignedShort (unsigned short val )
{
    gchar *buf = g_strdup_printf("%u", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
    return *this;
}

/**
 *
 */
Writer &BasicWriter::writeInt (int val)
{
    gchar *buf = g_strdup_printf("%d", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
    return *this;
}

/**
 *
 */
Writer &BasicWriter::writeUnsignedInt (unsigned int val)
{
    gchar *buf = g_strdup_printf("%u", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
    return *this;
}

/**
 *
 */
Writer &BasicWriter::writeLong (long val)
{
    gchar *buf = g_strdup_printf("%ld", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
    return *this;
}

/**
 *
 */
Writer &BasicWriter::writeUnsignedLong(unsigned long val)
{
    gchar *buf = g_strdup_printf("%lu", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
    return *this;
}

/**
 *
 */
Writer &BasicWriter::writeFloat(float val)
{
#if 1
    gchar *buf = g_strdup_printf("%8.3f", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
#else
    std::string tmp = ftos(val, 'g', 8, 3, 0);
    writeStdString(tmp);
#endif
    return *this;
}

/**
 *
 */
Writer &BasicWriter::writeDouble(double val)
{
#if 1
    gchar *buf = g_strdup_printf("%8.3f", val);
    if (buf) {
        writeString(buf);
        g_free(buf);
    }
#else
    std::string tmp = ftos(val, 'g', 8, 3, 0);
    writeStdString(tmp);
#endif
    return *this;
}


Writer& operator<< (Writer &writer, char val)
    { return writer.writeChar(val); }

Writer& operator<< (Writer &writer, Glib::ustring &val)
    { return writer.writeUString(val); }

Writer& operator<< (Writer &writer, std::string &val)
    { return writer.writeStdString(val); }

Writer& operator<< (Writer &writer, char const *val)
    { return writer.writeString(val); }

Writer& operator<< (Writer &writer, bool val)
    { return writer.writeBool(val); }

Writer& operator<< (Writer &writer, short val)
    { return writer.writeShort(val); }

Writer& operator<< (Writer &writer, unsigned short val)
    { return writer.writeUnsignedShort(val); }

Writer& operator<< (Writer &writer, int val)
    { return writer.writeInt(val); }

Writer& operator<< (Writer &writer, unsigned int val)
    { return writer.writeUnsignedInt(val); }

Writer& operator<< (Writer &writer, long val)
    { return writer.writeLong(val); }

Writer& operator<< (Writer &writer, unsigned long val)
    { return writer.writeUnsignedLong(val); }

Writer& operator<< (Writer &writer, float val)
    { return writer.writeFloat(val); }

Writer& operator<< (Writer &writer, double val)
    { return writer.writeDouble(val); }



//#########################################################################
//# O U T P U T    S T R E A M    W R I T E R
//#########################################################################


OutputStreamWriter::OutputStreamWriter(OutputStream &outputStreamDest)
                     : outputStream(outputStreamDest)
{
}

    

/**
 *  Close the underlying OutputStream
 */
void OutputStreamWriter::close()
{
    flush();
    outputStream.close();
}
    
/**
 *  Flush the underlying OutputStream
 */
void OutputStreamWriter::flush()
{
      outputStream.flush();
}
    
/**
 *  Overloaded to redirect the output chars from the next Writer
 *  in the chain to an OutputStream instead.
 */
void OutputStreamWriter::put(char ch)
{
    outputStream.put(ch);
}

//#########################################################################
//# S T D    W R I T E R
//#########################################################################


/**
 *  
 */
StdWriter::StdWriter()
{
    outputStream = new StdOutputStream();
}

    
/**
 *  
 */
StdWriter::~StdWriter()
{
    delete outputStream;
}

    

/**
 *  Close the underlying OutputStream
 */
void StdWriter::close()
{
    flush();
    outputStream->close();
}
    
/**
 *  Flush the underlying OutputStream
 */
void StdWriter::flush()
{
      outputStream->flush();
}
    
/**
 *  Overloaded to redirect the output chars from the next Writer
 *  in the chain to an OutputStream instead.
 */
void StdWriter::put(char ch)
{
    outputStream->put(ch);
}


} // namespace IO
} // namespace Inkscape


//#########################################################################
//# E N D    O F    F I L E
//#########################################################################
