// Copyright 2000 by Kevin Atkinson under the terms of the LGPL

#ifndef __aspell_data_hh_
#define __aspell_data_hh_

#include <string>
#include <iostream>
#include <cassert>

#include "emulation.hh"
#include "language.hh"
#include "copy_ptr.hh"

class PspellAppendableString;

namespace autil {
  class SimpleFstream;
}

namespace aspell {

  using namespace autil;

  class Manager;
  class Language;
  class Config;

  class SensitiveCompare;

  class DataSet {
    friend class Manager;
  private:
    CopyPtr<Language>       lang_;
    int                     attach_count_;
  private:
    void attach(const Language &);
    void detach();
  public:
    class FileName {
      void copy(const FileName & other);
    public:
      const string       path;
      const char * const name;
      
      void clear();
      void set(const string &);
      
      FileName() {clear();}
      explicit FileName(const string & str) {set(str);}
      FileName(const FileName & other) {copy(other);}
      FileName & operator=(const FileName & other) {copy(other); return *this;}
    };
    class Id;
  protected:
    CopyPtr<Id> id_;
    virtual void set_lang_hook(Config *) {}
    
  public:
    //this is here because dynamic_cast in gcc 2.95.1 took too dam long
    enum BasicType {no_type, basic_word_set, basic_replacement_set, basic_multi_set};
    BasicType basic_type;


    DataSet();
    virtual ~DataSet();
    const Id & id() {return *id_;}
    void check_lang(const string& lang);
    void set_check_lang(const string & lang, Config *);
    const Language * lang() const {return lang_;};
    const char * lang_name() const;
    bool is_attached () const ;
  };

  bool operator==(const DataSet::Id & rhs, const DataSet::Id & lhs);

  inline bool operator!=(const DataSet::Id & rhs, const DataSet::Id & lhs)
  {
    return !(rhs == lhs);
  }

  struct LocalWordSetInfo;

  class LoadableDataSet : public DataSet {
  private:
    FileName file_name_;
  protected:
    void set_file_name(const string & name);
    void update_file_info(SimpleFstream & f);
  public:
    bool compare(const LoadableDataSet &);
    const char * file_name() const {return file_name_.path.c_str();}
    virtual void load(const string &, Config *, Manager * = 0, const LocalWordSetInfo * li = 0) = 0;
  };

  class WritableDataSet {
  public:
    virtual void merge(const string &) = 0;
    virtual void synchronize() = 0;
    virtual void save_noupdate() = 0;
    virtual void save_as(const string &) = 0;
    virtual void clear() = 0;
  };

  struct CompoundInfo {
    unsigned char d;
    
    CompoundInfo(unsigned char d0 = 0) : d(d0) {}

    unsigned int mid_char()       const {return d & (1<<0|1<<1);}

    void mid_char(unsigned int c) {
      assert(c < 4);
      d |= c;
    }

    bool mid_required ()       const {return d & 1<<2; }
    void mid_required (bool c)       { d |= c<<2;}
    
    bool beg () const {return d & 1<<3;}
    void beg (bool c) {d |= c<<3;}

    bool mid () const {return d & 1<<4;}
    void mid (bool c) {d |= c<<4;}

    bool end () const {return d & 1<<5;}
    void end (bool c) {d |= c<<5;}

    bool any() const {return d & (1<<3|1<<4|1<<5);}

    const char * read(const char * str, const Language & l);
    void write(PspellAppendableString &, const Language & l) const;

    SimpleFstream & write(SimpleFstream & o, const Language & l) const;
    ostream & write(ostream & o, const Language & l) const;

    enum Position {Orig, Beg, Mid, End};

    bool compatible(Position pos);
  };

  CompoundInfo::Position 
  new_position(CompoundInfo::Position unsplit_word, 
	       CompoundInfo::Position pos);
  
  struct SoundslikeWord {
    const char * soundslike;
    const void * word_list_pointer;

    operator bool () const {return soundslike;}
  
    SoundslikeWord() : soundslike(0) {}
    SoundslikeWord(const char * w, const void * p) 
      : soundslike(w), word_list_pointer(p) {}
  };

  static const unsigned int MaxCompoundLength = 8;

  struct BasicWordInfo {
    const char * word;
    CompoundInfo compound;
    BasicWordInfo(const char * w = 0, CompoundInfo c = 0)
      : word(w), compound(c) {}
    //operator const char * () const {return word;}
    operator bool () const {return word != 0;}
    void get_word(string & w, const ConvertWord &c) {
      w = "";
      c.convert(word, w);
    }
     
    SimpleFstream & write(SimpleFstream & o, const Language & l,
			  const ConvertWord &) const;
    ostream & write(ostream & o, const Language & l,
		    const ConvertWord &) const;
  };

  struct SingleWordInfo {
    const char * word;
    char middle_char;
    SingleWordInfo(const char * w = 0, char mc = '\0')
      : word(w), middle_char(mc) {}
    void clear() {word = 0;}
    void set(const char * w, char mc = '\0') {word = w; middle_char = mc;}
    void append_word(string & word, const Language & l, 
		     const ConvertWord &) const;
    operator bool() const {return word != 0;}
  };

  struct WordInfo {
    SingleWordInfo words[MaxCompoundLength + 1];
    void get_word(string & word, const Language & l, 
		  const ConvertWord &) const;
    operator bool () const {return words[0].word != 0;}
    SimpleFstream & write(SimpleFstream & o, const Language & l, 
			  const ConvertWord &) const;
    ostream & write(ostream & o, const Language & l, 
		   const ConvertWord &) const;
  };

  struct LocalWordSetInfo 
  {
    SensitiveCompare compare;
    ConvertWord      convert;
    void set_language(const Language * l);
    void set(const Language * l, const Config * c, bool strip = false);
  };

  class BasicWordSet : public LoadableDataSet
  {
  public:
    BasicWordSet() {
      basic_type =  basic_word_set;
    }
    
    typedef Emulation<BasicWordInfo>      Emul;
    typedef VirEmulation<BasicWordInfo>   VirEmul;
    typedef const char *                  Value;
    typedef unsigned int                  Size;
    typedef SoundslikeWord                SoundslikeValue;
    typedef Emulation<SoundslikeWord>     SoundslikeEmul;
    typedef VirEmulation<SoundslikeWord>  VirSoundslikeEmul;

    virtual VirEmul * elements() const = 0;
    virtual Size   size()     const = 0;
    virtual bool   empty()    const {return !size();}
  
    virtual BasicWordInfo lookup (const char * word,   
			     const SensitiveCompare &) const = 0;
    virtual BasicWordInfo lookup (const string & word, 
			     const SensitiveCompare &) const = 0;
    
    // guaranteed to return all words with the soundslike 
    virtual VirEmul * words_w_soundslike(const char * sondslike) const = 0;

    // the elements returned are only guaranteed to remain valid
    // guaranteed to return all soundslike and all words 
    // however an individual soundslike may appear multiple
    // times in the list....
    virtual VirSoundslikeEmul * soundslike_elements() const = 0;

    // NOT garanteed to return all words with the soundslike
    virtual VirEmul * words_w_soundslike(SoundslikeWord soundslike) const = 0;

  };

  class WritableWordSet : public BasicWordSet,
			  public WritableDataSet
  {
  public:
    virtual void add(const string &w) = 0;
    virtual void add(const string &w, const string &s) = 0;
  };

  struct ReplacementList {
    typedef Emulation<const char *>    Emul;
    typedef const char *               Value;
    typedef VirEmulation<const char *> VirEmul;

    const char *  misspelled_word;
    VirEmul    *  elements; // you are responable for freeing this with delete
    bool empty() const {return elements == 0;}

    ReplacementList()
      : elements(0) {}
    ReplacementList(const char * w, VirEmul * els)
      : misspelled_word(w), elements(els) {}
  };

  class BasicReplacementSet : public LoadableDataSet
  {
  public:
    BasicReplacementSet() {
      basic_type = basic_replacement_set;
    }
    
    typedef Emulation<ReplacementList>    Emul;
    typedef VirEmulation<ReplacementList> VirEmul;
    typedef const char *                  Value;
    typedef unsigned int                  Size;
    typedef SoundslikeWord                SoundslikeValue;
    typedef Emulation<SoundslikeWord>     SoundslikeEmul;
    typedef VirEmulation<SoundslikeWord>  VirSoundslikeEmul;

    virtual VirEmul * elements() const = 0;
    virtual Size   size()     const = 0;
    virtual bool   empty()    const {return !size();}

    virtual VirEmul * repls_w_soundslike(const char * soundslike) const = 0;
    virtual VirEmul * repls_w_soundslike(SoundslikeWord soundslike) const = 0;
    
    virtual VirSoundslikeEmul * soundslike_elements() const = 0;
  };


  class WritableReplacementSet : public BasicReplacementSet,
				 public WritableDataSet
  {
  public:
    virtual void add(const string &mis, const string &cor) = 0;
    virtual void add(const string &mis, const string &cor, const string &s) = 0;
  };

  struct LocalWordSet {
    // NOTE: perhaps LoadableDataSet is too specific
    LoadableDataSet  * word_set;
    LocalWordSetInfo local_info;
    LocalWordSet() : word_set(0) {}
    LocalWordSet(LoadableDataSet * ws, LocalWordSetInfo li) 
      : word_set(ws), local_info(li) {}
    operator bool () const {return word_set != 0;}
  };
  
  class BasicMultiSet : public LoadableDataSet
  {
  public:
    BasicMultiSet() {
      basic_type = basic_multi_set;
    }
    
    typedef LocalWordSet         Value;
    typedef Emulation<Value>     Emul;
    typedef VirEmulation<Value>  VirEmul;
    typedef unsigned int         Size;

    virtual VirEmul * elements() const = 0;
    virtual Size   size()     const = 0;
    virtual bool   empty()    const {return !size();}
  };


  typedef unsigned int DataType;
  static const DataType DT_ReadOnly     = 1<<0;
  static const DataType DT_Writable     = 1<<1;
  static const DataType DT_WritableRepl = 1<<2;
  static const DataType DT_Multi        = 1<<3;
  static const DataType DT_Any          = 0xFF;

  LoadableDataSet * add_data_set(const string & file_name,
				 Config &,
				 Manager * = 0,
				 const LocalWordSetInfo * = 0,
				 DataType allowed = DT_Any);
  
  // implemented in readonly_ws.cc
  BasicWordSet * new_default_readonly_word_set();
  void create_default_readonly_word_set(VirEmulation<char *> * els,
					Config & config);

  // implemented in multi_ws.cc
  BasicMultiSet * new_default_multi_word_set();

  // implemented in writable_ws.cc
  WritableWordSet * new_default_writable_word_set();

  // implemented in writable_repl.cc
  WritableReplacementSet * new_default_writable_replacement_set();

  
}

#endif

