/* pdatei.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2002-2023 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 "pdatei.h"
#include "aguix/lowlevelfunc.h"
#include "worker_lfs.h"
#include "nwc_fsentry.hh"

PDatei::PDatei()
{
  type = PDATEI_UNKNOWN;
  fd = -1;
  name = "";
}

PDatei::~PDatei()
{
  if ( fd >=0 ) {
    close();
  }
}

int PDatei::open( const char *filename, open_param_t mode )
{
    int flags = O_RDONLY;

    close();

    if ( filename == NULL ) return 1;

    NWC::FSEntry fse( filename );
    if ( mode == NONBLOCKING_FOR_NONREGULAR &&
         fse.entryExists() == true &&
         fse.isReg( true ) == false ) {
        flags |= O_NONBLOCK;
    }

    enum pdatei_handler candidate = getCruncher( filename );
    std::string vfsname = getVFSName( filename, candidate );
  
    // try to open file
    fd = ::worker_open( vfsname.c_str(), flags, 0 );
    if ( fd >= 0 ) {
        type = candidate;
        name = filename;
    } else {
        fd = ::worker_open( filename, flags, 0 );
        if ( fd >= 0 ) {
            type = PDATEI_NORMAL;
            name = filename;
        }
    }
    if ( fd < 0 ) return 1;
    return 0;
}

ssize_t PDatei::read( void *buf, size_t size )
{
  if ( fd < 0 || buf == NULL || size < 0 ) return -1;

  return ::worker_read( fd, buf, size );
}

void PDatei::close()
{
  if ( fd >= 0 ) {
    ::worker_close( fd );
    fd = -1;
    type = PDATEI_UNKNOWN;
    name = "";
  }
}

enum PDatei::pdatei_handler PDatei::getCruncher( const char *filename )
{
    enum pdatei_handler candidate = PDATEI_UNKNOWN;
    int basefd;
    int flags = O_RDONLY;
    char tbuf[5];
    int r;

    if ( filename == NULL ) return candidate;

    NWC::FSEntry fse( filename );
    if ( fse.entryExists() == true &&
         fse.isReg( true ) == false ) {
        flags |= O_NONBLOCK;
    }

    basefd = ::worker_open( filename, flags, 0 );
    if ( basefd < 0 ) return candidate;

    candidate = PDATEI_NORMAL;
  
    // identify type
    r = ::worker_read( basefd, tbuf, 4 );
    if ( r == 4 ) {
        if ( ( tbuf[0] == 'X' ) &&
             ( tbuf[1] == 'P' ) &&
             ( tbuf[2] == 'K' ) &&
             ( tbuf[3] == 'F' ) ) {
            candidate = PDATEI_XPK;
        } else if ( ( tbuf[0] == 'B' ) &&
                    ( tbuf[1] == 'Z' ) &&
                    ( tbuf[2] == 'h' ) ) {
            candidate = PDATEI_BZIP2;
        } else {
            const unsigned char *tbuf2 = (const unsigned char*)tbuf;
            if ( ( tbuf2[0] == 0x1f ) &&
                 ( tbuf2[1] == 0x8b ) ) {
                candidate = PDATEI_GZIP;
            }
        }
    }
    ::worker_close( basefd );

    return candidate;
}

enum PDatei::pdatei_handler PDatei::getHandler()
{
  return type;
}

const std::string PDatei::getVFSName( const char *filename, enum pdatei_handler type )
{
    if ( ! filename ) return "";

    if ( type == PDATEI_XPK ) {
        std::string vfsname = filename;
        vfsname += "#uxpk/CONTENT";

        return vfsname;
    } else if ( type == PDATEI_BZIP2 ) {
        std::string vfsname = filename;
        vfsname += "#ubz2";

        return vfsname;
    } else if ( type == PDATEI_GZIP ) {
        std::string vfsname = filename;
        vfsname += "#ugz";

        return vfsname;
    }

    return filename;
}

loff_t PDatei::getFileSize( const std::string &filename )
{
    if ( filename.empty() ) return 0;

    enum pdatei_handler candidate = getCruncher( filename.c_str() );
    std::string vfsname = getVFSName( filename.c_str(), candidate );
  
    NWC::FSEntry fse( vfsname );

    if ( fse.isReg( true ) == true ) {
        if ( fse.isLink() == false ) {
            return fse.stat_size();
        } else {
            return fse.stat_dest_size();
        }
    }

    return 0;
}
