#ifndef LOCATION_H
#define LOCATION_H
#include "hub.h"
#include "err.h"
#include "named.h"
#include <string>

namespace hub {
    // ----------------------------------------------------------------------------
    class LocationItem {
        Name content_;
        bool locked_;
        bool oblig_;
    public:
        LocationItem (void): locked_(false), oblig_(false) {};
        LocationItem (const std::string& s): content_(s), locked_(false), oblig_(false) {};
        bool locked (void) const { return locked_; }
        void locked (bool v) { locked_ = v;  }
        bool oblig  (void) const { return oblig_; }
        void oblig  (bool v) { oblig_ = v;  }
        const std::string& content (void) const { return content_; }
        void content (const std::string& v) { content_ = v; }
        bool empty (void) { return content_.empty(); }
        void clear (void) { content_.clear(); locked_=false; oblig_=false; }
    };

    // ----------------------------------------------------------------------------
    class Location {
    protected:
        Architecture* theArch_; bool fixedArch_;
        Entity* theEntity_;     bool fixedEntity_;
        Library* theLib_;       bool fixedLib_;
        Design* theDesign_;     bool fixedDesign_;
        Hub* theHub_;
        std::string label_;             // human readable
        std::string ident_;             // short identification + Tk var prefix
    public:
        typedef enum { design, arch, ent, lib, last } what;
    protected:
        LocationItem items_ [last];

    public:
        LocationItem& operator [] (int i) { return items_[i]; }
        const LocationItem& operator [] (int i)const { return items_[i]; }

        Location (Hub*);
        Location (Hub*, const std::string&);
        Location (const Location&);

	    InconsReason FindOrCreate (void);       // for destinations - no design
        InconsReason FindOrCreateDesign (void);
	    InconsReason Find (void);               // for sources - architecture owerwrite check? 
        InconsReason FindDesign (void);         // for sources and dest design overwrite check
        template <class AImpl> InconsReason CreateUniqueEntity ();
                                                // for unique destinations
        void Reset(void);
        void Inherit (const Location&);         // get fields that are not locked

        Architecture* theArch   (void) const { return theArch_; }
        Entity*       theEntity (void) const { return theEntity_; }
        Library*      theLib    (void) const { return theLib_; }
        Design*       theDesign (void) const { return theDesign_; }
        Hub*          theHub    (void) const { return theHub_; }
        void theArch   (Architecture* a) { theArch_=a; }
        void theEntity (Entity* e)       { theEntity_=e; }
        void theLib    (Library* l)      { theLib_=l; }
        void theDesign (Design* d)       { theDesign_=d; }

        std::string label (void) const { return label_; }
        void label (const std::string& l) { label_ = l; }
        std::string ident (void) const { return ident_; }
        void ident (const std::string& l) { ident_ = l; }
    };
    // --------------------------------------------------------------------------------
    template <class AImpl> InconsReason Location :: CreateUniqueEntity () {
        // provisions for successive evaluation: 
        // existing pointers preserved until reset
        // ------  find/create library: could be taken over from an existing design?
        if (!theLib_) {
            if (items_[lib].empty()) {
                if (items_[lib].oblig()) {
                    return InconsReason ("Library not specified");
                } else {
                    theLib_ = NULL;
                    fixedLib_ = false;
                }
            } else {
                theLib_ = theHub_->FindOrCreateLib (items_[lib].content());
                fixedLib_ = true;
            }
        }

        // ------ find/create entity ------------------------------------------------
        if (!theEntity_) {
            if (items_[ent].empty()) {   // must be given
                return InconsReason ("Entity not specified");
            } else {
                if (theLib_) {
                    theEntity_ = theLib_->CreateUniqueEntity (items_[ent].content());
                    theArch_ = NULL;
                } else {
                    theEntity_ = NULL;
                }
            }
        }
        // ------ find/create architecture --------------------------------------------
        if (!theArch_) {
            if (items_[arch].empty()) {     // must be given
                return InconsReason ("Architecture not specified");
            } else {
                if (theEntity_) {
                    theArch_ = theEntity_->CreateArch <AImpl> (items_[arch].content());
                    theEntity_->CurrentArch(theArch_);
                } else {
                    theArch_ = NULL;
                }
            }
        }
        return InconsReason (0);
    }
}

#endif
