//@copyright_begin
// ================================================================
// Copyright Notice
// Copyright (C) 1998-2004 by Joe Linoff
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL JOE LINOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 
// Comments and suggestions are always welcome.
// Please report bugs to http://ccdoc.sourceforge.net/ccdoc
// ================================================================
//@copyright_end

// MULTIPLE INCLUSION GUARD
#ifndef ccdoc_database_h
#define ccdoc_database_h

/**
 * This variable allows the header version
 * to be queried at runtime.
 */
namespace {
   char ccdoc_database_h_rcsid[] = "$Id: database.h,v 1.7 2004/09/30 04:16:01 jlinoff Exp $";
}

#include "switches.h"
#include "statement.h"
#include "strmgr.h"
#include <string>
#include <algorithm>

using namespace std;

namespace ccdoc {
  /**
   * The ccdoc database interface.
   * @author Joe Linoff
   * @version $Id: database.h,v 1.7 2004/09/30 04:16:01 jlinoff Exp $
   */
  class database {
  public:
    typedef map<string,statement::base::stmts_t > path_map_type;
    typedef map<string,statement::base::stmts_t >::iterator path_map_itr_type;
  public:
    /**
     * Constructor.
     * The database is loaded during construction. If
     * the load fails, an exception is thrown.
     * @param sw The switches object.
     * @exception ccdoc::exceptions::invalid_database 
     *   Thrown if there is a problem with the db file.
     */
    database(switches& sw);
    /**
     * Destructor.
     * The database is written here.
     */
    ~database();
  public:
    /**
     * Disable writing of the database. By default the
     * database is always written.
     *
     * This is used to disable the writing of the database
     * when the user only specifies phase3 processing because
     * this phase does not change the database.
     */
    void disable_write(){m_write=false;}
  public:
    /**
     * Get the specified statement. If it does not exist,
     * return a NULL.
     * @param path The path to the package. Hierarchy levels
     * are separated by periods '.' or double colons '::'.
     * @returns The statement object or NULL if it was not found.
     */
    statement::base* get_statement(string path);
  public:
    /**
     * Get the specified statement, ignore packages.
     * This is used to access @link and @see references.
     * @param path The path to the package. Hierarchy levels
     * are separated by periods '.' or double colons '::'.
     * @param stmts All of the statements that match the path.
     * @param pkgs_as_last_resort
     *        Use packages as a last resort if no match is found
     *        without packages.
     */
    void get_stmt_no_pkgs(string path,
			  statement::base::stmts_t& stmts,
                          bool pkgs_as_last_resort=true);
  public:
    /**
     * Get the specified package. If it does not exist,
     * create it.
     * @param path The path to the package. Hierarchy levels
     * are separated by periods.
     * @returns The package object or NULL if it was not found.
     */
    statement::base* get_create_package(string path);
  public:
    /**
     * Convert a path to tokens.
     * @param path The path.
     * @param ids The path.
     */
    void parse_path(string path,vector<string>& ids);
  public:
    /**
     * Get the database root record.
     * This record is the starting point for all other records
     * in the database.
     * @returns The root record.
     */
    const statement::base* root() const {return m_root;}
  public:
    /**
     * Remove statements associated with a specific file.
     * Package associations are ignored.
     * @param file The name of the file.
     */
    void remove_file_statements(const char* file);
  private:
    void remove_file_statements(const char* file,
				statement::base* stmt);
  private:
    /**
     * Read in the database.
     */
    void read();
    bool read_verbose(istream&);
    bool read_terse(istream&);
    bool read_string_table(istream&,
			   unsigned& lineno,
			   vector<string>& strings);
    const char* read_verbose_string(istream&,unsigned);
    const char* read_terse_string(istream&,
				  unsigned,
				  vector<string>& strings);
    void read_error(unsigned lineno,
		    const char* expected,
		    const char* found);
    void read_error(unsigned lineno,
		    const char* expected,
		    const string& found);
  private:
    /**
     * Write out the database.
     * @throws ccdoc::exceptions::invalid_database 
     *   Thrown if there is a problem with the db file.
     */
    void write();
    void write(statement::base::stmts_t& vec,
	       statement::base* stmt);
  public:
    /**
     * Get the string manager.
     * @returns The string manager.
     */
    strmgr& get_strmgr() {return m_strmgr;}
  public:
    /**
     * Get the next comment id.
     * @returns The next comment id.
     */
    void get_next_comment_id(string& id);
  public:
    /**
     * Load all statements into the vector.
     * @param vec The vector.
     * @param t The type.
     */
    void load(statement::base::stmts_t& vec);
  private:
    void load(statement::base::stmts_t& vec,
	      statement::base*);
  public:
    /**
     * Load all statements of the specified type
     * into the vector.
     * @param vec The vector.
     * @param t The type.
     */
    void load(statement::base::stmts_t& vec,
	      statement::base::TYPE t);
  private:
    void load(statement::base::stmts_t& vec,
	      statement::base::TYPE t,
	      statement::base*);
  public:
    /**
     * Load all statements of the specified type
     * into the vector if their parent is a
     * namespace or a package.
     * @param vec The vector.
     * @param t The type.
     */
    void load_top(statement::base::stmts_t& vec,
		  statement::base::TYPE t);
  private:
    void load_top(statement::base::stmts_t& vec,
		  statement::base::TYPE t,
		  statement::base*);
  public:
    /**
     * This method was added as part of the resolution of issue 0039.
     * It populates a map of the path names that is used for
     * fast O( N log(N) ) access in
     *@link ccdoc::database::get_stmt_no_pkgs get_stmt_no_pkgs
     *.
     * It is used in phase2 and phase3 processing.
     */
    void load_path_map();
    /**
     * This method was added as part of the resolution of issue 0068.
     * @param stmt Erase this statement.
     */
    void erase_from_path_map(statement::base* stmt);
    /**
     * This method was added as part of the resolution of issue 0039.
     * It clears out the map created by load_path_map.
     * It is only used in phase2 processing.
     */
    void clear_path_map();
  private:
    void load_path_map(statement::base*);
  public:
    bool get_debug() const {return m_debug;}
    void set_debug(bool f) {m_debug=f;}
  public:
    /**
     * Debug dump.
     * Output the contents of this object for test purposes.
     * @param prefix The optional prefix.
     */
    void debug_dump(const char* prefix) const;
  private:
    void debug_dump(const char* prefix,statement::base* stmt) const;
  private:
    statement::base* m_root;
    statement::base::stmts_t m_top_file_stmts;
    switches& m_sw;
    strmgr m_strmgr;
    path_map_type m_path_map;
    unsigned m_comment_id;
    bool m_debug;
    bool m_write;
  };
};

#endif
