
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#ifndef HDR_dbOASIS
#define HDR_dbOASIS

#include "dbPoint.h"

#include "tlException.h"
#include "tlInternational.h"
#include "tlString.h"
#include "tlAssert.h"

#include <string>
#include <vector>

//  place this macro to force linking of OASIS plugin
#define FORCE_LINK_OASIS void force_link_OASIS_f () { extern int force_link_OASIS; force_link_OASIS = 0; }

namespace db
{

/**
 *  @brief The diagnostics interface for reporting problems in the reader or writer
 */
class OASISDiagnostics
{
public:
  virtual ~OASISDiagnostics ();

  /**
   *  @brief Issue an error with positional informations
   */
  virtual void error (const std::string &txt) = 0;

  /**
   *  @brief Issue a warning with positional informations
   */
  virtual void warn (const std::string &txt) = 0;
};

class RepetitionBase;
class RepetitionIteratorBase;
class OASISReader;

/**
 *  @brief A repetition iterator
 */
class RepetitionIterator
{
public:
  /**
   *  @brief Create a repetition with the given implementation
   */
  RepetitionIterator (RepetitionIteratorBase *base);

  /**
   *  @brief Destructor
   */
  ~RepetitionIterator ();

  /**
   *  @brief Copy constructor
   */
  RepetitionIterator (const RepetitionIterator &d);

  /**
   *  @brief Assignment 
   */
  RepetitionIterator &operator= (const RepetitionIterator &d);

  /**
   *  @brief Comparison
   */
  bool operator== (const RepetitionIterator &d) const;

  /**
   *  @brief Inequality
   */
  bool operator!= (const RepetitionIterator &d) const
  {
    return !operator== (d);
  }

  /**
   *  @brief Tell, if the iterator is at the end
   */
  bool at_end () const;

  /**
   *  @brief Increment
   */
  RepetitionIterator &operator++ ();

  /**
   *  @brief Access
   */
  db::Point operator* () const;

private:
  RepetitionIteratorBase *mp_base; 
};

/**
 *  @brief A class representing a repetition
 */
class Repetition
{
public:
  /**
   *  @brief Create a repetition with the given implementation
   */
  Repetition (RepetitionBase *base = 0);

  /**
   *  @brief Destructor
   */
  ~Repetition ();

  /**
   *  @brief Copy constructor
   */
  Repetition (const Repetition &d);

  /**
   *  @brief Replace the base pointer
   */
  Repetition &operator= (RepetitionBase *base);

  /**
   *  @brief Assignment 
   */
  Repetition &operator= (const Repetition &d);

  /**
   *  @brief "Less" predicate
   */
  bool operator< (const Repetition &d) const;

  /**
   *  @brief Comparison
   */
  bool operator== (const Repetition &d) const;

  /**
   *  @brief Inequality
   */
  bool operator!= (const Repetition &d) const
  {
    return !operator== (d);
  }

  /**
   *  @brief Check, if the repetition is an repetition at all 
   *
   *  This method returns true, if the repetition is not singular.
   *  Singular repetitions are created by the default contructor.
   */
  bool is_singular () const
  {
    return mp_base == 0;
  }

  /**
   *  @brief Check, if the repetition is a regular one
   *
   *  This method returns true, if the repetition is regular. It 
   *  returns true, if the repetition can be represented as a 
   *  set of points 0..i*a+j*b (i=0..n-1,j=0..m-1).
   *  This method does not only return a flag but the parameters as
   *  well.
   */
  bool is_regular (db::Point &a, db::Point &b, size_t &n, size_t &m) const;

  /**
   *  @brief Check, if the repepetition is a iterated one
   *
   *  @return 0 if not, otherwise a pointer to a vector of points
   */
  const std::vector<db::Point> *is_iterated () const;

  /**
   *  @brief Get the iterator
   */
  RepetitionIterator begin () const;

  /**
   *  @brief Access to the base object
   */
  void set_base (RepetitionBase *b);

  /**
   *  @brief Access to the base object
   */
  RepetitionBase *base ()
  {
    return mp_base;
  }

  /**
   *  @brief Access to the base object
   */
  const RepetitionBase *base () const
  {
    return mp_base;
  }

private:
  RepetitionBase *mp_base; 
};

//  Base classes

class RepetitionBase 
{
public:
  RepetitionBase ()
  {
    // .. nothing yet ..
  }

  virtual ~RepetitionBase ()
  {
    // .. nothing yet ..
  }

  virtual RepetitionBase *clone () const = 0;
  virtual RepetitionIteratorBase *begin () const = 0;
  virtual unsigned int type () const = 0;
  virtual bool equals (const RepetitionBase *) const = 0;
  virtual bool less (const RepetitionBase *) const = 0;
  virtual bool is_regular (db::Point &a, db::Point &b, size_t &n, size_t &m) const = 0;
  virtual const std::vector<db::Point> *is_iterated () const = 0;
  virtual unsigned int type () = 0;
};


class RepetitionIteratorBase 
{
public:
  RepetitionIteratorBase ()
  {
    // .. nothing yet ..
  }

  virtual ~RepetitionIteratorBase ()
  {
    // .. nothing yet ..
  }

  virtual RepetitionIteratorBase *clone () const = 0;
  virtual void inc () = 0;
  virtual db::Point get () const = 0;
  virtual unsigned int type () const = 0;
  virtual bool equals (const RepetitionIteratorBase *) const = 0;
  virtual bool at_end () const = 0;
};


//  Regular repetitions

class RegularRepetition 
  : public RepetitionBase
{
public:
  RegularRepetition (const db::Point &a, const db::Point &b, size_t n, size_t m);

  virtual RepetitionBase *clone () const;
  virtual RepetitionIteratorBase *begin () const;
  virtual unsigned int type () const;
  virtual bool equals (const RepetitionBase *b) const;
  virtual bool less (const RepetitionBase *b) const;
  virtual bool is_regular (db::Point &a, db::Point &b, size_t &n, size_t &m) const;
  virtual const std::vector<db::Point> *is_iterated () const;
  virtual unsigned int type () { return 1; }

private:
  friend class RegularRepetitionIterator;

  db::Point m_a, m_b;
  size_t m_n, m_m;
};

class RegularRepetitionIterator 
  : public RepetitionIteratorBase
{
public:
  RegularRepetitionIterator (const RegularRepetition *rep, size_t i, size_t j);

  virtual RepetitionIteratorBase *clone () const;
  virtual void inc ();
  virtual db::Point get () const;
  virtual unsigned int type () const;
  virtual bool equals (const RepetitionIteratorBase *) const;
  virtual bool at_end () const;

private:
  const RegularRepetition *mp_rep;
  size_t m_i, m_j;
};

//  Irregular repetitions

class IrregularRepetition 
  : public RepetitionBase
{
public:
  IrregularRepetition ();

  virtual RepetitionBase *clone () const;
  virtual RepetitionIteratorBase *begin () const;
  virtual unsigned int type () const;
  virtual bool equals (const RepetitionBase *b) const;
  virtual bool less (const RepetitionBase *b) const;
  virtual bool is_regular (db::Point &a, db::Point &b, size_t &n, size_t &m) const;
  virtual const std::vector<db::Point> *is_iterated () const;
  virtual unsigned int type () { return 2; }

  void reserve (size_t n) 
  {
    m_points.reserve (n);
  }

  void push_back (const db::Point &p)
  {
    m_points.push_back (p);
  }

  std::vector<db::Point> &points ()
  {
    return m_points;
  }

private:
  friend class IrregularRepetitionIterator;

  std::vector <db::Point> m_points;
};

class IrregularRepetitionIterator 
  : public RepetitionIteratorBase
{
public:
  IrregularRepetitionIterator (const IrregularRepetition *rep, size_t i);

  virtual RepetitionIteratorBase *clone () const;
  virtual void inc ();
  virtual db::Point get () const;
  virtual unsigned int type () const;
  virtual bool equals (const RepetitionIteratorBase *) const;
  virtual bool at_end () const;

private:
  const IrregularRepetition *mp_rep;
  size_t m_i;
};

/**
 *  @brief A template class representing a modal variable
 */
template <class T>
class modal_variable
{
public:
  /**
   *  @brief Set up the modal variable
   *
   *  The reader is required since errors are reported to the reader.
   *  The name is reported in the error message. 
   *  This constructor creates an uninitialized variable.
   */
  modal_variable (OASISDiagnostics *reader = 0, const char *name = "")
    : mp_diag (reader), m_name (name), m_t (), m_initialized (false)
  {
    //  .. nothing yet ..
  }

  /**
   *  @brief Get the value of the modal variable
   * 
   *  An error will be reported if the value is not initialized.
   */
  const T &get () const;

  /**
   *  @brief Get the value of the modal variable
   * 
   *  This method does not report an error if the value is not initialized.
   *  After modifying the object, set_initialized is supposed to be called.
   */
  T &get_non_const ()
  {
    return m_t;
  }

  /**
   *  @brief Equality
   */
  bool operator== (const T &t) const
  {
    return (m_initialized && m_t == t);
  }

  /**
   *  @brief Inequality
   */
  bool operator!= (const T &t) const
  {
    return !operator== (t);
  }

  /**
   *  @brief Assignment operator
   */
  modal_variable &operator= (const modal_variable<T> &d)
  {
    m_t = d.m_t;
    m_initialized = d.m_initialized;
    return *this;
  }

  /**
   *  @brief Assign a value to the modal variable
   *
   *  This sets the variable into the initialized state.
   */
  template <class X>
  modal_variable &operator= (const X &x)
  {
    m_t = x;
    m_initialized = true;
    return *this;
  }

  /**
   *  @brief Swap with a target value (mainly intended for T==vector<something>)
   *
   *  This sets the variable into the initialized state.
   */
  template <class X>
  modal_variable &swap (X &x)
  {
    m_t.swap (x);
    m_initialized = true;
    return *this;
  }

  /**
   *  @brief Reset the initialized state of the variable
   */
  void reset () 
  {
    m_initialized = false;
  }

  /**
   *  @brief Set the initialized state of the variable
   *
   *  This method is supposed to be used after a modification has been performed
   *  with the non-const accessor.
   */
  void set_initialized () 
  {
    m_initialized = true;
  }

  /**
   *  @brief Test, if the variable is set
   */
  bool is_set () const
  {
    return m_initialized;
  }

private:
  OASISDiagnostics *mp_diag;
  std::string m_name;
  T m_t;
  bool m_initialized;
};

template <class T>
inline const T &
modal_variable<T>::get () const
{
  if (! m_initialized) {
    if (mp_diag) {
      mp_diag->warn (tl::to_string (QObject::tr ("Modal variable accessed before being defined: ")) + m_name);
    } else {
      tl_assert (false);
    }
  }
  return m_t;
}

}

#endif

