/*
   FALCON - The Falcon Programming Language.
   FILE: module.h

   Falcon module extensions.
   -------------------------------------------------------------------
   Author: Giancarlo Niccolai
   Begin: mar ago 3 2004

   -------------------------------------------------------------------
   (C) Copyright 2004: the FALCON developers (see list in AUTHORS file)

   See LICENSE file for licensing details.
*/

#ifndef FLC_MODULE_H
#define FLC_MODULE_H

#include <falcon/setup.h>
#include <falcon/string.h>
#include <falcon/symbol.h>
#include <falcon/symtab.h>
#include <falcon/types.h>
#include <falcon/deptab.h>
#include <falcon/linemap.h>
#include <falcon/dll.h>
#include <falcon/strtable.h>
#include <falcon/service.h>
#include <falcon/genericmap.h>
#include <falcon/basealloc.h>

namespace Falcon {

class String;
class Stream;
class AttribMap;

/** Module class abstraction.

   A module is uniquely indicated by its name; loading a module which has the same name of a
   previous one will make the other existing module to be discarded.

   When a module is added to the VM, it requests the module symbol table. The exported symbols
   are then loaded in the global VM symbol table.
   Only a module may be executed; when it is executed, it may load additional modules (the vm
   acts in its behalf) and then the VM creates the global context array.

   The array is filled with the items provided in the symbols of the symbol table, or with
   NIL were missing; if symbols are external, they are searched in the global VM symbol table
   (and again filled with the provided item or with NIL).

   A module may even exist without code. This is the case of pure C modules that register
   themselves using only C functions as symbol's initial items.

*/
class FALCON_DYN_CLASS Module: public BaseAlloc
{
protected:

   mutable volatile int32 m_refcount;

   /******************************
   * Anagraphic section
   *******************************/
   char m_version;
   char m_subversion;
   String m_name;
   String m_path;
   String m_language;
   uint32 m_modVersion;
   uint32 m_engineVersion;

   /******************************
   * Tables section
   *******************************/
   StringTable* m_strTab;
   bool m_bOwnStringTable;
   SymbolVector m_symbols;
   SymbolTable m_symtab;
   DependTable m_depend;
   /** The line map is a pointer as it can be absent or given after the generation step. */
   LineMap *m_lineInfo;

   /** Dynamic link loader attached with this module.
      If this module is created using a DLL, then it is necessary to close
      the DLL handle as soon as the module is disposed (or, at least,
      not before). For this reason, the loader that generated the module
      should be contained in the module itself.

      The loader provides internally a system to provide resource reference
      counting, so that if it insists on the same handler as other loaders
      (contained by other module instances), the correct library handler count
      is kept (Actually, this service is usually available at kernel level on
      many OSs).

      Also, DllLoader instance contains usually a minimal representation for
      the DLL handler and points to a minimal set of non-virtual methods to
      manage it, so it has the same weight as a reference count or even a
      pointer to the an handler. This justifies the presence of this field
      also in modules that are not generated by DLLs, but are loaded from
      binary Falcon objects.
   */
   DllLoader *m_loader;

   /** Detach the loader for decref.
      If the module has been loaded through a DLL, decreffing it to death
      should unload the module. If this was the last instance of the module
      in memory, the return address of the DllLoader desctructor may not
      be available anymore when the destructor returns.

      For this reason, the decref() member calls this method and destroys
      autonomously the handler after having destroyed the module.
   */
   DllLoader *detachLoader();

   Map m_serviceMap;

   AttribMap* m_attributes;

   virtual ~Module();

public:

   static const uint32 c_noEntry;
   /** Variable parameter initializer semantic.
      Use the following functions to initialize the module.
      If a SymbolTable is not provided here, an empty one is immediately created.
   */
   Module();
   Module &name( const String &n ) { m_name.bufferize( n ); return *this; }
   Module &path( const String &p ) { m_path.bufferize( p ); return *this; }
   Module &language( const String &lang ) { m_language = lang; return *this; }


   Module &version( uint32 version ) { m_modVersion = version; return *this; }
   Module &version( int major, int minor, int revision ) {
      m_modVersion = major << 16 | minor << 8 | revision;
      return *this;
   }

   Module &engineVersion( uint32 version ) { m_engineVersion = version; return *this; }
   Module &engineVersion( int major, int minor, int revision )  {
      m_engineVersion = major << 16 | minor << 8 | revision;
      return *this;
   }

   virtual char pcodeVersion() const;
   virtual char pcodeSubVersion() const;

   void incref() const;
   void decref() const;

   /** Determine this to be a flexy module.
      Flexy modules can be queried for unknown symbols at runtime;
      they declare themselves as sticking only with this VM (or otherwise
      providing a VM-sensible local environment), and are available
      for late-lazy binding of symbols.
   */
   virtual bool isFlexy() const { return false; }


   Symbol *findGlobalSymbol( const String &name ) const
   {
      return m_symtab.findByName( name );
   }

   const String &name() const { return m_name; }
   const String &path() const { return m_path; }
   const String &language() const { return m_language;}

   String *addCString( const char *pos, uint32 size );

   /** Add a string to the module string table.
      If the string already exists, the previous entry is returned.
      \note If the string isn't found in the string table and is
         created on the spod, its exported status
         (international string) is copied from \b st.

      \param st the string to be added.
      \return A pointer to the string in the module string table.
   */
   String *addString( const String &st );

   /** Add a string to the module string table and eventually exports it.
      If the string already exists, the previous entry is returned.
      This method also set the "exported" bit of the returned string,
      so that the string can be directly used in exported intenrationalization
      tables.
      \param st the string to be added.
      \param exported set to true to make this string subject to
                      internationalization table exports.
      \return A pointer to the string in the module string table.
   */
   String *addString( const String &st, bool exported );

   /** Add a string to the module string table, but return its ID.
       This always adds a new string, even if another
       identical string already existed.

      \param st the string to be added.
      \param exported set to true to make this string subject to
                      internationalization table exports.

       \return The ID in the string table of the newly added string.
   */
   uint32 addStringID( const String &st, bool exported );

   /** Return a string given an ID in the string table.
      This function doesn't check for out-of-bounds access, so be careful.

      \param id the ID of the string in the string table (insertion order).
      \return A pointer to the string.
   */
   const String *getString( uint32 id ) const { return m_strTab->get(id); }

   Symbol *getSymbol( uint32 id ) const
   {
      if ( id > m_symbols.size() )
         return 0;
      return *(Symbol **) m_symbols.at(id);
   }

   /** Returns the module symbol table (const version).
      This holds the global variables that are available at module level.
      \return the global symbol table (const version).
   */
   const SymbolTable &symbolTable() const {
      return m_symtab;
   }

   /** Returns the module symbol table.
      This holds the global variables that are available at module level.
      \return the global symbol table.
   */
   SymbolTable &symbolTable() {
      return m_symtab;
   }


   /** Returns the symbol list (const version).
      The symbol list is a vector which contains the list of all the symbols in
      the module (including local, static, private, parameters and the like).
      Each Symbol contains an id that backreferences the position in the module
      symbol list so that

         symbols()[i]->id() == symbols[i];
      \return a reference to the symbol vector.
   */
   const SymbolVector &symbols() const {
      return m_symbols;
   }

   SymbolVector &symbols() {
      return m_symbols;
   }

   const DependTable &dependencies() const {
      return m_depend;
   }

   /** Add a dependency to an external module.
      \param name The name of the module from which this module depends.
      \param bPrivate if true import the module only locally.
      \param bIsFilename if true the load name is a filename.
   */
   void addDepend( const String &name, bool bPrivate = false, bool bIsFilename = false  );

   /** Add a dependency to an external module.
      \param alias The local alias under which the module is known.
      \param module The name of the module from which this module depends.
      \param bPrivate if true import the module only locally.
      \param bIsFilename if true the load name is a filename.
   */
   void addDepend( const String &alias, const String &module, bool bPrivate=false, bool bIsFilename = false );


   /** Adds a generic symbol to the module */
   void addSymbol( Symbol *sym );

   /** Shortcut to create a symbol.
      Creates a symbol and adds it to the module symbol vector.
      It is not added to the global symbol table, though. For that, use
      addGlobalSymbol().
      \note this function allows the creation of symbols having the same
         name of already existing  ones (useful i.e. for local/params).

      \param name the name of the symbol that must be created.
      \return the newly created symbol
   */
   Symbol *addSymbol( const String &name );

   Symbol *addExternalRef( const String &name ) {
      return addGlobalSymbol( addSymbol( name ) );
   }

   /** Shortcut to create a symbol as an external function.

      Binary modules may use this as a shortcut to create external
      function definitions.

      The function will also store the symbol int the global symbol
      table, and eventually add the string to the module string table
      if needed. In case the symbol is already used, it will return 0,
      else it will return the newly created symbol.

      If the symbol has to be set as a class method, or otherwise used
      internally by the module, then set the \b exp parameter to false,
      so that the symbol is not exported in the VM, sparing link time
      and memory.

      \param name the name of the symbol that must be created.
      \param func a function pointer to the external function.
      \param exp true if the symbol myst be exported (true by default)
      \return the newly created symbol
   */
   Symbol *addExtFunc( const String &name, ext_func_t func, bool exp = true )
   {
      return addExtFunc( name, func, 0, exp );
   }

   /** Shortcut to create a symbol as an external function.

      This also sets extra data for the callable symbol.
      \see ExtFuncDef

      \param name the name of the symbol that must be created.
      \param func a function pointer to the external function.
      \param extra extra data for this function definition.
      \param exp true if the symbol myst be exported (true by default)
      \return the newly created symbol
   */
   Symbol *addExtFunc( const String &name, ext_func_t func, void *extra, bool exp = true );


   /** Shortcut to create a symbol and store it in the module symbol list.
      \return a newly allocated symbol which is already in the symbol list.
   */
   Symbol *addSymbol();


   /** Adds a symbol to the global symbol table.
      The function checks if the symbol is already in the module table;
      if not it adds it to the module table and sets the proper id.

      \return A pointer to the symbol.
   */
   Symbol *addGlobalSymbol( Symbol *sym );


   /** Adds a global variable symbol to the module
      @param name the name of the symbol
      @param exp true if the symbol is exported, false otherwise
      @return the newly created symbol
   */
   Symbol *addGlobal( const String &name, bool exp=true );

   /** Shortcut to add class properties that are to be considered variables.
      This method takes care to create the module strings needed to represent
      the class properties.
      \note The Symbol passed to this method must be
            one of the symbols held by this module.
      \param cls the symbol holding the ClassDef that must be operated on.
      \param prop the property name.
      \return the newly added VarDef; it can be used to set default values or vardef properties.
   */
   VarDef& addClassProperty( Symbol *cls, const String &prop );

   /** Adds methods to ClassDefs in symbols held by this module.

      This method takes care to create the module strings needed to represent
      the class properties.

      \note The Symbols passed to this method must be held by this module.
      \param cls the symbol holding the ClassDef that must be operated on
      \param prop the property name
      \param method the symbol that will be used as a method
      \return the newly added VarDef; it can be used to set default values or vardef properties.
   */
   VarDef& addClassMethod( Symbol *cls, const String &prop, Symbol *method );

   /** Shortcut to add external function methods to Classes in this module.

      This is a shortcut that creates a non-exported symbol represented by
      the class name, a dot, and the property name. This symbol is created
      in the module as non-exported, and then is fed into
      addClassMethod( Symbol *, const String, Symbol * ).

      \note The Symbols passed to this method must be held by this module.
      \param cls the symbol holding the ClassDef that must be operated on
      \param prop the property name
      \param func the function that will be used as a method
      \return the newly added VarDef; it can be used to set default values or vardef properties.
   */
   VarDef& addClassMethod( Symbol *cls, const String &prop, ext_func_t func );


   /** Adds an internal function to the module.
      As the functions and other callable are usually created during module load
      and added directly to the symbol table, this method serves only as a
      shortcut to define pseudo-functions. The most notable of them is the
      "__main__" function that starts at module entry point and never accepts
      parameters or locals

      The name parameter will be copied in a new string ar stored in the module string
      table. If the string already exists, that instance will be used as the symbol name.

      @param name the name of the symbol
      @param code the code of the function to be executed. The data will be destroyed
         with memFree at symbol destruction.
      @param size size of the function code in bytes.
      @param exp true if the symbol is to be exported
      @return a pointer to the created symbol
   */
    Symbol *addFunction( const String &name, byte *code, uint32 size, bool exp=true );

   /** Adds a class definition to this module.
      This method creates a class which is pertinent to this modules and
      return the corresponding class symbol. The symbol may then be used
      to add properties to the class; every property must be a valid symbol
      that is already inserted in the same module of the class (at the moment,
      in future it may also be a symbol from another module).

    * @param name The name of the class
    * @param ctor_sym An optional symbol that is registered in the same module acting as a
                      constructor (pass zero if no constructor is needed).
    * @param exp true if the symbol is to be exported
    * @return The created class
    */
   Symbol *addClass( const String &name, Symbol *ctor_sym, bool exp=true );

   /** Adds a class definition with an external constructor to this module.
      This is a shortcut that creates a method in the module by getting the
      class name and adding a "._init". The created symbol is not exported, and
      is set as class constructor by calling addClass( name, Symbo *, bool ).

    * @param name The name of the class
    * @param ctor the external function that will be used as constructor.
    * @param exp true if the symbol is to be exported
    * @return The created class
    */
   Symbol *addClass( const String &name, ext_func_t ctor, bool exp=true );

   /** Adds a class definition without a constructor.

    * @param name The name of the class
    * @param exp true if the symbol is to be exported
    * @return The created class
    */
   Symbol *addClass( const String &name, bool exp=true ) {
      return addClass( name, static_cast<Symbol *>( 0 ), exp );
   }

   /** Adds a singleton object to this module.
      This method creates a singleton object, that is a private class which
      is instantiated at link time in an unique, eventually exported object.
      The given name is the name by which the object will be known; the private
      class is given the special name "%[object name]", where "[object name]" is
      the \b name parameter.

      If both the name and the "%" prefixed name are currently unassigned, a new
      symbol containing the singletone instance declaration is returned. On failure,
      0 is returned.

      The class giving form to this singleton can be retrieved using the Symbol::instanceOf()
      method. That symbol can then be used to add methods and properties to the singleton, or
      to derive the private class from other classes.

      \param name the name of the singleton to be created
      \param ctor_sym the constructor of the base class, or 0 for none
      \param exp true to export the singleton. The class is always private; to export it
         get it with Symbol::instanceOf() and export it with Symbol::exported().
      \return the symbol of the singleton object on success, zero on failure.
   */
   Symbol *addSingleton( const String &name, Symbol *ctor_sym=0, bool exp = true );

   /** Adds a singleton object to this module.
      This is like addSingleton( const String &, Symbol *, bool ), but this version
      of the function takes an external function as a constructor and assigns it a name
      that coresponds to the class name followed by "._init". If the constructor name,
      the class name or the instance name are not free, the method returns false.

      \param name the name of the singleton to be created
      \param ctor the constructor of the base class; cannot be zero (use the other version instead)
      \param exp true to export the singleton. The class is always private; to export it
         get it with Symbol::instanceOf() and export it with Symbol::exported().
      \return the symbol of the singleton object on success, zero on failure.
   */
   Symbol *addSingleton( const String &name, ext_func_t ctor, bool exp = true );

   /** Returns the line number that generated a certain code portion.
      Given a Program Counter index, this funtion returns the code
      line that originally generated the part of the code to which the
      Program Counter is pointing to, if this information is available.

      The function will return zero if the information cannot be retreived
      (i.e. be cause debug informations have been stripped).
      \param pc a code position for which the original code line must be discovered.
      \return the code line that generated the coed at PC position, or 0 if line
             info are missing.
   */
   uint32 getLineAt( uint32 pc ) const;

   /** Adds a line information to the module.
      The line informations are used in debug and error reporting to provide
      the users with the line number of the source falcon script that generated
      a certain code portion in the bytecode.

      The the assembler calls this function each time a new line generates new code.

      \param pc the current code position.
      \param line the line that generated the code starting at PC.
   */
   void addLineInfo( uint32 pc, uint32 line );

   /** Adds pre-compiled line informations to the module.
      The line informations are used in debug and error reporting to provide
      the users with the line number of the source falcon script that generated
      a certain code portion in the bytecode.

      Generators knowing the line informations (i.e. the code generator) can
      create a ready-to-use line information map, that can then be inserted
      into the same module where the target code will find place.

      The given line map is then owned by the module, and it will destroyed
      at module destruction; so the creator must give off its ownership.

      If the module already had some line infrmations, they are destroyed.

      \param infos a map containing line informations relative to the code
               used in this module.
   */
   void setLineInfo( LineMap *infos );

   /** Access the DllLoader attached with this module.
      The internal dll loader is created the first time this
      mehtod gets called. This is to avoid application-only
      modules to have a Dll loader attached.

      \see m_loader
      \return the dynamic code loader that is used by this module.
   */
   DllLoader &dllLoader();


   /** Write the module to a stream.

      The module is written to a stream (that does not requires seeking
      capabilities, like i.e. stdout). If skipCode
      is true, the code member of the module (m_code) is not written and when
      the operation reaches the point where the code should be written, it skips
      forward of m_codeSize bytes. If skipCode is false (default), the m_code
      member is regularily written.
      The code is usually skipped by the compiler and assembler if they have the
      byte code handy in another place (i.e. stored on a seekable temporary
      stream). In that case, importing the bytecode in the module just to
      serialize it would be a waste.

      The very last things to be written are 4 bytes representing the de-endianized
      code size and then the bytecode itself. This allow a caller that sets
      skipCode=true to write those data right after save() has returned
      successfully.

      \param os an output stream with seek capabilities
      \param skipCode true if the caller doesn't want to save the bytecode.
      \throw std::ostream::failure in case the stream reports a write error
   */
   virtual bool save( Stream *os, bool skipCode=false ) const;

   /** Reads the module from a stream.
      This method may only load a Falcon binary module; it has no capability to compile
      a source module or to load a native module in DLL or shared object format.

      This method is used as a convenience by the module loader or by other agents when they
      discover that the module is a native Falcon module.

      For this reason, the 4 bytes containing the Falcon module intestation should be skipped
      (read) by the caller \b before calling this method. In case they are not, i.e. because
      the caller knows the module being right without reading the header, then it should
      call this function with skipHeader set to false.

      The skipHeader parameter only refers to the first 4 bytes of the module file, which
      contain a signature, the version and the subversion of the module; the whole header
      may be much longer and depends on the module version.

      \param is an input stream
      \param skipHeader wheter to skip or read the first 4 module bytes.
   */
   virtual bool load( Stream *is, bool skipHeader=true );

   const StringTable &stringTable() const { return *m_strTab; }
   StringTable &stringTable() { return *m_strTab; }

   void adoptStringTable( StringTable *st, bool bOwn = false );

   /** Creates an istance for a certain service.
      Returns 0 if the module does not provide the required service.
      The caller will have to delete the service.
   */
   Service *getService( const String &name ) const;

   /** Publishes a service on the module.
      \param sp the ServiceProvider (function pointer) that will create the service on request.
      \return true if possible, false if the service was already registered.
   */
   bool publishService( Service *sp );

   /** Return a map of the provided services.
      Should be used only in the vm.
   */
   const Map &getServiceMap() const { return m_serviceMap; }

   /** Adds a constant to the module.
      This object will become a constant item in the VM.
      \param exp true if the symbol is to be exported.
      \param value the value that the constant will assume in the vm.
      \param name the name of the symbol in the vm.
   */
   Symbol *addConstant( const String &name, int64 value, bool exp = true );

   /** Adds a constant to the module.
      This object will become a constant item in the VM.
      \param exp true if the symbol is to be exported.
      \param value the value that the constant will assume in the vm.
      \param name the name of the symbol in the vm.
   */
   Symbol *addConstant( const String &name, numeric value, bool exp = true );

   /** Adds a constant to the module.
      This object will become a constant item in the VM.
      \param exp true if the symbol is to be exported.
      \param value the value that the constant will assume in the vm.
      \param name the name of the symbol in the vm.
   */
   Symbol *addConstant( const String &name, const String &value, bool exp = true );

   /** Get the version declared by this module */
   void getModuleVersion( int &major, int &minor, int &revision ) const;

   /** Get the version of the engine libraries under which this module has been compiled. */
   void getEngineVersion( int &major, int &minor, int &revision ) const;

   /** Save internationalization infos on required file.
      Creates an XML file staring with ?xml tag.
      Shouldn't be called if it's table has no international strintgs.
   */
   bool saveTableTemplate( Stream *stream, const String &encoding ) const;

   /** Extract the full logical module name from current module name and parent loader. */
   static String absoluteName( const String &module_name, const String &parent_name );

   /** Adds a module-wide attribute. */
   void addAttribute( const String &name, VarDef* vd );

   /** Returns the module-wide attribute list.
      May be zero if this module has no attributes.
   */
   AttribMap* attributes() const { return m_attributes; }

   /** Unrolls the symbols back to a previous state.
    *    This function removes the symbols added past a size.
    *  This allows to return to a previous state in the module
    *  declaration, that can be simply recored by getting
    *  the size of the symbol table.
    *
    *  For example.
    *  @code
    *  int modSize = module->symbols().size();
    *  try {
    *     // ... do something altering the module
    *  }
    *  catch( Error* e )
    *  {
    *     // roll back
    *     module->rollBackSymbols( modSize );
    *     throw;
    *  }
    *  @endcode
    */
   void rollBackSymbols( uint32 size );
};

}

//====================================================
// Module declaration and service macros
//

// module init and declaration
#define DEFALUT_FALCON_MODULE_INIT falcon_module_init

#define FALCON_MODULE_DECL \
   FALCON_MODULE_TYPE DEFALUT_FALCON_MODULE_INIT()

// Module string table service functions
#ifndef FAL_STR
   #define FAL_STR( id )   vm->moduleString( id )
#endif

#endif

/* end of module.h */
