//-----------------------------------------------------------
// Copyright Christian Arnault LAL-Orsay CNRS
// arnault@lal.in2p3.fr
// Modified by garonne@lal.in2p3.fr
// Modified by Grigory Rybkin
// See the complete license in cmt_license.txt "http://www.cecill.info". 
//-----------------------------------------------------------

#ifndef __cmt_project_h__
#define __cmt_project_h__

#include "cmt_parser.h"
#include "cmt_system.h"
#include "cmt_use.h"
#include "cmt_awk.h"

class Project;

class StrategyDef
{
public:
  typedef cmt_vector <StrategyDef*> StrategyDefs;

  cmt_string m_keyword;
  cmt_string m_name;
  cmt_string m_on_value;
  cmt_string m_off_value;
  bool m_default_value;
  bool m_priority_value;

  const cmt_string& get_default_value () const; 
};

class Strategy
{
public:
  StrategyDef* m_definition;
  bool m_specified;
  bool m_specified_value;
  bool m_value;
  Tag* m_on_tag;
  Tag* m_off_tag;
  cmt_string m_context;

  Strategy ();
  void clear ();
  void set (StrategyDef* definition, bool value, const cmt_string& project_name);
  void update (StrategyDef* definition, bool value, const cmt_string& project_name);
};

class StrategyMgr
{
public:
  static StrategyMgr& instance ();
  static StrategyDef* find_strategy (const cmt_string& name);
  static bool get_default_strategy (const cmt_string& name);
  static bool get_priority_strategy (const cmt_string& name);
  static StrategyDef::StrategyDefs& get_definitions ();

private:
  StrategyMgr ();
  StrategyDef::StrategyDefs m_defs;
};

class ProjectFactory : public IProjectFactory
{
public:
  static IProjectFactory& instance ();

public:
  void reset ();
  Project* create_project (const cmt_string& name,
			   const cmt_string& path,
			   const cmt_string& path_source,
			   Project* parent);

};

class IProjectAction
{
public:
  virtual bool run (const Project& project) = 0;
};

class IProjectVisitor
{
public:
  virtual void pre (Project* p) = 0;
  virtual void in (Project* p) = 0;
  virtual void in_again (Project* p) = 0;
  virtual void post (Project* p) = 0;
};

class Project
{
public:

  typedef cmt_vector<Project> ProjectVector;
  typedef cmt_vector<Project*> ProjectPtrVector;
  typedef cmt_vector<const Project*> ConstProjectPtrVector;

  static bool create (const cmt_string& name, 
		      const cmt_string& release, 
		      const cmt_string& path);

  static Project* find_by_name (const cmt_string& name);
  static Project* find_by_cmtpath (const cmt_string& cmtpath);
  static Project* get_current ();
  static Project* add (const cmt_string& name,
		       const cmt_string& release);

  static ProjectVector& projects ();
  static ProjectPtrVector& ordered_projects ();
  static void order_all ();
  static void clear_all ();
  static void show_all (PrintMode mode = Csh); // Csh is default for ArgParser::mode
  //  static void show_all ();
  static void show_container (const cmt_string& path = "");
  static void show_specified_strategies_for_all ();
  static void show_paths (const CmtSystem::cmt_string_vector& arguments, ostream& out = cout);
  //  static void show_paths ();
  static const cmt_string& get_project_file_name ();

  static void fill_selection (int depth, ConstProjectPtrVector& path_selections);
  //  static void fill_selection (int depth, CmtSystem::cmt_string_vector& path_selections);
  static void fill_exclusion (ConstProjectPtrVector& path_exclusions);

  static void broadcast (IProjectAction& action);
  static void reverse_broadcast (IProjectAction& action);
  static void scan_paths (PathScanner& scanner, PathScanner::actor& actor);
  static void scan_paths_for_package (PathScanner& scanner, const cmt_string& name);

  static cmt_string find_in_cmt_paths (const cmt_string& path, bool realpath = false);

  static void fill_cmtpaths (cmt_string& buffer);

  static void start_visit (IProjectVisitor& visitor);

public:

  Project ();

  const cmt_string& get_name () const;
  const cmt_string& get_release () const;
  const cmt_string& get_container_name () const;
  const cmt_string& get_container_version () const;
  const cmt_string& get_container_path () const;
  const Use& get_container () const;
  const cmt_string& get_cmtpath () const;
  const cmt_string& get_cmtpath_real () const;
  const cmt_string& get_cmtpath_pwd () const;
  const cmt_string& get_cmtpath_source () const;
  int get_children_size () const;
  
  const CmtSystem::cmt_string_vector get_project_releases(const cmt_string& name) const; 
  bool select_release(const cmt_string& name, const cmt_string& release, cmt_string& result);
  
  Project* get_child (int index) const;
  bool visited () const;

  void set_name (const cmt_string& name);
  void set_release (const cmt_string& release);
  //  void set_container (const cmt_string& container);
  void set_container_name (const cmt_string& name);
  void set_container_version (const cmt_string& version);
  void set_container_path (const cmt_string& path);
  void set_cmtpath (const cmt_string& path);
  void set_cmtpath_real (const cmt_string& path);
  void set_cmtpath_pwd (const cmt_string& path);
  void set_cmtpath_source (const cmt_string& source);
  void set_is_current (bool is_current);
  bool is_current () const;
  void clear ();
  void configure ();
  bool has_parents () const;
  bool has_parent (Project* p) const;
  bool has_child (Project* p) const;
  void add_parent (Project* p);
  void add_child (Project* p);
  void erase_child (Project* p);

  void container_action (const CmtSystem::cmt_string_vector& words);
  //  void container_action (const cmt_string& name, const cmt_string& version);
  void use_action (const cmt_string& name, const cmt_string& release);

  //  Project& operator = (const Project& other);
  bool operator == (const cmt_string& name) const;
  bool operator != (const cmt_string& name) const;

  void show (PrintMode mode = Csh); // Csh is default for ArgParser::mode
  //  void show ();
  void show_specified_strategies () const;

  bool has_strategy (const StrategyDef* definition) const;
  bool is_specified (const StrategyDef* definition) const;
  bool get_strategy (const StrategyDef* definition) const;
  bool get_strategy (const cmt_string& name) const;
  void set_default_strategy (const cmt_string& name);
  void set_strategy (const cmt_string& name, const cmt_string& value, const cmt_string& context);
  void set_strategy (StrategyDef* definition, bool b_value, const cmt_string& context);
  void update_strategy (StrategyDef* definition, bool b_value);
  void update_strategy_from_children (StrategyDef* definition);
  void update_strategies_from_children ();
  void set_author (const cmt_string& name);
  const cmt_string& get_author () const;
  void project_author_action (const CmtSystem::cmt_string_vector& words);
  // bool get_author (const cmt_string& name) const;
  
  void   visit (IProjectVisitor& visitor);
  static  void   visit (IProjectVisitor& visitor, ProjectPtrVector& projects);
  static  void   visit (const int offset, int& order, ProjectPtrVector& projects);
  
  // Use
  Use*  get_use () const;  
  void  set_use (Use* use);  
  
  cmt_vector <cmt_string> m_extra_lines;
  
private:

  Project& operator = (const Project& other);
  Project (const Project& other);

  void fill_cmtpath (cmt_string& buffer);

  cmt_string m_name;
  cmt_string m_author;
  cmt_string m_release;
  cmt_string m_container_name;
  cmt_string m_container_version;
  cmt_string m_container_path;
  Use m_container;

  ProjectPtrVector m_parents;
  ProjectPtrVector m_children;

  bool m_visited;
  int m_visits;
  int m_order;
  bool m_is_current;

  cmt_string m_cmtpath;
  cmt_string m_cmtpath_real;
  cmt_string m_cmtpath_pwd;
  cmt_string m_cmtpath_source;

  bool m_configured;

  cmt_vector <Strategy> m_strategies;
  
  Use* m_use;
};


#endif
