using System;
using System.Data;
using System.Data.Objects;
using System.Linq;
using System.Net;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using System.Text;
using System.Web;

namespace TaskIt.Web.Services
{
    // TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access
    // Also consider adding roles to restrict access as appropriate.
    // [RequiresAuthentication]
    [EnableClientAccess]
    public class DataService : LinqToEntitiesDomainService<TaskItEntities>
    {
        public IQueryable<Contact> GetContacts(long userID)
        {
            return ObjectContext.Contacts.Where(c => c.UserID == userID || c.UserID == 2).OrderBy(c => c.Name);
        }

        public void InsertContact(Contact contact)
        {
            contact.CreatedDate = DateTime.Now;
            contact.ModifiedDate = DateTime.Now;
            if ((contact.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(contact, EntityState.Added);
            }
            else
            {
                ObjectContext.Contacts.AddObject(contact);
            }
        }

        public void UpdateContact(Contact contact)
        {
            contact.ModifiedDate = DateTime.Now;
            ObjectContext.Contacts.AttachAsModified(contact, ChangeSet.GetOriginal(contact));
        }

        public void DeleteContact(Contact contact)
        {
            var notes = GetContactNotes(contact.ID);
            foreach (var note in notes)
            {
                DeleteContactNote(note);
            }
            if ((contact.EntityState == EntityState.Detached))
            {
                ObjectContext.Contacts.Attach(contact);
            }
            ObjectContext.Contacts.DeleteObject(contact);
        }

        public IQueryable<ContactNote> GetContactNotes(long contactID)
        {
            return ObjectContext.ContactNotes.Where(n => n.ContactID == contactID);
        }

        public void InsertContactNote(ContactNote contactNote)
        {
            contactNote.CreatedDate = DateTime.Now;
            if ((contactNote.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(contactNote, EntityState.Added);
            }
            else
            {
                ObjectContext.ContactNotes.AddObject(contactNote);
            }
        }

        public void DeleteContactNote(ContactNote note)
        {
            if ((note.EntityState == EntityState.Detached))
            {
                ObjectContext.ContactNotes.Attach(note);
            }
            ObjectContext.ContactNotes.DeleteObject(note);
        }

        public IQueryable<Context> GetContexts(long userID)
        {
            return ObjectContext.Contexts.Where(c => c.UserID == userID || c.UserID == 2).OrderBy(c => c.Name);
        }

        public void InsertContext(Context context)
        {
            context.CreatedDate = DateTime.Now;
            context.ModifiedDate = DateTime.Now;
            if ((context.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(context, EntityState.Added);
            }
            else
            {
                ObjectContext.Contexts.AddObject(context);
            }
        }

        public void UpdateContext(Context context)
        {
            context.ModifiedDate = DateTime.Now;
            ObjectContext.Contexts.AttachAsModified(context, ChangeSet.GetOriginal(context));
        }

        public void DeleteContext(Context context)
        {
            var notes = GetContextNotes(context.ID);
            foreach (var note in notes)
            {
                DeleteContextNote(note);
            }
            if ((context.EntityState == EntityState.Detached))
            {
                ObjectContext.Contexts.Attach(context);
            }
            ObjectContext.Contexts.DeleteObject(context);
        }

        public IQueryable<ContextNote> GetContextNotes(long contextID)
        {
            return ObjectContext.ContextNotes.Where(n => n.ContextID == contextID);
        }

        public void InsertContextNote(ContextNote contextNote)
        {
            contextNote.CreatedDate = DateTime.Now;
            if ((contextNote.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(contextNote, EntityState.Added);
            }
            else
            {
                ObjectContext.ContextNotes.AddObject(contextNote);
            }
        }

        public void DeleteContextNote(ContextNote note)
        {
            if ((note.EntityState == EntityState.Detached))
            {
                ObjectContext.ContextNotes.Attach(note);
            }
            ObjectContext.ContextNotes.DeleteObject(note);
        }

        public IQueryable<Priority> GetPriorities()
        {
            return ObjectContext.Priorities;
        }

        public IQueryable<Project> GetProjects(long userID)
        {
            return ObjectContext.Projects.Where(p => p.UserID == userID || p.UserID == 2).OrderBy(p => p.Name);
        }

        public IQueryable<Project> GetProjectsDueThisWeek(long userID)
        {
            var weekStartDate = ServiceUtils.GetStartOfCurrentWeek();
            var weekEndDate = ServiceUtils.GetEndOfCurrentWeek();
            return ObjectContext.Projects.Where(p => p.UserID == userID).Where(p => p.CompletionDate == null).Where(p => p.DueDate > weekStartDate).Where(p => p.DueDate < weekEndDate);
        }

        public void InsertProject(Project project)
        {
            project.CreatedDate = DateTime.Now;
            project.ModifiedDate = DateTime.Now;
            if ((project.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(project, EntityState.Added);
            }
            else
            {
                ObjectContext.Projects.AddObject(project);
            }
        }

        public void UpdateProject(Project project)
        {
            project.ModifiedDate = DateTime.Now;
            ObjectContext.Projects.AttachAsModified(project, ChangeSet.GetOriginal(project));
        }

        public void DeleteProject(Project project)
        {
            var notes = GetProjectNotes(project.ID);
            foreach (var note in notes)
            {
                DeleteProjectNote(note);
            }
            if ((project.EntityState == EntityState.Detached))
            {
                ObjectContext.Projects.Attach(project);
            }
            ObjectContext.Projects.DeleteObject(project);
        }

        public IQueryable<ProjectNote> GetProjectNotes(long projectID)
        {
            return ObjectContext.ProjectNotes.Where(n => n.ProjectID == projectID);
        }

        public void InsertProjectNote(ProjectNote projectNote)
        {
            projectNote.CreatedDate = DateTime.Now;
            if ((projectNote.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(projectNote, EntityState.Added);
            }
            else
            {
                ObjectContext.ProjectNotes.AddObject(projectNote);
            }
        }

        public void DeleteProjectNote(ProjectNote note)
        {
            if ((note.EntityState == EntityState.Detached))
            {
                ObjectContext.ProjectNotes.Attach(note);
            }
            ObjectContext.ProjectNotes.DeleteObject(note);
        }

        public IQueryable<Task> GetTasksByTaskCategory(long userID, long taskCategoryID, bool showCompleted, bool showDue)
        {
            var query = ObjectContext.Tasks.Include("Project").Where(t => t.UserID == userID).Where(t => t.TaskCategoryID == taskCategoryID);
            return ServiceUtils.GetCompleteTasksQuery(query, showCompleted, showDue);
        }

        public IQueryable<Task> GetAllTasks(long userID, bool showCompleted, bool showDue)
        {
            var query = ObjectContext.Tasks.Where(t => t.UserID == userID);
            return ServiceUtils.GetCompleteTasksQuery(query, showCompleted, showDue);
        }

        public IQueryable<Task> GetTasksByProject(long userID, long projectID, bool showCompleted, bool showDue)
        {
            var query = ObjectContext.Tasks.Where(t => t.UserID == userID).Where(t => t.ProjectID == projectID);
            return ServiceUtils.GetCompleteTasksQuery(query, showCompleted, showDue);
        }

        public IOrderedQueryable<Task> GetCompletedBlogPosts()
        {
            return ObjectContext.Tasks.Where(t => t.ProjectID == 24).Where(t => t.CompletionDate != null).OrderBy(t => t.CompletionDate);
        }

        public IOrderedQueryable<Task> GetCompletedVideos()
        {
            return ObjectContext.Tasks.Where(t => t.ProjectID == 23).Where(t => t.CompletionDate != null).OrderBy(t => t.CompletionDate);
        }

        public IOrderedQueryable<Task> GetCompletedWebinars()
        {
            return ObjectContext.Tasks.Where(t => t.ProjectID == 25).Where(t => t.CompletionDate != null).OrderBy(t => t.CompletionDate);
        }

        public IQueryable<Task> GetTasksByContext(long userID, long contextID, bool showCompleted, bool showDue)
        {
            var query = ObjectContext.Tasks.Where(t => t.UserID == userID).Where(t => t.ContextID == contextID);
            return ServiceUtils.GetCompleteTasksQuery(query, showCompleted, showDue);
        }

        public IQueryable<Task> GetTasksByContact(long userID, long contactID, bool showCompleted, bool showDue)
        {
            var query = ObjectContext.Tasks.Where(t => t.UserID == userID).Where(t => t.ContactID == contactID);
            return ServiceUtils.GetCompleteTasksQuery(query, showCompleted, showDue);
        }

        public IQueryable<Task> GetTasksDueThisWeek(long userID)
        {
            var weekStartDate = ServiceUtils.GetStartOfCurrentWeek();
            var weekEndDate = ServiceUtils.GetEndOfCurrentWeek();
            return ObjectContext.Tasks.Where(t => t.UserID == userID).Where(t => t.CompletionDate == null).Where(t => t.DueDate > weekStartDate).Where(t => t.DueDate < weekEndDate);
        }

        //public void UpdateTasksDueThisWeek(TasksDueThisWeek taskDueThisWeek)
        //{
        //    var task = ObjectContext.Tasks.Where(t => t.ID == taskDueThisWeek.ID).First();
        //    UpdateTask(task);
        //}

        //public void DeleteTaskDueThisWeek(TasksDueThisWeek taskDueThisWeek)
        //{
        //    var task = ObjectContext.Tasks.Where(t => t.ID == taskDueThisWeek.ID).First();
        //    ObjectContext.Tasks.DeleteOnSubmit(task);
        //}

        //public IOrderedEnumerable<OverdueTasks> GetOverdueTasks(long userID)
        //{
        //    return ObjectContext.GetOverdueTasks(userID).OrderBy(t => t.DueDate);
        //}

        public IOrderedQueryable<Task> GetOverdueTasks(long userID)
        {
            return ObjectContext.Tasks.Where(t => t.UserID == userID).Where(t => t.DueDate < DateTime.Now).Where(t => t.CompletionDate == null).OrderBy(t => t.DueDate);
        }

        public void InsertTask(Task task)
        {
            task.CreatedDate = DateTime.Now;
            task.ModifiedDate = DateTime.Now;
            if ((task.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(task, EntityState.Added);
            }
            else
            {
                ObjectContext.Tasks.AddObject(task);
            }
        }

        public void UpdateTask(Task task)
        {
            task.ModifiedDate = DateTime.Now;
            ObjectContext.Tasks.AttachAsModified(task, ChangeSet.GetOriginal(task));
        }

        public void DeleteTask(Task task)
        {
            var notes = GetTaskNotes(task.ID);
            foreach (var note in notes)
            {
                DeleteTaskNote(note);
            }
            if ((task.EntityState == EntityState.Detached))
            {
                ObjectContext.Tasks.Attach(task);
            }
            ObjectContext.Tasks.DeleteObject(task);
        }

        public IQueryable<TaskNote> GetTaskNotes(long taskID)
        {
            return ObjectContext.TaskNotes.Where(n => n.TaskID == taskID);
        }

        public void InsertTaskNote(TaskNote taskNote)
        {
            taskNote.CreatedDate = DateTime.Now;
            if ((taskNote.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(taskNote, EntityState.Added);
            }
            else
            {
                ObjectContext.TaskNotes.AddObject(taskNote);
            }
        }

        public void DeleteTaskNote(TaskNote note)
        {
            if ((note.EntityState == EntityState.Detached))
            {
                ObjectContext.TaskNotes.Attach(note);
            }
            ObjectContext.TaskNotes.DeleteObject(note);
        }

        public IQueryable<TaskCategory> GetTaskCategories()
        {
            return ObjectContext.TaskCategories;
        }

        public IQueryable<TaskItUser> GetUsers()
        {
            return ObjectContext.TaskItUsers;
        }

        public void InsertUser(TaskItUser user)
        {
            user.CreatedDate = DateTime.Now;
            user.ModifiedDate = DateTime.Now;
            if ((user.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Added);
            }
            else
            {
                ObjectContext.TaskItUsers.AddObject(user);
            }
        }

        public void UpdateUser(TaskItUser user)
        {
            user.ModifiedDate = DateTime.Now;
            ObjectContext.TaskItUsers.AttachAsModified(user, ChangeSet.GetOriginal(user));
        }

        public void DeleteUser(TaskItUser user)
        {
            if ((user.EntityState == EntityState.Detached))
            {
                ObjectContext.TaskItUsers.Attach(user);
            }
            ObjectContext.TaskItUsers.DeleteObject(user);
        }

        //public ObjectResult<TasksOverview> GetTasksOverview(long userID)
        //{
        //    return ObjectContext.TasksOverview(userID);
        //}
        
        public int GetTaskCountForProject(long id)
        {
            return ObjectContext.Tasks.Where(t => t.ProjectID == id).Count();
        }

        public int GetTaskCountForContext(long id)
        {
            return ObjectContext.Tasks.Where(t => t.ContextID == id).Count();
        }

        public int GetTaskCountForContact(long id)
        {
            return ObjectContext.Tasks.Where(t => t.ContactID == id).Count();
        }

        public void Tweet(string username, string password, string message)
        {
            // encode the username/password
            var user = Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password));
            // determine what we want to upload as a status
            var parameters = string.Format("status={0}&source={1}", HttpUtility.HtmlEncode(message), "TaskIt");
            var bytes = Encoding.ASCII.GetBytes(parameters);
            // connect with the update page
            var request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
            // set the method to POST
            request.Method = "POST";
            // thanks to argodev for this recent change!
            request.ServicePoint.Expect100Continue = false;
            // set the authorisation levels
            request.Credentials = new NetworkCredential(username, password);
            //request.Headers.Add("Authorization", "Basic " + user);
            request.ContentType = "application/x-www-form-urlencoded";
            // set the length of the content
            request.ContentLength = bytes.Length;
            // set up the stream
            var reqStream = request.GetRequestStream();
            // write to the stream
            reqStream.Write(bytes, 0, bytes.Length);
            // close the stream
            reqStream.Close();

            try
            {
                request.GetResponse();
            }
            catch (Exception ex)
            {
                throw new Exception("Error tweeting " + ex.Message);
            }
        }
    }
}