/*---------------------------------------------------------------------------------------------------------------------
- File      : html_tag.h                                                                  Project ELSE, EPFL - DI/LIA -
-                                                                       Evaluation in Language and Speech Engineering -
- Author    : Seydoux Florian   Creation date : 29 June 1999                                                          -
- Eulogist  : -                 Approval date : -                  Version: 0.1                                       -
-                                                                                                                     -
- Descript. : Define Html Tags and attributes.                                                                        -
-                                                                                                                     -
- Requested : -                                                                                                       -
-                                                                                                                     -
- Gaps      : o) Réécrire proprement toute les opération de lecture, avec réinitialisation correct (et vérifiée) du   -
-                flux dans le cas de tags inconnus. Extirper également les assertions dépendantes du format, au       -
-                profit d'un message d'erreur en bonne et due forme (un mauvais format de fichier ne devrait pas      -
-                stopper l'application, mais produire un message signalant l'erreur, et ignorer la section invalide). -
-             o) La conception est à repenser, et à 'uniformiser'. (Il serait meme presque préférable d'utiliser une  -
-                librairie spécialisée dans la persistance des objets & la lecture de format balisé)                  -
-             o) La conception actuelle interdit à une étiquette de posséder plusieurs attributs de meme nom          -
-             o) Sensibilité à la casse des identificateurs en désacord avec HTML (et l'usage des formats balisé)     -
-             o) Cosmétique: remplacer tous les littéraux relatifs au format par des constantes ('<','>','{' ...)     - 
-                                                                                                                     -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Rev. date | Reviser               | Revise's description                                                            -
- - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ../../....| ........              | ...                                                                             -
---------------------------------------------------------------------------------------------------------------------*/

#ifndef SGML_TAG_H
#define SGML_TAG_H    

#include <string>
#include <vector>
#include <map>
#include "globaldef.h"

#ifdef _USE_NAMESPACES
    namespace Else { 
#endif // _USE_NAMESPACES


//
// Define the attibutes. It can:
// o) have single (default) or multi values
// o) be compact (unnamed) or extended (default):
//     -> extended: <tag attrib_name=attrib_value> -> compact: <tag attrib_value> (== unnamed attribute)
// 0) be required (default) or optional:
//     -> if the attribute is optional and equal to the default value, it's not saved/displayed.
//        Warning: It's dangerous to define more than one optional compact attribute, or one but not at last attrib.
//        (attrib order: depend on name).
//        
// Attribut terminators: {'>',<space>,<ht>,<cr>,<lf>,<vt>,<bs>,<ff>} and {',' , '}'} for multivaluate attrib.
// 

class TagAttribute { // Can't have a value with '>', or ',' for the multi-valuate attributes.
public:
    TagAttribute(const bool extended = false, const bool optional = false, const bool multi = false);
    virtual ~TagAttribute();
    void read(istream& in);                 // input stream MUST be positioned on the good tag !
    void write(ostream& os) const;          // write ' vale' | ' name=value' | ' {value,value}' | ' name={value,value}'
    virtual const string& name() const = 0; // (*) Must always provided, even for compact attrib.
    virtual bool isDefault() const;         // true if the actual value is equal to the default value.
    virtual void reset();                   // After that, isDefault() for optional tag must be true.
    bool isExtended() const;
    bool isOptional() const;
    bool isMultiValuate() const;
    
protected:
    virtual void readSingle(istream& in);        // read a single value from the stream
    virtual void writeSingle(ostream& os) const; // write single value.
	virtual void prepareReadSequence() const;    // preamble for reading multi-valuate attrib (not necess. is most case)
    virtual void readNext(istream& in);          // read (add) one more value
    virtual bool prepareWriteSequence() const;   // preamble for writing multi-valuate attrib.
    virtual bool writeNext(ostream& os) const;   // return false at the end of the sequence.
    
    bool   extended;
    bool   optional;
    bool   multivaluate;

protected:
    static const string nullString;              // name for spcl or single attribute.
};

// (*) this signature (string&) implie that all derived class must have an (class) attribute for
//     storing the result, even though this name is part of the definition of the class, and not
//     a property of her. However, it's one of the ways to avoid invocation of multiple constructors.

template <class T> struct AlwaysRq { AlwaysRq(){}  bool operator()(const T&) const   { return false; } };
template <class T> struct NotEmpty { NotEmpty(){}  bool operator()(const T& t) const { return t.empty(); } };
template <class T> struct NotNull  { NotNull(){}   bool operator()(const T& t) const { return (t); } };

template <class T, class DefaultPredicate = AlwaysRq<T> >
class GenericAttribute: public TagAttribute {
public:
    GenericAttribute(const string& name, const T& init, const bool extended = false, const bool optional = false)
    : TagAttribute(extended, optional, false), id(name), attribute(init), predicat()
	{ }
	const string& name() const { return id; }
	bool isDefault() const { return predicat(attribute); }
	const T& operator()() const { return attribute; }
	T& operator()() { return attribute; }

	string id;
    T      attribute;
    DefaultPredicate predicat;

protected:
	void readSingle(istream& in)         { in >> attribute; }
    void writeSingle(ostream& os) const  { os << attribute; }
};

// Manque une classe générique pour les type énumérés.


typedef GenericAttribute<Real>                                       RealAttribute;
typedef GenericAttribute<string, NotEmpty<string> >                  StringAttribute;
typedef GenericAttribute<Natural, NotNull<Natural> >                 NaturalAttribute;
typedef GenericAttribute<ShortNatural, NotNull<ShortNatural> >       ShortNaturalAttribute;
typedef GenericAttribute<LongNatural, NotNull<LongNatural> >         LongNaturalAttribute;
typedef GenericAttribute<VeryLongNatural, NotNull<VeryLongNatural> > VeryLongNaturalAttribute;


class HtmlField {
	struct LessStringPtr {
		bool operator()(const string*const& s1, const string*const& s2) { return *s1 < *s2; }
	};
	typedef vector<TagAttribute*> AttribOrder;
	typedef map<const string*, TagAttribute*, LessStringPtr> AttributePtrs;
	typedef map<const string*, HtmlField*, LessStringPtr> HtmlFieldPtrs;

    class Start : public TagAttribute {
        friend class HtmlField;
        Start(const HtmlField& outer);
        const string& name() const;
        void writeSingle(ostream& os) const;
        const HtmlField& outer;
    };
    
    class Stop : public TagAttribute {
        friend class HtmlField;
        Stop(const HtmlField& outer);
        const string& name() const;
        void writeSingle(ostream& os) const;
        const HtmlField& outer;
    };

friend class Start;
friend class Stop;

public:
	static bool defineHtmlTag(HtmlField& field);   // false if field.tagName() is already defnied.
	static bool undefineHtmlTag(HtmlField& field); // false if field is unknown.

	static void skipNextTag(istream& in);
	static bool skipToTag(istream& in, HtmlField*const tag);

	static HtmlField* nextTag(istream& in);        // Advance to the next tag. If it's a known tag, return pointer to
	                                               // this, and get the tag's id out of the stream. Otherwise, return 0
	                                               // and keep the entire tag entry in the stream.

	static HtmlField* readHtmlTag(istream& in);    // return a pointer to the READED tag, or 0 if the next tag
	                                               // is NOT defined as known tag.

	// L'identification du prochain tag a lire dans le flot devrait retourner un identifiant sur le tag en question,
	// typiquement 'typeid'... mais comme avec gcc 2.8.x, on ne peut rien faire de cette info ( opérateurs == et !=
	// non publique)...
	
    HtmlField(const bool implicitEnding = true, const bool autoDefine = false);
    virtual HtmlField& operator=(const HtmlField& fld);
    virtual ~HtmlField();
    virtual void read(istream& in);           // (*) read head att., call readField(), and read tail att. if !implicitEnd
    virtual void write(ostream& os) const;    // write head att, call writeField(), and write tail, if !implicitEnd
    virtual const string& tagName() const = 0;

    const Start   startTag;
    const Stop    stopTag;
    bool  implicitEnding;

protected:
	virtual void readField(istream& in);         // by default, read nothing.
	virtual void writeField(ostream& os) const;  // by default, write nothing.
    bool defineHeadAttribute(TagAttribute& attribute); // false if attribute.name() is the same that another attribute
    bool defineTailAttribute(TagAttribute& attribute); // idem.

private:
	static HtmlFieldPtrs knownTags;

    AttributePtrs headAttributes;
    AttribOrder   headOrder;
    AttributePtrs tailAttributes;
    AttribOrder   tailOrder;
};   
 // (*) Read attribut bloc. If 'xxx=' is found, and 'xxx' is the name of an attribute, read the attribute,
 //     otherwise, read compact attribute in definition order.

#ifdef _USE_NAMESPACES
}
#endif // _USE_NAMESPACES

#endif // SGML_TAG_H

