/****************************************************************************
 *   This file is part of the aGrUM/pyAgrum library.                        *
 *                                                                          *
 *   Copyright (c) 2005-2026 by                                             *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *                                                                          *
 *   The aGrUM/pyAgrum library is free software; you can redistribute it    *
 *   and/or modify it under the terms of either :                           *
 *                                                                          *
 *    - the GNU Lesser General Public License as published by               *
 *      the Free Software Foundation, either version 3 of the License,      *
 *      or (at your option) any later version,                              *
 *    - the MIT license (MIT),                                              *
 *    - or both in dual license, as here.                                   *
 *                                                                          *
 *   (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html)    *
 *                                                                          *
 *   This aGrUM/pyAgrum library is distributed in the hope that it will be  *
 *   useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,          *
 *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
 *   FOR A PARTICULAR PURPOSE  AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,        *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  *
 *   OTHER DEALINGS IN THE SOFTWARE.                                        *
 *                                                                          *
 *   See LICENCES for more details.                                         *
 *                                                                          *
 *   SPDX-FileCopyrightText: Copyright 2005-2026                            *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *   SPDX-License-Identifier: LGPL-3.0-or-later OR MIT                      *
 *                                                                          *
 *   Contact  : info_at_agrum_dot_org                                       *
 *   homepage : http://agrum.gitlab.io                                      *
 *   gitlab   : https://gitlab.com/agrumery/agrum                           *
 *                                                                          *
 ****************************************************************************/


/*----------------------------------------------------------------------
Compiler Generator Coco/R,
Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz
extended by M. Loeberbauer & A. Woess, Univ. of Linz
ported to C++ by Csaba Balazs, University of Szeged
with improvements by Pat Terry, Rhodes University

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 2, 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, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As an exception, it is allowed to write an extension of Coco/R that is
used as a plugin in non-free software.

If not otherwise stated, any source code generated by Coco/R (other than
Coco/R itself) does not fall under the GNU General Public License.
-----------------------------------------------------------------------*/

#ifndef COCO_R_COMMON_H
#define COCO_R_COMMON_H

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#  include <climits>
#  include <cstdio>
#  include <cstdlib>
#  include <cstring>
#  include <cwchar>
#  include <iostream>
#  include <locale>
#  include <sstream>
#  include <string>

#  include <agrum/agrum.h>

// io.h and fcntl are used to ensure binary read from streams on windows
#  if _MSC_VER >= 1300
#    include <fcntl.h>
#    include <io.h>
#  endif

#  if _MSC_VER >= 1400
#    define coco_swprintf swprintf_s
#  elif _MSC_VER >= 1300
#    define coco_swprintf _snwprintf
#  elif defined __MINGW32__
#    define coco_swprintf _snwprintf
#  elif defined __GNUC__
#    define coco_swprintf swprintf
// cocoR genereted files may create unused-variable
#    pragma GCC diagnostic ignored "-Wunused-variable"
#  else
#    error unknown compiler!
#  endif

#  define COCO_WCHAR_MAX               65535
#  define HEAP_BLOCK_SIZE              (64 * 1024)
#  define COCO_CPP_NAMESPACE_SEPARATOR L':'

// ==========================================================================
// Buffer size thresholds for hybrid I/O strategy
// ==========================================================================
// Files smaller than this are loaded entirely into memory (fastest for small files)
#  define COCO_SMALL_FILE_THRESHOLD (2 * 1024 * 1024)   // 4 MB

// Files between SMALL and MMAP thresholds use large buffer with chunked reading
#  define COCO_LARGE_BUFFER_SIZE (4 * 1024 * 1024)   // 4 MB buffer

// Files larger than this use memory-mapped I/O (most efficient for large files)
#  define COCO_MMAP_FILE_THRESHOLD (64 * 1024 * 1024)   // 64 MB

// Legacy constants (kept for compatibility, but superseded by hybrid strategy)
#  define MIN_BUFFER_LENGTH 1024
#  define MAX_BUFFER_LENGTH COCO_LARGE_BUFFER_SIZE

// MappedBuffer system headers — must be included outside any namespace to avoid
// polluting gum:: with syscall() and related POSIX symbols (GCC 14 RHEL issue).
#  if defined(_WIN32)
#    ifndef NOGDI
#      define NOGDI
#      include <windows.h>
#      undef NOGDI
#    else
#      include <windows.h>
#    endif
#  else
#    include <fcntl.h>
#    include <unistd.h>

#    include <sys/mman.h>
#    include <sys/stat.h>
#  endif

namespace gum {

  /// string handling, wide character
  wchar_t*     coco_string_create(const wchar_t* value);
  wchar_t*     coco_string_create(const wchar_t* value, int startIndex);
  wchar_t*     coco_string_create(const wchar_t* value, int startIndex, int length);
  wchar_t*     coco_string_create_upper(const wchar_t* data);
  wchar_t*     coco_string_create_lower(const wchar_t* data);
  wchar_t*     coco_string_create_lower(const wchar_t* data, int startIndex, int dataLen);
  wchar_t*     coco_string_create_append(const wchar_t* data1, const wchar_t* data2);
  wchar_t*     coco_string_create_append(const wchar_t* data, const wchar_t value);
  void         coco_string_delete(wchar_t*& data);
  int          coco_string_length(const wchar_t* data);
  bool         coco_string_endswith(const wchar_t* data, const wchar_t* value);
  int          coco_string_indexof(const wchar_t* data, const wchar_t value);
  int          coco_string_lastindexof(const wchar_t* data, const wchar_t value);
  void         coco_string_merge(wchar_t*& data, const wchar_t* value);
  bool         coco_string_equal(const wchar_t* data1, const wchar_t* data2);
  int          coco_string_compareto(const wchar_t* data1, const wchar_t* data2);
  unsigned int coco_string_hash(const wchar_t* data);

  /// string handling, ascii character
  wchar_t* coco_string_create(const char* value);
  char*    coco_string_create_char(const wchar_t* value);
  void     coco_string_delete(char*& data);

  /// CocoR uses unicode, thus use this to cast string in wstring.
  inline std::wstring widen(const std::string& str) {
    std::wostringstream          wstm;
    const std::locale            loc     = wstm.getloc();
    const std::ctype< wchar_t >& ctfacet = std::use_facet< std::ctype< wchar_t > >(loc);

    for (size_t i = 0; i < str.size(); ++i)
      wstm << ctfacet.widen(str[i]);

    return wstm.str();
  }

  /// CocoR uses unicode, thus use this to cast wstring in string.
  inline std::string narrow(const std::wstring& str) {
    std::ostringstream           stm;
    const std::locale            loc     = stm.getloc();
    const std::ctype< wchar_t >& ctfacet = std::use_facet< std::ctype< wchar_t > >(loc);

    for (size_t i = 0; i < str.size(); ++i)
      stm << ctfacet.narrow(str.at(i),
                            0);   // std::ctype<char>::_Elem(str[i]), 0 );

    return stm.str();
  }

  inline float coco_atof(const wchar_t* c) {
    float              res;
    std::istringstream istr(narrow(c));
    istr.imbue(std::locale("C"));
    istr >> res;
    return res;
  }

  inline int coco_atoi(const wchar_t* c) {
    int                res;
    std::istringstream istr(narrow(c));
    istr.imbue(std::locale("C"));
    istr >> res;
    return res;
  }

  // ==========================================================================
  // Token - Represents a lexical token
  // ==========================================================================
  class Token {
    public:
    int      kind;      // token kind
    int      pos;       // token position in bytes in the source text (starting at 0)
    int      charPos;   // token position in characters in the source text (starting at 0)
    int      col;       // token column (starting at 1)
    int      line;      // token line (starting at 1)
    wchar_t* val;       // token value
    Token*   next;      // ML 2005-03-11 Peek tokens are kept in linked list

    Token();
    ~Token();
  };

  // ==========================================================================
  // Buffer - Abstract base class for input buffering
  // ==========================================================================
  // Hybrid I/O strategy based on file size:
  // - Small files (< 2MB): InMemoryBuffer - entire file loaded at once
  // - Medium files (2-64MB): StreamBuffer - large buffer with chunked reading
  // - Large files (>= 64MB): MappedBuffer - memory-mapped I/O
  // ==========================================================================
  class Buffer {
    protected:
    int fileLen_;   // length of input stream
    int bufPos_;    // current position in buffer

    public:
    static const int EoF = COCO_WCHAR_MAX + 1;

    Buffer() : fileLen_(0), bufPos_(0) {}

    virtual ~Buffer() = default;

    // Factory method: creates optimal buffer based on file size
    static Buffer* CreateOptimal(const char* fileName);
    static Buffer* CreateOptimal(FILE* s, bool isUserStream);
    static Buffer* CreateFromMemory(const unsigned char* data, int len);

    virtual void     Close()                     = 0;
    virtual int      Read()                      = 0;
    virtual int      Peek()                      = 0;
    virtual wchar_t* GetString(int beg, int end) = 0;
    virtual int      GetPos()                    = 0;
    virtual void     SetPos(int value)           = 0;

    int GetPercent() { return (fileLen_ > 0) ? (int)((100.0 * GetPos()) / fileLen_) : 0; }

    int GetFileLen() const { return fileLen_; }
  };

  // ==========================================================================
  // InMemoryBuffer - For small files (< COCO_SMALL_FILE_THRESHOLD)
  // ==========================================================================
  // Loads entire file into memory at once. Fastest for small files.
  class InMemoryBuffer: public Buffer {
    private:
    unsigned char* buf_;
    bool           ownsData_;

    public:
    InMemoryBuffer(const unsigned char* data, int len, bool copy = true);
    InMemoryBuffer(FILE* s);
    ~InMemoryBuffer() override;

    void Close() override {}

    int Read() override { return (bufPos_ < fileLen_) ? buf_[bufPos_++] : EoF; }

    int Peek() override { return (bufPos_ < fileLen_) ? buf_[bufPos_] : EoF; }

    wchar_t* GetString(int beg, int end) override;

    int GetPos() override { return bufPos_; }

    void SetPos(int value) override {
      bufPos_ = (value >= 0 && value <= fileLen_) ? value : bufPos_;
    }
  };

  // ==========================================================================
  // StreamBuffer - For medium files (COCO_SMALL_FILE_THRESHOLD to COCO_MMAP_FILE_THRESHOLD)
  // ==========================================================================
  // Uses a large buffer with chunked reading from file stream.
  class StreamBuffer: public Buffer {
    private:
    unsigned char* buf_;
    int            bufCapacity_;
    int            bufStart_;   // position of first byte in buffer relative to input stream
    int            bufLen_;     // length of valid data in buffer
    FILE*          stream_;
    bool           isUserStream_;

    int  ReadNextStreamChunk();
    bool CanSeek();

    public:
    StreamBuffer(FILE* s, bool isUserStream);
    StreamBuffer(StreamBuffer* b);   // for UTF8Buffer wrapping
    ~StreamBuffer() override;

    void     Close() override;
    int      Read() override;
    int      Peek() override;
    wchar_t* GetString(int beg, int end) override;

    int GetPos() override { return bufPos_ + bufStart_; }

    void SetPos(int value) override;
  };

  // ==========================================================================
  // MappedBuffer - For large files (>= COCO_MMAP_FILE_THRESHOLD)
  // ==========================================================================
  // Uses memory-mapped I/O for efficient access to very large files.
  // (System headers for mmap/fcntl/unistd are included above, outside namespace gum.)

  class MappedBuffer: public Buffer {
    private:
    unsigned char* mappedData_;
#  if defined(_WIN32)
    HANDLE fileHandle_;
    HANDLE mappingHandle_;
#  else
    int fd_;
#  endif

    public:
    explicit MappedBuffer(const char* fileName);
    ~MappedBuffer() override;

    void Close() override;

    int Read() override { return (bufPos_ < fileLen_) ? mappedData_[bufPos_++] : EoF; }

    int Peek() override { return (bufPos_ < fileLen_) ? mappedData_[bufPos_] : EoF; }

    wchar_t* GetString(int beg, int end) override;

    int GetPos() override { return bufPos_; }

    void SetPos(int value) override {
      bufPos_ = (value >= 0 && value <= fileLen_) ? value : bufPos_;
    }
  };

  // ==========================================================================
  // UTF8Buffer - Wrapper for UTF-8 decoding (works with any buffer type)
  // ==========================================================================
  class UTF8Buffer: public Buffer {
    private:
    Buffer* innerBuffer_;
    bool    ownsInner_;

    public:
    UTF8Buffer(Buffer* b, bool takeOwnership = true);
    ~UTF8Buffer() override;

    void Close() override {
      if (innerBuffer_) innerBuffer_->Close();
    }

    int Read() override;
    int Peek() override;

    wchar_t* GetString(int beg, int end) override { return innerBuffer_->GetString(beg, end); }

    int GetPos() override { return innerBuffer_->GetPos(); }

    void SetPos(int value) override { innerBuffer_->SetPos(value); }
  };

  // ==========================================================================
  // StartStates - Maps characters to start states of tokens
  // ==========================================================================
  class StartStates {
    private:
    class Elem {
      public:
      int   key, val;
      Elem* next;

      Elem(int key, int val) : key(key), val(val), next(nullptr) {}
    };

    Elem** tab;

    public:
    StartStates() {
      tab = new Elem*[128];
      memset(tab, 0, 128 * sizeof(Elem*));
    }

    virtual ~StartStates() {
      for (int i = 0; i < 128; ++i) {
        Elem* e = tab[i];
        while (e != nullptr) {
          Elem* next = e->next;
          delete e;
          e = next;
        }
      }
      delete[] tab;
    }

    void set(int key, int val) {
      Elem* e = new Elem(key, val);
      int   k = ((unsigned int)key) % 128;
      e->next = tab[k];
      tab[k]  = e;
    }

    int state(int key) {
      Elem* e = tab[((unsigned int)key) % 128];
      while (e != nullptr && e->key != key)
        e = e->next;
      return e == nullptr ? 0 : e->val;
    }
  };

  // ==========================================================================
  // KeywordMap - Maps strings to integers (identifiers to keyword kinds)
  // ==========================================================================
  class KeywordMap {
    private:
    class Elem {
      public:
      wchar_t* key;
      int      val;
      Elem*    next;

      Elem(const wchar_t* key, int val) : val(val), next(nullptr) {
        this->key = coco_string_create(key);
      }

      virtual ~Elem() { coco_string_delete(key); }
    };

    Elem** tab;

    public:
    KeywordMap() {
      tab = new Elem*[128];
      memset(tab, 0, 128 * sizeof(Elem*));
    }

    virtual ~KeywordMap() {
      for (int i = 0; i < 128; ++i) {
        Elem* e = tab[i];
        while (e != nullptr) {
          Elem* next = e->next;
          delete e;
          e = next;
        }
      }
      delete[] tab;
    }

    void set(const wchar_t* key, int val) {
      Elem* e = new Elem(key, val);
      int   k = coco_string_hash(key) % 128;
      e->next = tab[k];
      tab[k]  = e;
    }

    int get(const wchar_t* key, int defaultVal) {
      Elem* e = tab[coco_string_hash(key) % 128];
      while (e != nullptr && !coco_string_equal(e->key, key))
        e = e->next;
      return e == nullptr ? defaultVal : e->val;
    }
  };

}   // namespace gum

#endif   // DOXYGEN_SHOULD_SKIP_THIS

#endif   // COCO_R_COMMON_H
