// Filename: dcSwitch.h
// Created by:  drose (23Jun04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#ifndef DCSWITCH_H
#define DCSWITCH_H

#include "dcbase.h"
#include "dcDeclaration.h"
#include "dcPackerInterface.h"

class DCParameter;
class HashGenerator;
class DCField;

////////////////////////////////////////////////////////////////////
//       Class : DCSwitch
// Description : This represents a switch statement, which can appear
//               inside a class body and represents two or more
//               alternative unpacking schemes based on the first
//               field read.
////////////////////////////////////////////////////////////////////
class EXPCL_DIRECT DCSwitch : public DCDeclaration {
public:
  DCSwitch(const string &name, DCField *key_parameter);
  virtual ~DCSwitch();

PUBLISHED:
  virtual DCSwitch *as_switch();
  virtual const DCSwitch *as_switch() const;

  const string &get_name() const;
  DCField *get_key_parameter() const;

  int get_num_cases() const;
  int get_case_by_value(const string &case_value) const;
  DCPackerInterface *get_case(int n) const;
  DCPackerInterface *get_default_case() const;

  string get_value(int case_index) const;
  int get_num_fields(int case_index) const;
  DCField *get_field(int case_index, int n) const;
  DCField *get_field_by_name(int case_index, const string &name) const;

public:
  bool is_field_valid() const;
  int add_case(const string &value);
  void add_invalid_case();
  bool add_default();
  bool add_field(DCField *field);
  void add_break();

  const DCPackerInterface *apply_switch(const char *value_data, size_t length) const;

  virtual void output(ostream &out, bool brief) const;
  virtual void write(ostream &out, bool brief, int indent_level) const;
  void output_instance(ostream &out, bool brief, const string &prename, 
                       const string &name, const string &postname) const;
  void write_instance(ostream &out, bool brief, int indent_level,
                      const string &prename, const string &name,
                      const string &postname) const;
  virtual void generate_hash(HashGenerator &hashgen) const;
  virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;

  bool do_check_match_switch(const DCSwitch *other) const;

public:
  typedef pvector<DCField *> Fields;
  typedef pmap<string, DCField *> FieldsByName;

  class SwitchFields : public DCPackerInterface {
  public:
    SwitchFields(const string &name);
    ~SwitchFields();
    virtual DCPackerInterface *get_nested_field(int n) const;

    bool add_field(DCField *field);
    bool do_check_match_switch_case(const SwitchFields *other) const;

    void output(ostream &out, bool brief) const;
    void write(ostream &out, bool brief, int indent_level) const;

  protected:
    virtual bool do_check_match(const DCPackerInterface *other) const;

  public:
    Fields _fields;
    FieldsByName _fields_by_name;
    bool _has_default_value;
  };

  class SwitchCase {
  public:
    SwitchCase(const string &value, SwitchFields *fields);
    ~SwitchCase();

    bool do_check_match_switch_case(const SwitchCase *other) const;

  public:
    string _value;
    SwitchFields *_fields;
  };

private:
  SwitchFields *start_new_case();

private:
  string _name;
  DCField *_key_parameter;

  typedef pvector<SwitchCase *> Cases;
  Cases _cases;
  SwitchFields *_default_case;

  // All SwitchFields created and used by the DCSwitch object are also
  // stored here; this is the vector that "owns" the pointers.
  typedef pvector<SwitchFields *> CaseFields;
  CaseFields _case_fields;

  // All nested DCField objects that have been added to one or more of
  // the above SwitchFields are also recorded here; this is the vector
  // that "owns" these pointers.
  Fields _nested_fields;

  // These are the SwitchFields that are currently being filled up
  // during this stage of the parser.  There might be more than one at
  // a time, if we have multiple cases being introduced in the middle
  // of a series of fields (without a break statement intervening).
  CaseFields _current_fields;
  bool _fields_added;

  // This map indexes into the _cases vector, above.
  typedef pmap<string, int> CasesByValue;
  CasesByValue _cases_by_value;
};

#endif
