/// <summary>
    /// Třída zapouzdřující základní chování (Layer Supertype)
    /// </summary>
    public abstract class BusinessObjectBase : IPersistable
    {
        
        #region Public constants
        /// <summary>
        /// Konstanta pro Id objektu bez perzistence v databáze
        /// </summary>
        public const int NO_ID = -1;
        
        /// <summary>
        /// Konstanta používaná, jestliže není nalezen objekt/sloupec atd.
        /// </summary>
        public const int NOT_FOUND = -1;
        #endregion Public constants
        
        #region Private variables
        private bool m_isDirty;
        private bool m_isLoaded;
        private bool m_isNew;
        private bool m_isDeleted;
        #endregion Private variables
        
        #region Protected fields
        protected int m_id;
        protected ObjectState m_objectState;        
        #endregion Protected fields

        /// <summary>
        /// Konstruktor pro objekt bez perzistence v databázi
        /// </summary>
        public BusinessObjectBase() : base()
        {
            m_isDirty = false;
            
            m_id =   NO_ID;
            setNew();
            SetLoaded();
            m_objectState = ObjectState.Active;
            
        }
        
        /// <summary>
        /// Konstruktor pro objekt, jehož data jsou perzistována v databázi
        /// </summary>
        public BusinessObjectBase(int id) : base()
        {
            m_isDirty = false;
            m_isLoaded = false;
            m_id = id;
            
        }
        

        #region Public properties
        /// <summary>
        /// Atribut udávájící, zda byla data změněna oproti datům v databázi
        /// </summary>
        public virtual bool IsDirty
        {
            get
            {
                return m_isDirty;
            }
        }

        /// <summary>
        /// Atribut udávájící, zda byla data objektu změněna oproti datům v databázi
        /// </summary>
        public virtual bool IsLoaded
        {
            get
            {
                return m_isLoaded;
            }
        }
        
        /// <summary>
        /// Atribut má hodnotu true, jestliže jde o objekt bez perzistence
        /// </summary>
        public virtual bool IsNew
        {
            get
            {
                return m_isNew;        
            }
            
        }

        /// <summary>
        /// Atribut má hodnotu true, jestliže byl vymazán objekt z databáze, případně byl zapsán příznak neaktivní v databázi
        /// </summary>
        public virtual bool IsDeleted
        {
            get
            {
                return m_isDeleted;
            }
        }

        /// <summary>
        /// Id objektu
        /// </summary>
        public virtual int Id
        {
            get
            {
                return m_id;

            }
        }

        /// <summary>
        /// Stav objektu
        /// </summary>
        public virtual ObjectState ObjectState
        {
            get
            {
                return m_objectState;
            }
        }

        #endregion Public pproperties

        #region Public methods
        #region IPersistable members
        
        /// <summary>
        /// Implementace metody nahraje objekt z databáze
        /// </summary>
        public virtual void Load()
        {
            
            if (IsLoaded && IsDeleted)
            {
                return;
            }
            
            DataRow row = DataCacheHelper.Instance.GetData(this.GetType(), m_id);
            
            try
            {
                            
                if (row == null)
                {
                    DoInternalLoad();
                }
                else
                {
                    DoInternalLoad(row);                
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine(e);
                throw;
            }
            
            SetLoaded();
        }

        /// <summary>
        /// Implementace metody uloží objekt do databáze
        /// </summary>
        public virtual void Save()
        {
            if ((!IsLoaded) || 
                (IsDeleted) ||
                ((!IsDirty) && (!IsNew)))
            {
                return;
            }
            
            try
            {
                DoInternalSave();            
            }
            
            catch (Exception e)
            {
                Trace.WriteLine(e);
                throw;
            }
            
          setClear();
	    if (ObjectState == ObjectState.Discarded)
	    {
		SetDeleted();
	    }
        }

        
        #endregion IPersistable members

        /// <summary>
        /// Metoda převede   objekt do stavu ObjectState.Discarded
        /// </summary>
        public virtual void Discard()
        {
            TriggerLoad();
            
            if (IsDeleted)
            {
                return;
            }
            
            m_objectState = ObjectState.Discarded;
            SetDirty();

        }     
        
        #endregion Public methods
        
        
        
       #region Protected methods
        
        /// <summary>
        /// Metoda zavolá metodu Save na všech IPersistable objektech v argumentu
        /// </summary>
        /// <param name="persistableObjectsList">Kolekce objektů implementujících IPersistable </param>
        protected static void SaveCollection(IEnumerable persistableObjectsList)
        {
            foreach (IPersistable persistable in  persistableObjectsList)
            {
                persistable.Save();
            }
        }

            /// <summary>
        /// Metoda nahraje objekt z předaného řádku
        /// </summary>
        /// <param name="row">Řádek, z něhož mají být nahrána data</param>
        /// <remarks>Metoda čte z předaného řádku pouze atribut State</remarks>
        protected virtual void DoInternalLoad (DataRow row)
        {
            Debug.Assert(row.Table.Columns.IndexOf("State") > NOT_FOUND, 
                        "Assertion failed -  method BusinessObjectBase.DoInternalLoad - Column State does not exist");
            
            m_objectState =  (ObjectState) row["State"];
        }
        
        /// <summary>
        /// Implementace metody nahraje objekt z databáze
        /// </summary>
        /// <remarks>Prázdná implementace</remarks>
        protected virtual void DoInternalLoad()
        {
            
        }
        
        /// <summary>
        /// Implementace metody uloží objekt do databáze
        /// </summary>
        /// <remarks>Prázdná implementace</remarks>
        protected virtual void DoInternalSave()
        {

        }

        

        /// <summary>
        /// Metoda iniciuje nahrávání objektu
        /// </summary>
        protected virtual void TriggerLoad()
        {
            if((IsLoaded) || (IsNew) || (IsDeleted))
            {
                return;
            }
            
            try
            {
                Load();    
            }
            
            catch(Exception e)
            {

                Trace.WriteLine(e);
                throw;
            }

            SetLoaded();
        
        }
        
        /// <summary>
        /// Metoda přepisují odvozené třídy v případě, že potřebují inicializovat například instance kolekcí
        /// </summary>
        /// <remarks>Metoda nemá implementaci a NENÍ volána z konstruktorů tříy <see cref = "BusinessObjectBase" /> </see></remarks>
        protected virtual void Init()
        {
            
        }
        
        /// <summary>
        /// Metoda označí objekt jako změněný
        /// </summary>
        protected virtual void SetDirty()
        {
            if (IsNew || IsDeleted)
            {
                return;
            }
            m_isDirty = true;
        }
        
        /// <summary>
        /// Metoda zkontroluje rovnost dvou objektů - jestliže se objekty neshodují, je instance označena jako změněná (Dirty)
        /// </summary>
        /// <param name="obj1">První argument operace Equals</param>
        /// <param name="obj2">Druhý argument operace Equals</param>
        /// <remarks>Metodu používají odvozené třídy při kontrole změněných atributů</remarks>
        protected virtual bool CheckEquals(object obj1, object obj2)
        {
            if (!Object.Equals(obj1, obj2))
            {
                SetDirty();
                return false;
            }
            
            return true;
        }
        
        /// <summary>
        /// Metoda nastaví příznak, že byla nahrána data objektu
        /// </summary>
        protected virtual void SetLoaded()
        {
            m_isLoaded = true;
        }

        /// <summary>
        /// Metoda nastaví příznak, že proběhlo zrušení objektu v databázi
        /// </summary>
        protected virtual void SetDeleted()
        {
            m_isDeleted = true;
        }    
        
        #endregion Protected methods
        
        #region Private methods
        /// <summary>
        /// Metoda označí objekt jako nový (neuložený v databázi)
        /// </summary>
        private void setNew()
        {
            m_isNew = true;
        
        }

        /// <summary>
        /// Metoda zruší příznak Nový a Změněný v databázi
        /// </summary>
        private void setClear()
        {
            m_isNew = false;
            m_isDirty = false;
        }
        #endregion Private methods        
        
    }

        /// <summary>
    /// Rozhraní, které implementují všechny perzistentní objekty
    /// </summary>
    public interface IPersistable
    {
        /// <summary>
        /// Implementace metody nahraje objekt z databáze
        /// </summary>
        void Load();

        /// <summary>
        /// Implementace metody uloží objekt do databáze
        /// </summary>
        void Save();
    }

    /// <summary>
    /// Enumerace vyjadřující stav objektů
    /// </summary>
    public enum ObjectState : int
    {
        /// <summary>
        /// Neaktivní
        /// </summary>
        Inactive = 0,
        
        /// <summary>
        /// Aktivní
        /// </summary>
        Active = 1,
        
        /// <summary>
        /// Zrušený
        /// </summary>
        Discarded = 2
    }