using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Services;
using System.Web.Mail;
using edu.jhu.Cas.Jobs;
using edu.jhu.Cas.Util;
using edu.jhu.Cas.Servers;
namespace edu.jhu.Cas.Users {
    [WebService(Namespace = "http://Users.Cas.jhu.edu", Name = "CasGroups", Description = "An interface for user administrative operations on a jobsDB")]
    public class CasGroups : System.Web.Services.WebService {
        [WebMethod(Description = "Create a new group")]
        public long CreateGroup(long wsid, string groupName, string description) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GROUP_NAME, groupName);
            object exists = SqlFactory.scalar(job, JobGroups.CreateGroupStatement1);
            if (exists != null) throw new Exception("A group called " + groupName + " already exists");
            long gid = groupName.GetHashCode() + (description.GetHashCode() << 32);
            if (gid < 0) gid = gid * -1;
            job.SetValue(JobGroups.GID, gid);
            job.SetValue(JobGroups.OWNER, wsid);
            job.SetValue(JobGroups.DESCRIPTION, description);
            try {
                SqlFactory.insert(job, JobGroups.CreateGroupStatement2);
            } catch { throw new Exception("Group creation failed"); }
            InviteUser(wsid, wsid, gid);
            AcceptInvite(wsid, gid);
            return gid;
        }
        public edu.jhu.Cas.Util.Group[] FindGroups(long wsid, string predicate) {
            return DataConverter.dataSet2Group(FindGroupsDataSet(wsid, predicate, true));
        }
        public edu.jhu.Cas.Util.Group[] FindGroups(long wsid, string predicate, bool withPublic) {
            return DataConverter.dataSet2Group(FindGroupsDataSet(wsid, predicate, withPublic));
        }
        public DataSet FindGroupsDataSet(long wsid, string predicate) {
            return FindGroupsDataSet(wsid,predicate,true);
        }
        /**
         * searches all if wsid is admin, otherwise only groups of which wsid is member
         */
        public DataSet FindGroupsDataSet(long wsid, string predicate, bool withPublic) {
            CasUsers cUsers = new CasUsers();
            JobGroups job = new JobGroups();
            string statement = JobGroups.FindGroupsStatement;
            try {
                cUsers.HasPrivilege(wsid, "admin");
            } catch {
                statement += " and webservicesid = " + wsid;
            }
            if (predicate != null && predicate.Length > 1)
                statement += " and " + predicate;
            if (withPublic)
                statement += " union select * from groups where gid = " + ID.PUBLIC_GID;
            return SqlFactory.select(job, statement);
        }
        //returns a list of groups a table is published to
        public edu.jhu.Cas.Util.Group[] TablePublishedTo(long wsid, string tableName){
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.TABLE_NAME, tableName);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            try {
                DataSet ds = SqlFactory.select(job, JobGroups.TablePublishedToStatement);
                return DataConverter.dataSet2Group(ds);
            } catch { throw new Exception("Invitation could not be accepted"); }
        }
        [WebMethod(Description = "Invite a user to a group.")]
        /**
         * might want to add something so this only updates a row instead of 
         * adding a new one if a user has been previously removed from a group
         */
        public void InviteUser(long wsid, long inviteThis, long toThis) {
            if (!IsOwner(wsid, toThis))
                throw new CasJobsException("Only a group's owner may invite users");
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, toThis);
            job.SetValue(JobGroups.WEB_SERVICES_ID, inviteThis);
            try {
                SqlFactory.insert(job, JobGroups.InviteUserStatement);
            } catch { throw new Exception("User invitation failed"); }
        }
        [WebMethod(Description = "Remove user from group.")]
        public void RemoveUser(long wsid, long removeThis, long fromThis) {
            if (fromThis == ID.PUBLIC_GID)
                throw new CasJobsException("You may not unsubscribe from the public group.");
            if(wsid != removeThis)
                if (!IsOwner(wsid, fromThis))
                    throw new CasJobsException("You must be a groups owner to remove other users from a group.");
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, fromThis);
            job.SetValue(JobGroups.WEB_SERVICES_ID, removeThis);
            try {
                SqlFactory.insert(job, JobGroups.RemoveUserStatement);
            } catch {throw new Exception("User could not be removed."); }
        }
        [WebMethod(Description = "Accept group invitation")]
        public void AcceptInvite(long wsid, long gid) {          
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            try {
                SqlFactory.insert(job, JobGroups.AcceptInviteStatement);
            } catch{ throw new Exception("Invitation could not be accepted"); }
        }
        [WebMethod(Description = "Decline group invitation")]
        public void DeclineInvite(long wsid, long gid) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            try {
                SqlFactory.insert(job, JobGroups.DeclineInviteStatement);
            } catch{ throw new Exception("Invitation could not be declined"); }
        }
        [WebMethod(Description = "Publish table")]
        public void PublishTable(long wsid, long gid, string tableName) {
            //need to publish a table to a specific group
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.TABLE_NAME, tableName);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            job.SetValue(JobGroups.GID, gid);
            try {
                SqlFactory.insert(job, JobGroups.PublishTableStatement);
            } catch {
                throw new Exception("Could not publish " + tableName + ".  This table may already be published.");
            }
        }
        [WebMethod(Description = "unPublish table")]
        public void DePublishTable(long wsid, long gid, string tableName) {
            //same deal
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.TABLE_NAME, tableName);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            job.SetValue(JobGroups.GID, gid);
            try {
                SqlFactory.insert(job, JobGroups.DePublishTableStatement);
            } catch{throw new Exception("Table not be depublished"); }
        }
        
        [WebMethod(Description = "")]
        public bool IsOwner(long wsid, long gid) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            Object test = null;
            try {
                test = SqlFactory.scalar(job, JobGroups.IsOwnerStatement);
            } catch { throw new Exception("IsOwner failed"); }
            return test != null && test.ToString().Length > 1;
        }

        public bool IsMember(long wsid, long gid) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            Object test = null;
            try {
                test = SqlFactory.scalar(job, JobGroups.IsMemberStatement);
            } catch { throw new Exception("IsOwner failed"); }
            bool isMember = test != null && test.ToString().Length > 1;
            if (isMember)
                return true;
            else
                return IsOwner(wsid, gid);
        }
        [WebMethod(Description = "Must be owner or admin")]
        public void DestroyGroup(long wsid, long gid) {
            if (!IsOwner(wsid, gid))
                (new CasUsers()).HasPrivilege(wsid, "admin");
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            try {
                SqlFactory.insert(job, JobGroups.DestroyGroupsMembersStatement);
                SqlFactory.insert(job, JobGroups.DestroyGroupsGroupsStatement);
                SqlFactory.insert(job, JobGroups.DestroyGroupsTablesStatement);
            } catch { throw new Exception("Could not destroy group."); }
        }
        [WebMethod(Description = "returns true if a user can access this table, false if not")]
        public bool CanReadTable(long borrower, long lender, string lendedTable) {
            //is borrower a member of gid?
            //is lender a member of gid?
            //is lendedtable one of lenders tables?
            //check for existance, if not, remove table from group tables.
            if (borrower == lender) return true;
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.BORROWER_WEB_SERVICES_ID, borrower);
            job.SetValue(JobGroups.WEB_SERVICES_ID, lender);
            job.SetValue(JobGroups.TABLE_NAME, lendedTable);
            int i = (int)SqlFactory.scalar(job, JobGroups.CanReadTableStatement);
            switch (i) {
                case 0: return false;
                default:
                    return true;
             }
        }
        public void SetPublishedTableDescription(long wsid, string tableName, string description) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.DESCRIPTION, description);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            job.SetValue(JobGroups.TABLE_NAME, tableName);
            SqlFactory.insert(job, JobGroups.SetPublishedTableDescriptionStatement);
        }
        public string GetPublishedTableDescription(long wsid, string tableName) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.TABLE_NAME, tableName);
            job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
            Object o = SqlFactory.scalar(job, JobGroups.GetPublishedTableDescriptionStatement);
            if (o != null) return o.ToString();
            else return null;
        }

        public string GetPublishedTableDescription(string userID, string tableName) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.TABLE_NAME, tableName);
            job.SetValue(JobGroups.USERID, userID);
            Object o = SqlFactory.scalar(job, JobGroups.GetPublishedTableDescriptionStatementID);
            if (o != null) return o.ToString();
            else return null;
        }
        public string GetGroupDescription(long gid) {
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            return SqlFactory.scalar(job, JobGroups.GetGroupDescriptionStatement).ToString();
        }
        public void SetGroupDescription(long wsid, long gid, string description) {
            if (!IsOwner(wsid, gid))
                throw new Exception("You must be the owner of a group to set its description");
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            job.SetValue(JobGroups.DESCRIPTION, description);
            SqlFactory.insert(job, JobGroups.SetGroupDescriptionStatement);
        }
        /**
         * first row is the admin (i think)
         */
        public DataSet ListGroupMembers(long wsid, long gid) {
            if (gid == ID.PUBLIC_GID) {
                return SqlFactory.select("select userid, name from users where gvisible = 1");                
            }
            else {
                if (!IsMember(wsid, gid))
                    throw new Exception("You must be a member of a group to view its members.");
                JobGroups job = new JobGroups();
                job.SetValue(JobGroups.GID, gid);
                job.SetValue(JobGroups.WEB_SERVICES_ID, wsid);
                return SqlFactory.select(job, JobGroups.ListGroupMembersStatement);
            }
        }
        /** 
         * anyone should be able to run this, i think
         */
        public string GetGroupAdmin(long gid) {
            if (gid == ID.PUBLIC_GID) return "No admin";
            JobGroups job = new JobGroups();
            job.SetValue(JobGroups.GID, gid);
            return SqlFactory.scalar(job, JobGroups.GetGroupAdminStatement).ToString();
        }
    }
}
