/* $Header$ */

/* 
 *   Copyright (c) 2010 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  vmtmpfil.h - CVmObjTemporaryFile object
Function
  The TemporaryFile intrinsic class describes a temp file on disk.  This
  isn't an open instance of a temp file; rather, it basically represents a
  filename in the local file system.

  The reason to make this an object is that it provides a natural way of
  identifying and freeing temp files.  The File class can validate that the
  program is supplying a valid temporary file generated by the system,
  because only the system can create a TemporaryFile object.  This prevents a
  malicious program from trying to circumvent the file safety levels by
  faking a temp file name; it's essentially impossible to invent a fake name
  since the name is encapsulated in the system object.  The system can tell
  when it's safe to delete the underlying file system object through the
  magic of garbage collection: when the TemporaryFile object is collected,
  the program is definitely done with it, since the program doesn't have any
  references to it.
Notes
  
Modified
  11/20/10 MJRoberts  - Creation
*/

#ifndef VMTMPFIL_H
#define VMTMPFIL_H

#include "t3std.h"
#include "vmtype.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmundo.h"


/* ------------------------------------------------------------------------ */
/*
 *   Image file data block:
 *   
 *   This object doesn't store anything in an image file.  A temporary file
 *   object is completely transient and doesn't have anything to store.  
 */


/* ------------------------------------------------------------------------ */
/*
 *   Our in-memory extension data structure.  This is usually a 'struct'
 *   that contains the same information as in the image file, but using
 *   native types.
 */
struct vm_tmpfil_ext
{
    /* allocate the structure */
    static vm_tmpfil_ext *alloc_ext(
        VMG_ class CVmObjTemporaryFile *self, const char *filename);

    /* 
     *   The filename, as a null-terminated string.  We overallocate the
     *   structure with enough space.  If this is set to an empty string, it
     *   means that the temporary file is no longer valid.  This happens if
     *   the object is loaded from an image file or explicitly released via
     *   the deleteFile() method.  
     */
    char filename[1];
};


/* ------------------------------------------------------------------------ */
/*
 *   CVmObjTemporaryFile intrinsic class definition
 */

class CVmObjTemporaryFile: public CVmObject
{
    friend class CVmMetaclassTemporaryFile;
    
public:
    /* metaclass registration object */
    static class CVmMetaclass *metaclass_reg_;
    class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }

    /* am I of the given metaclass? */
    virtual int is_of_metaclass(class CVmMetaclass *meta) const
    {
        /* try my own metaclass and my base class */
        return (meta == metaclass_reg_
                || CVmObject::is_of_metaclass(meta));
    }

    /* is this a CVmObjTemporaryFile object? */
    static int is_tmpfil_obj(VMG_ vm_obj_id_t obj)
        { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }

    /* 
     *   Get the name of the temp file (as a null-terminated string).
     *   Returns null if the temp file is invalid. 
     */
    const char *get_fname() const
    {
        const char *f = get_ext()->filename;
        return f[0] != 0 ? f : 0;
    }

    /* create dynamically using stack arguments */
    static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
                                         uint argc);

    /* 
     *   call a static property - we don't have any of our own, so simply
     *   "inherit" the base class handling 
     */
    static int call_stat_prop(VMG_ vm_val_t *result,
                              const uchar **pc_ptr, uint *argc,
                              vm_prop_id_t prop)
    {
        return CVmObject::
            call_stat_prop(vmg_ result, pc_ptr, argc, prop);
    }

    /* notify of deletion */
    void notify_delete(VMG_ int in_root_set);

    /* set a property */
    void set_prop(VMG_ class CVmUndo *undo,
                  vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val);

    /* get a property */
    int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
                 vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc);

    /* 
     *   receive savepoint notification - we don't keep any
     *   savepoint-relative records, so we don't need to do anything here 
     */
    void notify_new_savept() { }
    
    /* apply an undo record */
    void apply_undo(VMG_ struct CVmUndoRecord *rec);
    
    /* discard an undo record */
    void discard_undo(VMG_ struct CVmUndoRecord *);
    
    /* mark our undo record references */
    void mark_undo_ref(VMG_ struct CVmUndoRecord *) { }

    /* mark our references */
    void mark_refs(VMG_ uint) { }

    /* remove weak references */
    void remove_stale_weak_refs(VMG0_) { }
    void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }

    /* load from an image file */
    void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);

    /* reload from an image file */
    void reload_from_image(VMG_ vm_obj_id_t self,
                           const char *ptr, size_t siz);

    /* rebuild for image file */
    virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);

    /* save to a file */
    void save_to_file(VMG_ class CVmFile *fp);

    /* restore from a file */
    void restore_from_file(VMG_ vm_obj_id_t self,
                           class CVmFile *fp, class CVmObjFixup *fixups);

    /* we're immutable, so we can't have changed since loading */
    int is_changed_since_load() const { return FALSE; }

protected:
    /* get my extension data */
    vm_tmpfil_ext *get_ext() const { return (vm_tmpfil_ext *)ext_; }

    /* load or reload image data */
    void load_image_data(VMG_ const char *ptr, size_t siz);

    /* create a with no initial contents */
    CVmObjTemporaryFile() { ext_ = 0; }

    /* create with contents */
    CVmObjTemporaryFile(VMG_ const char *fname);

    /* property evaluator - undefined function */
    int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }

    /* getFilename() method */
    int getp_getFilename(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);

    /* deleteFile() method */
    int getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);

    /* property evaluation function table */
    static int (CVmObjTemporaryFile::*func_table_[])(VMG_ vm_obj_id_t self,
        vm_val_t *retval, uint *argc);
};


/* ------------------------------------------------------------------------ */
/*
 *   CVmObjTemporaryFile metaclass registration table object 
 */
class CVmMetaclassTemporaryFile: public CVmMetaclass
{
public:
    /* get the global name */
    const char *get_meta_name() const { return "tempfile/030000"; }

    /* create from image file */
    void create_for_image_load(VMG_ vm_obj_id_t id)
    {
        new (vmg_ id) CVmObjTemporaryFile();
        G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE);
    }

    /* create from restoring from saved state */
    void create_for_restore(VMG_ vm_obj_id_t id)
    {
        new (vmg_ id) CVmObjTemporaryFile();
        G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE);
    }

    /* create dynamically using stack arguments */
    vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
        { return CVmObjTemporaryFile::create_from_stack(vmg_ pc_ptr, argc); }
    
    /* call a static property */
    int call_stat_prop(VMG_ vm_val_t *result,
                       const uchar **pc_ptr, uint *argc,
                       vm_prop_id_t prop)
    {
        return CVmObjTemporaryFile::
            call_stat_prop(vmg_ result, pc_ptr, argc, prop);
    }
};

#endif /* VMTMPFIL_H */

/*
 *   Register the class 
 */
VM_REGISTER_METACLASS(CVmObjTemporaryFile)
