#ifndef __CDL_HXX
# define __CDL_HXX
//{{{  Banner                                   

//============================================================================
//
//      cdl.hxx
//
//      This header file declares the classes related to software
//      configuration.
//
//============================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// ----------------------------------------------------------------------------
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
//
// This file is part of the eCos host tools.
//
// 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., 
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// ----------------------------------------------------------------------------
//                                                                          
//####COPYRIGHTEND####
//============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):   bartv
// Contact(s):  bartv
// Date:        1998/02/09
// Version:     0.02
// Requires:    cdlcore.hxx
// Usage:       #include <cdl.hxx>
//
//####DESCRIPTIONEND####
//============================================================================

//}}}
//{{{  nested #include's                        

// ----------------------------------------------------------------------------
// Software CDL depends on the core but adds no new system requirements.

#ifndef __CDLCORE_HXX
# include <cdlcore.hxx>
#endif

//}}}

//{{{  Forward declarations of the body classes 

// ----------------------------------------------------------------------------
// This section provides forward declarations of the main classes used
// for software configuration.

class CdlConfigurationBody;
class CdlPackageBody;
class CdlComponentBody;
class CdlOptionBody;
class CdlPackagesDatabaseBody;

typedef CdlConfigurationBody*           CdlConfiguration;
typedef CdlPackageBody*                 CdlPackage;
typedef CdlComponentBody*               CdlComponent;
typedef CdlOptionBody*                  CdlOption;
typedef CdlPackagesDatabaseBody*        CdlPackagesDatabase;

typedef const CdlConfigurationBody*     CdlConstConfiguration;
typedef const CdlPackageBody*           CdlConstPackage;
typedef const CdlComponentBody*         CdlConstComponent;
typedef const CdlOptionBody*            CdlConstOption;
typedef const CdlPackagesDatabaseBody*  CdlConstPackagesDatabase;

//}}}
//{{{  CdlPackagesDatabase class                

// ----------------------------------------------------------------------------
// An eCos component repository can get to be quite complicated. There will
// be a number of core packages supplied by Red Hat. There may also be some
// number of third party and unsupported packages. Each package may come in
// several different versions. Keeping track of everything that has been
// installed should involve a separate administration tool, and there should
// be some sort of database of all this information.
//
// At the time of writing there is no such administration tool and there is
// no database with details of the various packages. Instead there is a
// static file "packages" at the top level of the component repository,
// containing some of the desired information.
//
// For now a temporary CdlPackagesDatabase class is provided.
// Essentially this provides C++ access to the packages file. In the
// long term this class will be replaced completely by a full and more
// rational implementation.
//
// The packages database class also, temporarily, provides information about
// targets and templates. This will change in future. A target will be specified
// by a save file, the output from Hardy. A template will also be specified by
// a save file, a partial software configuration.

class CdlPackagesDatabaseBody {

    friend class CdlTest;
    friend class CdlDbParser;
    
  public:

    static CdlPackagesDatabase  make(std::string = "") throw(CdlInputOutputException,std::bad_alloc);
    bool                        update(void) throw(CdlInputOutputException,std::bad_alloc);
    ~CdlPackagesDatabaseBody();

    std::string                         get_component_repository() const;
    
    const std::vector<std::string>&     get_packages(void) const;
    bool                                is_known_package(std::string) const;
    const std::string&                  get_package_description(std::string) const;
    const std::vector<std::string>&     get_package_aliases(std::string) const;
    const std::vector<std::string>&     get_package_versions(std::string) const;
    const std::string&                  get_package_directory(std::string) const;
    const std::string&                  get_package_script(std::string) const;
    bool                                is_hardware_package(std::string) const;

    const std::vector<std::string>&     get_targets(void) const;
    bool                                is_known_target(std::string) const;
    const std::string&                  get_target_description(std::string) const;
    const std::vector<std::string>&     get_target_aliases(std::string) const;
    const std::vector<std::string>&     get_target_packages(std::string) const;
    const std::string&                  get_target_command_prefix(std::string) const;
    const std::vector<std::pair<std::string,std::string> >& get_target_compiler_flags(std::string) const;
    const std::vector<std::string>&     get_target_enables(std::string) const;
    const std::vector<std::string>&     get_target_disables(std::string) const;
    const std::vector<std::pair<std::string,std::string> >& get_target_set_values(std::string) const;
    
    const std::vector<std::string>&     get_templates(void) const;
    bool                                is_known_template(std::string) const;
    std::string                         get_template_filename(std::string, std::string = "") const;
    const std::vector<std::string>&     get_template_versions(std::string) const;
    const std::string                   get_template_description(std::string, std::string = "");
    const std::vector<std::string>&     get_template_packages(std::string, std::string = "");
    static void                         extract_template_details(std::string /* filename */, std::string& /* description */,
                                                                 std::vector<std::string>& /* packages */);
    
    // What are the valid compiler flag variables (ARCHFLAGS, ERRFLAGS, ...)?
    // For now the library provides a static vector of these things, but
    // this area is likely to change in future
    static const std::vector<std::string>& get_valid_cflags();
    
    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
    CYGDBG_DECLARE_MEMLEAK_COUNTER();
    
  private:
    // The only valid constructor gets invoked from the make() member function.
    // The argument should be a pathname for the component repository. The
    // constructor is responsible for reading in the whole packages file.
    CdlPackagesDatabaseBody(std::string) throw(CdlInputOutputException,std::bad_alloc);

    std::string                         component_repository;
    std::vector<std::string>            package_names;
    struct package_data {
      public:
        std::string                     description;
        std::vector<std::string>        aliases;
        std::vector<std::string>        versions;
        std::string                     directory;
        std::string                     script;
        bool                            hardware;
    };
    std::map<std::string,struct package_data> packages;
    
    std::vector<std::string>            target_names;
    struct target_data {
      public:
        std::string                     description;
        std::vector<std::string>        aliases;
        std::vector<std::string>        packages;
        std::string                     command_prefix;
        std::vector<std::pair<std::string,std::string> > cflags;
        std::vector<std::string>        enable;
        std::vector<std::string>        disable;
        std::vector<std::pair<std::string, std::string> > set_values;
    };
    std::map<std::string, struct target_data>    targets;
    
    std::vector<std::string>            template_names;
    struct template_version_data {
      public:
        std::string                     description;
        std::vector<std::string>        packages;
    };
    struct template_data {
      public:
        std::vector<std::string>        versions;
        std::map<std::string, struct template_version_data> version_details;
    };
    std::map<std::string, struct template_data>   templates;
    
    enum {
        CdlPackagesDatabaseBody_Invalid = 0,
        CdlPackagesDatabaseBody_Magic   = 0x50896acb
    } cdlpackagesdatabasebody_cookie;

    // This allows test cases to overwrite the name of the file
    // containing the database information.
    static char* database_name;
    
    // The default constructor, copy constructor and assignment operator are illegal.
    CdlPackagesDatabaseBody();
    CdlPackagesDatabaseBody(const CdlPackagesDatabaseBody&);
    CdlPackagesDatabaseBody& operator=(const CdlPackagesDatabaseBody&);
    
};

//}}}
//{{{  CdlConfiguration class                   

// ----------------------------------------------------------------------------
// The CdlConfiguration class is the toplevel used for mainpulating
// software configurations. It consists of a number of loaded packages,
// each of which consists of some hierarchy of components and options,
// plus dialogs, wizards, and interfaces from the core.
//
// Typically an application will deal with only one configuration at a
// time. There will be exceptions. The most obvious example would be
// some sort of diff utility, but there may well be times when a user
// wants to edit multiple configurations. One example would be a board
// containing two separate processors, e.g. a conventional one coupled
// with a separate DSP, and eCos is supposed to run on both: the two
// chips will need to interact, and hence there may well be
// configurability dependencies between them.
//
// A configuration object does not exist in isolation. It must be tied
// to an eCos component repository via a database objects. It must
// also be supplied with a suitable Tcl interpreter.

class CdlConfigurationBody : public virtual CdlToplevelBody
{
    friend class CdlTest;

  public:
    
    // ----------------------------------------------------------------------------
    // Create a new configuration.
    // Currently this requires a name, a database and a master interpreter.
    // The name is not used very much, but does appear in savefiles.
    // The packages database and the interpreter must be created before
    // any configuration object.
    static CdlConfiguration     make(std::string /* name */, CdlPackagesDatabase, CdlInterpreter);
    CdlPackagesDatabase         get_database() const;

    // Loading and unloading packages. This can happen in a number
    // of different ways:
    //
    // 1) explicitly adding or removing a single package
    // 2) changing the version of a package, which means unloading
    //    the old version and reloading the new one. Generally
    //    user settings should be preserved where possible.
    // 3) loading in a full or minimal savefile. The library
    //    does not actually distinguish between the two when
    //    loading, only when saving.
    // 4) adding a full or minimal savefile.
    // 5) setting a template for the first time. This is much the
    //    same as adding a minimal savefile. However the library
    //    keeps track of the "current" template and only one
    //    can be loaded at a time. The library does not keep
    //    track of which savefile(s) have been added.
    // 6) changing a template. This means unloading the packages
    //    that were loaded for the old template, then loading in
    //    the new one.
    // 7) unsetting the template. This just involves an unload.
    // 8) setting the hardware. Currently the database defines
    //    each supported target, listing the packages that should
    //    be loaded and possibly some option details. This is
    //    subject to change in future.
    // 9) changing the hardware. This is much the same as changing
    //    the template.
    // 10) unsetting the hardware.
    //
    // The unload operations are comparatively safe, although
    // they can result in new conflicts and the user may well want
    // to cancel the operation after discovering how many new
    // problems there would be. The load operations are a bit more
    // dangerous since they depend on external data and hence can
    // fail for a variety of reasons: missing files, corrupt files,
    // clashes with existing data, ... Changing e.g. a template
    // is especially dangerous because of the number of things
    // that can go wrong. All of these operations need to happen
    // in a transaction which the user can cancel. There are two
    // versions of all the relevant routines, one which operates
    // in an existing transaction and one which creates a new
    // one.
    //
    // Any operation that involves loading CDL data takes two
    // CdlDiagnosticFnPtr arguments, one for errors and one for
    // warnings. These should report the message to the user by
    // some suitable means. The error fnptr may throw a
    // CdlParseException to abort the current load immediately,
    // otherwise the load operation will be aborted at the end
    // if any errors were detected. New conflicts are not
    // handled by these diagnostic functions, instead they
    // are handled by the normal transaction methods.

    // A version argument of "" implies the most recent version.
    void load_package(std::string /* name */, std::string /* version */,
                      CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void load_package(CdlTransaction, std::string /* name */, std::string /* version */,
                      CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);

    void unload_package(std::string /* name */, bool /* limbo */ = true);
    void unload_package(CdlPackage, bool /* limbo */ = true);
    void unload_package(CdlTransaction, std::string /* name */, bool /* limbo */ = true);
    void unload_package(CdlTransaction, CdlPackage, bool /* limbo */ = true);
    
    void change_package_version(std::string /*name*/, std::string /*version*/,
                                CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void change_package_version(CdlPackage, std::string /*version*/, CdlDiagnosticFnPtr /* error */,
                                CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);

    void change_package_version(CdlTransaction, std::string /*name*/, std::string /*version*/,
                                CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void change_package_version(CdlTransaction, CdlPackage, std::string /*version*/, CdlDiagnosticFnPtr /* error */,
                                CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);

    // Loading a savefile is different in that it creates a new
    // toplevel. Since transactions can only be created if the
    // toplevel already exists, it is not possible to have a
    // per-transaction load() operation. It is possible to have
    // a per-transaction add() operation.
    static CdlConfiguration load(std::string /* filename */, CdlPackagesDatabase, CdlInterpreter,
                                 CdlDiagnosticFnPtr /* error */,  CdlDiagnosticFnPtr /* warn */)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);

    void add(std::string /* filename */,
             CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void add(CdlTransaction, std::string /* filename */,
             CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);

    // As with packages, a version of "" implies the most recent.
    void set_template(std::string, std::string /* version */,
                      CdlDiagnosticFnPtr, CdlDiagnosticFnPtr /* warn */,bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void set_template_file(std::string,
                           CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void set_template(CdlTransaction, std::string, std::string /* version */,
                      CdlDiagnosticFnPtr, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void set_template_file(CdlTransaction, std::string,
                           CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void unload_template(bool /* limbo */ = true);
    void unload_template(CdlTransaction, bool /* limbo */ = true);
    std::string get_template() const;
    void        set_template_name(std::string); // Intended for library use only

    void set_hardware(std::string,
                      CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void set_hardware(CdlTransaction, std::string,
                      CdlDiagnosticFnPtr /* error */, CdlDiagnosticFnPtr /* warn */, bool /* limbo */ = true)
        throw(CdlInputOutputException, CdlParseException, std::bad_alloc);
    void unload_hardware(bool /* limbo */ = true);
    void unload_hardware(CdlTransaction, bool /* limbo */ = true);
    std::string get_hardware() const;
    void        set_hardware_name(std::string); // Intended for library use only
    
    // ----------------------------------------------------------------------------
    // Save a configuration to a file
    void        save(std::string, bool /* minimal */ = false) throw(CdlInputOutputException,std::bad_alloc);
    void        initialize_savefile_support();
    std::string get_save_file() const;

    // ----------------------------------------------------------------------------
    // Get rid of a configuration.
    ~CdlConfigurationBody();

    virtual std::string         get_class_name() const;
    bool                        check_this(cyg_assert_class_zeal = cyg_quick) const;
    CYGDBG_DECLARE_MEMLEAK_COUNTER();
    
  private:

    // The only legal constructor, invoked from make() and load()
    CdlConfigurationBody(std::string, CdlPackagesDatabase, CdlInterpreter);

    // The internal implementation of the persistence support
    virtual void                save(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc);
    static int                  savefile_configuration_command(CdlInterpreter, int, char**);
    static int                  savefile_description_command(CdlInterpreter, int, char**);
    static int                  savefile_hardware_command(CdlInterpreter, int, char**);
    static int                  savefile_template_command(CdlInterpreter, int, char**);
    static int                  savefile_package_command(CdlInterpreter, int, char**);
    
    std::string                 current_hardware;
    std::string                 current_template;
    std::string                 description;
    CdlPackagesDatabase         database;
    std::string                 save_file;
    enum {
        CdlConfigurationBody_Invalid    = 0,
        CdlConfigurationBody_Magic      = 0x5c409a3d
    } cdlconfigurationbody_cookie;

    // The constructor can only be invoked via the make() and load()
    // members. Other constructors and the assignment operator are
    // illegal.
    CdlConfigurationBody();
    CdlConfigurationBody(const CdlConfigurationBody&);
    CdlConfigurationBody& operator=(const CdlConfigurationBody&);
    
};

//}}}
//{{{  CdlPackage class                         

// ----------------------------------------------------------------------------
// Packages inherit from most of the base classes.

class CdlPackageBody : public virtual CdlNodeBody,
                       public virtual CdlContainerBody,
                       public virtual CdlUserVisibleBody,
                       public virtual CdlValuableBody,
                       public virtual CdlParentableBody,
                       public virtual CdlBuildableBody,
                       public virtual CdlDefinableBody,
                       public virtual CdlLoadableBody,
                       public virtual CdlBuildLoadableBody,
                       public virtual CdlDefineLoadableBody
{                       
    friend class CdlTest;

    // Packages should not be created by application code, but
    // the CdlConfiguration class must be able to do so inside
    // load_package();
    friend class CdlConfigurationBody;
    
  public:

    ~CdlPackageBody();
    
    static int          parse_package(CdlInterpreter, int, char**);
    static int          parse_hardware(CdlInterpreter, int, char**);
    static int          parse_install_proc(CdlInterpreter, int, char**);
    static int          parse_license_proc(CdlInterpreter, int, char**);

    // Override the CdlDefineLoadable member. Hardware packages always
    // send their configuration options to hardware.h
    virtual std::string get_config_header() const;
    
    bool                is_hardware_package() const;
    bool                has_install_proc() const;
    const cdl_tcl_code& get_install_proc() const;
    bool                has_license_proc() const;
    const cdl_tcl_code& get_license_proc() const;

    // Propagation support. Because of multiple virtual inheritance
    // it is necessary to invoke the container and valuable
    // update members.
    virtual void update(CdlTransaction, CdlUpdate);
    
    // Persistence support.
    virtual void        save(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc);
    static void         initialize_savefile_support(CdlToplevel);
    static int          savefile_package_command(CdlInterpreter, int, char**);

    // Was this package loaded because of a template or hardware setting?
    bool                belongs_to_template() const;
    bool                belongs_to_hardware() const;
    
    virtual std::string get_class_name() const;
    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
    CYGDBG_DECLARE_MEMLEAK_COUNTER();
    
  private:

    // The only valid constructor requires a number of fields
    CdlPackageBody(std::string /* name */, CdlConfiguration, std::string /* directory */);

    // Other constructors are illegal
    CdlPackageBody();
    CdlPackageBody(const CdlPackageBody&);
    CdlPackageBody& operator=(const CdlPackageBody&);

    bool loaded_for_template;
    bool loaded_for_hardware;
    
    enum {
        CdlPackageBody_Invalid  = 0,
        CdlPackageBody_Magic    = 0x1d7c0d43
    } cdlpackagebody_cookie;
};

//}}}
//{{{  CdlComponent class                       

// ----------------------------------------------------------------------------
// Similarly components just inherit from the appropriate base classes.

class CdlComponentBody : public virtual CdlNodeBody,
                         public virtual CdlContainerBody,
                         public virtual CdlUserVisibleBody,
                         public virtual CdlValuableBody,
                         public virtual CdlParentableBody,
                         public virtual CdlBuildableBody,
                         public virtual CdlDefinableBody
{
    friend class CdlTest;

  public:

    ~CdlComponentBody();
    static int          parse_component(CdlInterpreter, int, char**);
    static int          parse_script(CdlInterpreter, int, char**);

    // Propagation support. Because of multiple virtual inheritance
    // it is necessary to invoke the container and valuable
    // update members.
    virtual void update(CdlTransaction, CdlUpdate);
    
    // Persistence support.
    virtual void        save(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc);
    static void         initialize_savefile_support(CdlToplevel);
    static int          savefile_component_command(CdlInterpreter, int, char**);
    
    virtual std::string get_class_name() const;
    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
    CYGDBG_DECLARE_MEMLEAK_COUNTER();
    
  private:

    // The only valid constructor requires a name.
    CdlComponentBody(std::string);
    
    enum {
        CdlComponentBody_Invalid  = 0,
        CdlComponentBody_Magic    = 0x6359d9a7
    } cdlcomponentbody_cookie;
    
    // Other constructors are illegal
    CdlComponentBody();
    CdlComponentBody(const CdlComponentBody&);
    CdlComponentBody& operator=(const CdlComponentBody&);
};

//}}}
//{{{  CdlOption class                          

// ----------------------------------------------------------------------------
// Again options just inherit their functionality from the base classes.

class CdlOptionBody : public virtual CdlNodeBody,
                      public virtual CdlUserVisibleBody,
                      public virtual CdlValuableBody,
                      public virtual CdlParentableBody,
                      public virtual CdlBuildableBody,
                      public virtual CdlDefinableBody
{
    friend class CdlTest;
    
  public:
    ~CdlOptionBody();
    
    static int          parse_option(CdlInterpreter, int, char**);
    
    // Persistence support.
    virtual void        save(CdlInterpreter, Tcl_Channel, int, bool) throw(CdlInputOutputException, std::bad_alloc);
    static void         initialize_savefile_support(CdlToplevel);
    static int          savefile_option_command(CdlInterpreter, int, char**);
    
    virtual std::string get_class_name() const;
    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
    CYGDBG_DECLARE_MEMLEAK_COUNTER();

  private:
    CdlOptionBody(std::string);

    enum {
        CdlOptionBody_Invalid   = 0,
        CdlOptionBody_Magic     = 0x1c1162d1
    } cdloptionbody_cookie;
    
    CdlOptionBody();
    CdlOptionBody(const CdlOptionBody&);
    CdlOptionBody& operator=(const CdlOptionBody&);
};

//}}}

#endif  /* !__CDL_HXX */
// EOF cdl.hxx
