<?php

/**
 * PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
 *
 * @package Shapefile
 * @author  Gaspare Sganga
 * @version 4.0.0dev
 * @license MIT
 * @link    https://gasparesganga.com/labs/php-shapefile/
 */

namespace Shapefile\File;

use Shapefile\Shapefile;
use Shapefile\ShapefileException;

/**
 * Default FileInterface implementation.
 * It allows reading/writing of files in binary mode.
 * It accepts both filepaths and stream resource handles.
 */
class StreamResourceFile implements FileInterface
{
    /////////////////////////////// PRIVATE VARIABLES ///////////////////////////////
    /**
     * @var resource    File resource handle.
     */
    private $handle = null;
    
    /**
     * @var bool        Flag to store whether file was passed as resource handle or not.
     */
    private $flag_resource = false;
    
    
    
    /////////////////////////////// PUBLIC ///////////////////////////////
    /**
     * Constructor.
     * Opens file with binary read or write access.
     *
     * @param   string|resource $file           Path to file or resource handle.
     * @param   bool            $write_access   Access type: false = read; true = write.
     */
    public function __construct($file, $write_access)
    {
        $this->flag_resource = is_resource($file);
        
        if ($this->flag_resource) {
            $this->handle = $file;
            if (get_resource_type($this->handle) !== 'stream' || !stream_get_meta_data($this->handle)['seekable']) {
                throw new ShapefileException(Shapefile::ERR_FILE_RESOURCE_NOT_VALID);
            }
            if ((!$write_access && !$this->isReadable()) || ($write_access && !$this->isWritable())) {
                throw new ShapefileException(Shapefile::ERR_FILE_PERMISSIONS);
            }
        } else {
            $this->handle = @fopen($file, $write_access ? 'c+b' : 'rb');
            if ($this->handle === false) {
                throw new ShapefileException(Shapefile::ERR_FILE_OPEN);
            }
        }
    }
    
    /**
     * Destructor.
     *
     * Closes file if it was NOT passed as resource handle.
     */
    public function __destruct()
    {
        if (!$this->flag_resource) {
            fclose($this->handle);
        }
    }
    
    
    /**
     * Gets canonicalized absolute pathname.
     */
    public function getFilepath()
    {
        return realpath(stream_get_meta_data($this->handle)['uri']);
    }
    
    
    public function isReadable()
    {
        return in_array(stream_get_meta_data($this->handle)['mode'], ['rb', 'r+b', 'w+b', 'x+b', 'c+b']);
    }
    
    public function isWritable()
    {
        return in_array(stream_get_meta_data($this->handle)['mode'], ['r+b', 'wb', 'w+b', 'xb', 'x+b', 'cb', 'c+b']);
    }
    
    
    public function truncate($size)
    {
        ftruncate($this->handle, $size);
    }
    
    public function getSize()
    {
        return fstat($this->handle)['size'];
    }
    
    
    public function getPointer()
    {
        return ftell($this->handle);
    }
    
    public function setPointer($position)
    {
        fseek($this->handle, $position, SEEK_SET);
    }
    
    public function resetPointer()
    {
        fseek($this->handle, 0, SEEK_END);
    }
    
    public function setOffset($offset)
    {
        fseek($this->handle, $offset, SEEK_CUR);
    }
    
    
    public function read($length)
    {
        return @fread($this->handle, $length);
    }
    
    public function write($data)
    {
        if (@fwrite($this->handle, $data) === false) {
            return false;
        }
        return true;
    }
}