/*
 * 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 slight modifications) from:
 *     Stream.h - base class for character-based streams
 * ----------------------------------------------------------------------------------
 * 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
 *
 * Parsing functions based on TextFinder library by Michael Margolis
 * ----------------------------------------------------------------------------------
 */


#ifndef ECXX_PLATFORM_NODEMCU_STREAM_H
#define ECXX_PLATFORM_NODEMCU_STREAM_H


#include "Print.h"


namespace eCxx {

namespace NodeMCU {


class Stream: public Print {
    public:
        inline Stream()
        : _timeout(1000)
        {}

        // Sets the maximum milliseconds to wait for stream data (the default is 1 second)
        inline void setTimeout(unsigned long timeout)
        { _timeout = timeout; }

        virtual int32_t available() = 0;
        virtual int32_t read() = 0;
        virtual int32_t peek() = 0;

        // Reads data from the stream until the target string is found
        // (returns true if target string is found, false if timeout)
        bool find(const char* target);
        bool find(const char* target, size_t length);

        inline bool find(char target)
        { return find (&target, 1); }

        inline bool find(const uint8_t* target)
        { return find( reinterpret_cast<const char*>(target) ); }

        inline bool find(const uint8_t* target, size_t length)
        { return find( reinterpret_cast<const char*>(target), length ); }

        // Similar to find(), but search ends if the terminator string is found
        bool findUntil(const char* target, const char *terminator);
        bool findUntil(const char* target, size_t targetLen, const char* terminate, size_t termLen);

        inline bool findUntil(const uint8_t *target, const char *terminator)
        { return findUntil( reinterpret_cast<const char*>(target), terminator ); }

        inline bool findUntil(const uint8_t*target, size_t targetLen, const char* terminate, size_t termLen)
        { return findUntil( reinterpret_cast<const char*>(target), targetLen, terminate, termLen ); }

        // Returns the first valid nmumeric value from the current position
        // # Initial characters that are not digits (or the minus sign) are skipped.
        // # Integer is terminated by the first character that is not a digit.
        inline long parseInt()
        { return parseInt(NO_SKIP_CHAR); }

        inline float parseFloat()
        { return parseFloat(NO_SKIP_CHAR); }

        // Read chars from stream into buffer
        // (terminates if length characters have been read or timeout)
        virtual size_t readBytes(char* buffer, size_t length);

        size_t readBytes(uint8_t* buffer, size_t length)
        { return readBytes( reinterpret_cast<char*>(buffer), length ); }

        // Similar to readBytes(), but search ends if the terminator string is found
        size_t readBytesUntil(char terminator, char* buffer, size_t length);

        size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length)
        { return readBytesUntil( terminator, reinterpret_cast<char*>(buffer), length ); }

        virtual const WString readString();
        const WString readStringUntil(char terminator);

    protected:
        // A magic character that will not be found in a valid ASCII numeric field
        static constexpr char NO_SKIP_CHAR = 1;

        // Similar to above but the given skipChar is ignored
        long parseInt(char skipChar);
        float parseFloat(char skipChar);

    protected:
        unsigned long _timeout;     // Number of milliseconds to wait for the next char before aborting timed read
        unsigned long _startMillis; // Used for timeout measurement

        int32_t timedRead();
        int32_t timedPeek();
        int32_t peekNextDigit();
};


} // namespace NodeMCU

} // namespace eCxx


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


#endif
