/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */

/*
 * ----------------------------------------------------------------------------------
 * The code in this file is copied (with a few modifications) from:
 *     Print.h - base class that provides print() and println()
 * ----------------------------------------------------------------------------------
 * Copyright (C) 2008 David A. Mellis. All right reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * ----------------------------------------------------------------------------------
 */


#ifndef ECXX_PLATFORM_NODEMCU_PRINT_H
#define ECXX_PLATFORM_NODEMCU_PRINT_H


#include <sys/string.h>

#include "WString.h"


namespace eCxx {

namespace NodeMCU {


class Print {
    public:
        static constexpr uint8_t BIN =  2;
        static constexpr uint8_t OCT =  8;
        static constexpr uint8_t DEC = 10;
        static constexpr uint8_t HEX = 16;

    public:
        inline Print()
        : writeError(0)
        {}

        inline int getWriteError()
        { return writeError; }

        inline void clearWriteError()
        { setWriteError(0); }

        virtual size_t write(uint8_t c) = 0;

        virtual size_t write(const uint8_t* buffer, size_t size);

        inline size_t write(const char* buffer, size_t size)
        { return write( reinterpret_cast<const uint8_t*>(buffer), size ); }

        inline size_t write(const char* str)
        { return str ? write( reinterpret_cast<const uint8_t*>(str), strlen(str) ) : 0; }

        inline size_t write(         char      c) { return write( static_cast<uint8_t>(c) ); }
        inline size_t write(  signed char      c) { return write( static_cast<uint8_t>(c) ); }
        inline size_t write(         short     t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(unsigned short     t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(         int       t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(unsigned int       t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(         long      t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(unsigned long      t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(         long long t) { return write( static_cast<uint8_t>(t) ); }
        inline size_t write(unsigned long long t) { return write( static_cast<uint8_t>(t) ); }

        virtual int32_t availableForWrite()
        { return 0; }

        size_t printf(const char * format, ...)  __EXCC_ATTR_PRINTF(2, 3);
        size_t printf_P(_PGMCharPointer format, ...) __EXCC_ATTR_PRINTF(2, 3);

        size_t print_P(_PGMCharPointer s);
        size_t println_P(_PGMCharPointer s);

        inline size_t print  (PGMCharPointer s) { return print_P  (s.p); }
        inline size_t println(PGMCharPointer s) { return println_P(s.p); }

        size_t print(const WString& s  );
        size_t print(const char     s[]);
        size_t print(      char     c  );

        size_t println();

        size_t println(const WString& s  );
        size_t println(const char     s[]);
        size_t println(char           c  );

        size_t print(  signed char      n, uint8_t base   = DEC);
        size_t print(unsigned char      n, uint8_t base   = DEC);
        size_t print(         short     n, uint8_t base   = DEC);
        size_t print(unsigned short     n, uint8_t base   = DEC);
        size_t print(         int       n, uint8_t base   = DEC);
        size_t print(unsigned int       n, uint8_t base   = DEC);
        size_t print(         long      n, uint8_t base   = DEC);
        size_t print(unsigned long      n, uint8_t base   = DEC);
        size_t print(         long long n, uint8_t base   = DEC);
        size_t print(unsigned long long n, uint8_t base   = DEC);
        size_t print(float              n, uint8_t digits = 2  );
        size_t print(double             n, uint8_t digits = 5  );

        size_t println(  signed char      n, uint8_t base   = DEC);
        size_t println(unsigned char      n, uint8_t base   = DEC);
        size_t println(         short     n, uint8_t base   = DEC);
        size_t println(unsigned short     n, uint8_t base   = DEC);
        size_t println(         int       n, uint8_t base   = DEC);
        size_t println(unsigned int       n, uint8_t base   = DEC);
        size_t println(         long      n, uint8_t base   = DEC);
        size_t println(unsigned long      n, uint8_t base   = DEC);
        size_t println(         long long n, uint8_t base   = DEC);
        size_t println(unsigned long long n, uint8_t base   = DEC);
        size_t println(float              n, uint8_t digits = 2  );
        size_t println(double             n, uint8_t digits = 5  );

        // Empty implementation for backward compatibility
        virtual void flush()
        {}

    protected:
        inline void setWriteError(int32_t err = 1)
        { writeError = err; }

    private:
        int writeError;

        size_t _printFloat(double n, uint8_t digits);

        template<typename ValueT> size_t _printUInt(ValueT n, uint8_t base);
};


} // namespace NodeMCU

} // namespace eCxx


//
// For compatibility with the Arduino library part
//
typedef eCxx::NodeMCU::Print Print;


#endif
