/* textstoragefile.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2005-2025 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "textstoragefile.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "worker_locale.h"
#include "pdatei.h"
#include <errno.h>
#include "aguix/lowlevelfunc.h"

TextStorageFile::TextStorageFile( const std::string &filename,
                                  const RefCount<AWidth> &lencalc,
                                  size_t initial_size,
                                  enum TextStorageString::text_storage_converion convert ) : m_tss( "", lencalc ),
                                                                                             m_filename( filename ),
                                                                                             m_current_size( 64 * 1024 ),
                                                                                             m_lencalc( lencalc ),
                                                                                             m_incomplete_file( true ),
                                                                                             m_file_size( -1 ),
                                                                                             m_convert( convert )
{
    if ( initial_size > 0 && initial_size <= 1 * 1024 * 1024 * 1024 ) {
        m_current_size = initial_size;
    }
    
    readFile();
}

int TextStorageFile::getNrOfLines() const
{
    return m_tss.getNrOfLines();
}

int TextStorageFile::getLine( int line_nr, unsigned int offset, int len, std::string &return_line ) const
{
    return m_tss.getLine( line_nr, offset, len, return_line );
}

int TextStorageFile::getLine( int line_nr, unsigned int offset, std::string &return_line ) const
{
    return getLine( line_nr, offset, -1, return_line );
}

int TextStorageFile::getMaxLineWidth() const
{
    return m_tss.getMaxLineWidth();
}

int TextStorageFile::getLineWidth( int line_nr ) const
{
    return m_tss.getLineWidth( line_nr );
}

void TextStorageFile::setLineLimit( int new_limit )
{
    m_tss.setLineLimit( new_limit );
}

int TextStorageFile::getLineLimit()
{
    return m_tss.getLineLimit();
}

std::pair<int,int> TextStorageFile::getRealLinePair( int line_nr ) const
{
    return m_tss.getRealLinePair( line_nr );
}

AWidth &TextStorageFile::getAWidth()
{
    return m_tss.getAWidth();
}

void TextStorageFile::setAWidth( const RefCount<AWidth> &lencalc )
{
    m_lencalc = lencalc;
    m_tss.setAWidth( lencalc );
}

int TextStorageFile::findLineNr( std::pair<int,int> real_line ) const
{
    return m_tss.findLineNr( real_line );
}

void TextStorageFile::readFile()
{
    char *buffer;
    PDatei pd;
    size_t buffer_size;

    buffer_size = m_current_size;
    if ( m_file_size >= 0 ) {
        buffer_size = m_file_size;
    }

    if ( buffer_size < 0 ) return;

    buffer = (char*)_allocsafe( buffer_size );

    if ( pd.open( m_filename.c_str(), PDatei::NONBLOCKING_FOR_NONREGULAR ) == 0 ) {
        ssize_t last_read = 0;
        size_t read_bytes = 0;
        time_t begin_time, current_time;

        begin_time = time( NULL );

        do {
            last_read = pd.read( buffer + read_bytes, buffer_size - read_bytes );
            if ( last_read > 0 ) {
                read_bytes += last_read;
            } else if ( last_read < 0 && errno == EAGAIN ) {
                // if we should try again (for nonblocking mode)
                // wait some time and try again but not longer than 3 seconds
                current_time = time( NULL );
                if ( ( current_time - begin_time ) <= 3 ) {
                    waittime( 10 );
                    
                    // set last_read to 1 so the loop continues
                    last_read = 1;
                    continue;
                }
            }
        } while ( last_read > 0 && read_bytes < buffer_size );

        if ( read_bytes > 0 || ( read_bytes == 0 && last_read >= 0 ) ) {
            //      m_tss = TextStorageString( buffer, read_bytes );
            m_tss = TextStorageString( buffer, read_bytes, m_lencalc, m_tss.getLineLimit(), m_convert );

            // try to read another byte to check for EOF
            // (it could be done by asking for the actual size
            //  but that wouldn't work with pipes...)
            if ( pd.read( buffer, 1 ) < 1 ) {
                m_incomplete_file = false;
                m_file_size = read_bytes;
            } else {
                m_incomplete_file = true;
            }
        } else {
            char *tstr = (char*)_allocsafe( strlen( catalog.getLocale( 281 ) ) +
                                            strlen( m_filename.c_str() ) + 1 );
            sprintf( tstr, catalog.getLocale( 281 ), m_filename.c_str() );
            m_tss = TextStorageString( tstr, m_lencalc );
            _freesafe( tstr );
        }
    }

    _freesafe( buffer );
}

void TextStorageFile::readMore( size_t size )
{
    if ( size > 0 && size < 1 * 1024 * 1024 * 1024 ) {
        m_current_size = size;
    } else {
        if ( m_current_size < 100 * 1024 * 1024 ) {
            m_current_size *= 2;
        } else {
            m_current_size += 100 * 1024 * 1024;
        }
        if ( m_current_size > 1 * 1024 * 1024 * 1024 ) {
            m_current_size = 1 * 1024 * 1024 * 1024;
        }
    }
    
    readFile();
}

void TextStorageFile::reloadFile()
{
    m_file_size = -1;
    readFile();
}

bool TextStorageFile::incompleteFile()
{
    return m_incomplete_file;
}

int TextStorageFile::getNrOfUnwrappedLines() const
{
    return m_tss.getNrOfUnwrappedLines();
}

int TextStorageFile::getUnwrappedLine( int line_nr,
                                       unsigned int offset,
                                       int len,
                                       std::string &return_line ) const
{
    return m_tss.getUnwrappedLine( line_nr,
                                   offset,
                                   len,
                                   return_line );
}

int TextStorageFile::getUnwrappedLine( int line_nr,
                                       unsigned int offset,
                                       std::string &return_line ) const
{
    return m_tss.getUnwrappedLine( line_nr,
                                   offset,
                                   return_line );
}

int TextStorageFile::getUnwrappedLineRaw( int line_nr,
                                          unsigned int offset,
                                          int len,
                                          std::string &return_line ) const
{
    return m_tss.getUnwrappedLineRaw( line_nr,
                                      offset,
                                      len,
                                      return_line );
}

int TextStorageFile::getUnwrappedLineRaw( int line_nr,
                                          unsigned int offset,
                                          std::string &return_line ) const
{
    return m_tss.getUnwrappedLineRaw( line_nr,
                                      offset,
                                      return_line );
}

std::pair< std::pair< int, int >, int > TextStorageFile::getLineForOffset( int unwrapped_line_nr, int line_offset ) const
{
    return m_tss.getLineForOffset( unwrapped_line_nr,
                                   line_offset );
}

void TextStorageFile::setFileName( const std::string &filename, size_t initial_size )
{
    if ( initial_size > 0 && initial_size <= 1 * 1024 * 1024 * 1024 ) {
        m_current_size = initial_size;
    }

    m_incomplete_file = true;
    m_file_size = -1;
    m_filename = filename;
    
    readFile();
}

size_t TextStorageFile::getCurrentSize() const
{
    return m_current_size;
}

void TextStorageFile::setConvertMode( enum TextStorageString::text_storage_converion convert )
{
    m_tss.setConvertMode( convert );
}

void TextStorageFile::resetContent()
{
    m_incomplete_file = true;
    m_file_size = -1;
    m_filename = "";
    
    m_tss = TextStorageString( "", m_lencalc );
}
