namespace SilverlightSqlSpatial.Web
{
    using System;
    using System.Collections;
    using System.Data.Services;
    using System.Linq;
    using System.Reflection;
    using Telerik.OpenAccess;

    /// <summary>
    /// Open Access Data Manager class
    /// </summary>
    public partial class DataManager : IDisposable, ICloneable, IUpdatable
    {
        #region Private Memebrs

        /// <summary>
        /// OpenAccess Context
        /// </summary>
        private OpenAccessContext _openAccessContext = null;
		
        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="OADataContext"/> class.
        /// </summary>
        public DataManager()
        {
            this._openAccessContext = new SilverlightSqlSpatial.Model.EntitiesModel();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="OADataContext"/> class.
        /// </summary>
        /// <param name="scope">Object scope <see cref="IObjectScope"/>.</param>
        public DataManager(OpenAccessContext context)
        {
            this._openAccessContext = context;
        }

        #endregion 
        
        #region Private helper Functions

        /// <summary>
        /// Roll Back the trancation 
        /// </summary>
        /// <param name="scope"> The scope of the trancation </param>
        public void HandleException(Exception ex)
        {
            try
            {
                if (this._openAccessContext != null)
                {
                    this._openAccessContext.ClearChanges();
                }
            }
            finally
            {
                throw ex;
            }
        }
       
        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the Counties.
        /// </summary>
        /// <value>The Counties.</value>
        public IQueryable<SilverlightSqlSpatial.Model.County> Counties
        {
            get
            {
                return this._openAccessContext.GetAll<SilverlightSqlSpatial.Model.County>();
            }
        } 
                /// <summary>
        /// Gets the Locations.
        /// </summary>
        /// <value>The Locations.</value>
        public IQueryable<SilverlightSqlSpatial.Model.Location> Locations
        {
            get
            {
                return this._openAccessContext.GetAll<SilverlightSqlSpatial.Model.Location>();
            }
        } 
        	
        #endregion
        
        #region IUpdatable Members

        /// <summary>
        /// Creates the resource of the specified type and that belongs to the specified container.
        /// </summary>
        /// <param name="containerName">The name of the entity set to which the resource belongs.</param>
        /// <param name="fullTypeName">The full namespace-qualified type name of the resource.</param>
        /// <returns>
        /// The object representing a resource of specified type and belonging to the specified container.
        /// </returns>
        public object CreateResource(string containerName, string fullTypeName)
        {
            try
            {
                object resource = Activator.CreateInstance(_openAccessContext.GetType().Assembly.GetType(fullTypeName));
                this._openAccessContext.Add(resource);
                return resource;
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return null;
            }
        }

        /// <summary>
        /// Gets the resource of the specified type identified by a query and type name.
        /// </summary>
        /// <param name="query">Language integratee query(LINQ) pointing to a particular resource.</param>
        /// <param name="fullTypeName">The fully qualified type name of resource.</param>
        /// <returns>
        /// An opaque object representing a resource of the specified type, referenced by the specified query.
        /// </returns>
        public object GetResource(IQueryable query, string fullTypeName)
        {
            try
            {
                object resource = null;

                foreach (object o in query)
                {
                    if (resource != null)
                    {
                        throw new Exception("Expected a single response");
                    }
                    resource = o;
                }

                if (resource == null)
                    return new Telerik.OpenAccess.Exceptions.NoSuchObjectException("Object cannot be found.", null);

                return resource;
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return null;
            }
        }

        /// <summary>
        /// Updates the resource identified by the parameter <paramref name="resource"/>.
        /// </summary>
        /// <param name="resource">The resource to be updated.</param>
        /// <returns>resouce value object</returns>
        public object ResetResource(object resource)
        {
            try
            {
                this._openAccessContext.Refresh( 
                    RefreshMode.OverwriteChangesFromStore,resource);
                return resource;
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return null;
            }
        }

        /// <summary>
        /// Sets the value of the property with the specified name on the target resource to the specified property value.
        /// </summary>
        /// <param name="targetResource">The target object that defines the property.</param>
        /// <param name="propertyName">The name of the property whose value needs to be updated.</param>
        /// <param name="propertyValue">The property value for update.</param>
        public void SetValue(object targetResource, string propertyName, object propertyValue)
        {
            try
            {
                PropertyInfo pi = targetResource.GetType().GetProperty(propertyName);
                if (pi == null)
                    throw new Exception("Can't find property");
				
				if (pi.CanWrite)
                {
                	pi.SetValue(targetResource, propertyValue, null);
				}
            }
            catch(OpenAccessException ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Gets the value of the specified property on the target object.
        /// </summary>
        /// <param name="targetResource">An opaque object that represents a resource.</param>
        /// <param name="propertyName">The name of the property whose value needs to be retrieved.</param>
        /// <returns>value of the resource</returns>
        public object GetValue(object targetResource, string propertyName)
        {
            try
            {
                PropertyInfo pi = targetResource.GetType().GetProperty(propertyName);
                if (pi == null)
                    throw new Exception("Can't find property");

                return pi.GetValue(targetResource, null);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Sets the value of the specified reference property on the target object.
        /// </summary>
        /// <param name="targetResource">The target object that defines the property.</param>
        /// <param name="propertyName">The name of the property whose value needs to be updated.</param>
        /// <param name="propertyValue">The property value to be updated.</param>
        public void SetReference(object targetResource, string propertyName, object propertyValue)
        {
            SetValue(targetResource, propertyName, propertyValue);
        }

        /// <summary>
        /// Adds the specified value to the collection.
        /// </summary>
        /// <param name="targetResource">Target object that defines the property.</param>
        /// <param name="propertyName">The name of the collection property to which the resource should be added..</param>
        /// <param name="resourceToBeAdded">The opaque object representing the resource to be added.</param>
        public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
        {
            PropertyInfo pi = targetResource.GetType().GetProperty(propertyName);
            if (pi == null)
                throw new Exception("Can't find property");
            IList collection = (IList)pi.GetValue(targetResource, null);

            collection.Add(resourceToBeAdded);
        }

        /// <summary>
        /// Removes the specified value from the collection.
        /// </summary>
        /// <param name="targetResource">The target object that defines the property.</param>
        /// <param name="propertyName">The name of the property whose value needs to be updated.</param>
        /// <param name="resourceToBeRemoved">The property value that needs to be removed.</param>
        public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
        {
            try
            {
                PropertyInfo pi = targetResource.GetType().GetProperty(propertyName);

                if (pi == null)
                    throw new Exception("Can't find property");
                IList collection = (IList)pi.GetValue(targetResource, null);

                collection.Remove(resourceToBeRemoved);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Deletes the specified resource.
        /// </summary>
        /// <param name="targetResource">The resource to be deleted.</param>
        public void DeleteResource(object targetResource)
        {
            try
            {
                this._openAccessContext.Delete(targetResource);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Saves all the changes that have been made by using the <see cref="T:System.Data.Services.IUpdatable"/> APIs.
        /// </summary>
        public void SaveChanges()
        {
            try
            {
                this._openAccessContext.SaveChanges();
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Returns the instance of the resource represented by the specified resource object.
        /// </summary>
        /// <param name="resource">The object representing the resource whose instance needs to be retrieved.</param>
        /// <returns>
        /// Returns the instance of the resource represented by the specified resource object.
        /// </returns>
        public object ResolveResource(object resource)
        {
            try
            {
                return resource;
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return null;
            }
        }

        /// <summary>
        /// Cancels a change to the data.
        /// </summary>
        public void ClearChanges()
        {
            try
            {
                this._openAccessContext.ClearChanges();
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        #endregion
        
        #region ICloneable Members

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public virtual object Clone()
        {
            return Activator.CreateInstance(GetType(), this._openAccessContext);
        }

        #endregion
        
        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this._openAccessContext != null)
            {
                this._openAccessContext.Dispose();
                this._openAccessContext = null;
            }
        }

        #endregion
    }
}
