using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using edu.jhu.Cas.Util;
using edu.jhu.Cas.Users;
namespace edu.jhu.Cas.Servers {
	/**
	 * don't think this should be a web service
	 */
	public class CasServers {
        public Server[] FindServers(long wsid, long usrwsid) {
            return DataConverter.dataSet2Server(FindServerDataSet(wsid, usrwsid));
        }
		public DataSet FindServerDataSet(long wsid, long usrwsid) {
			(new CasUsers()).HasPrivilege(wsid, "admin");
			JobServers job = new JobServers();
            job.SetValue(JobServers.WEB_SERVICES_ID, usrwsid);
            string qry = usrwsid == ID.SERVICE_ID?JobServers.FindAdminServersStatement:JobServers.FindUserServersStatement;
            DataSet dataSet = SqlFactory.select(job, qry);
            return dataSet;
        }
        public Server[] FindServerByTarget(long wsid, long usrwsid, string target) {
            //do this programatically to avoid having similar queries
            DataSet ds = FindServerDataSet(wsid, usrwsid);
            target = target.ToLower();
            for (int i = 0; i < ds.Tables[0].Rows.Count; i++) {
                if (ds.Tables[0].Rows[i]["Target"].ToString().ToLower() != target) {
                    ds.Tables[0].Rows.RemoveAt(i);
                    --i;
                }
            }
            return DataConverter.dataSet2Server(ds);
        }
        public Server[] FindNonQuickQueues(long wsid) {
            return DataConverter.dataSet2Server(SqlFactory.select(JobServers.NonQuickQueuesStatement));
        }
        //returns a dataset with target/queue --> running/waiting queries
        public DataSet FetchLoads(long wsid) {
			string qry = @"
		select s.target+'/'+CAST(s.queue as varchar(12)) as queue,count(j.jobid) as count from jobs j, servers s where (status = 1 or status = 0)
		and s.name = j.hostip
		group by s.target,s.name,s.target,s.queue
";
			return SqlFactory.select(qry);
		}
        //return 2 lowest queued servers, or one server if there's only one
        public Server[] GetTwoLowestQueues(long wsid, string context) {
            try {
                JobServers job = new JobServers();
                job.SetValue(JobServers.TARGET, context);
                DataSet ds = SqlFactory.select(job, JobServers.MinTwoQueuesStatement);
                if (ds.Tables[0].Rows.Count == 2)
                    return DataConverter.dataSet2Server(ds);
                else {//there must be only one entry, just return that
                    Server[] rst = FindServerByTarget(ID.SERVICE_ID, wsid, context);
                    if (rst.Length < 1)
                        throw new Exception("'" + context + "' does not exist");
                    return rst;
                }
            }
            catch {
                throw new CasJobsException("'" + context + "' is not a valid context.");
            }
        }
        private const string UserTargetsQry = @"
select distinct target from (
    select s.* from servers s, (
	    select g.privs from groups g,(
		    select distinct gid from groupmembers
		    where webservicesid = @WSID  and
		    status = 2
	    ) gm
	    where gm.gid = g.gid and
	    g.privs is not null
    ) gg
    where (charindex(s.privs,gg.privs) > 0)
    union
    select * from servers
    where privs is null
) z";
        private const string AdminTargetsQyr = @"
select distinct target from servers
";
        private static string[] TargetsQryParams = new string[1] { "@WSID" };
        private static SqlDbType[] TargetsQryTypes = new SqlDbType[1] { SqlDbType.BigInt};
        private static int[] TargetsQrySizes = new int[1] { 64 };
		public Server[] FindTargets(long wsid,long usrwsid){
            if (usrwsid == ID.SERVICE_ID) {
                return DataConverter.dataSet2Server(SqlFactory.select(AdminTargetsQyr));
            }else{
                return DataConverter.dataSet2Server(
                    SqlFactory.select_param(UserTargetsQry, TargetsQryParams, TargetsQryTypes, TargetsQrySizes, new object[1] { usrwsid })
                );
            }
		}
        public void CheckContextAccess(long wsid, string context) {
            context = context.ToUpper();
            foreach (Server s in FindTargets(ID.SERVICE_ID, wsid))
                if (s.Target.ToUpper().CompareTo(context) == 0)
                    return;
            throw new Exception("Context '"+context+"' does not exist or you do not have access to it.");
        }
		
		public Server[] FindDistinctOwnerServers(long wsid) {
			(new CasUsers()).HasPrivilege(wsid, "admin");
			JobServers job = new JobServers();
			job.SetValue(JobServers.WEB_SERVICES_ID, wsid);
			string qry = "select distinct usr from servers";
			DataSet dataSet = SqlFactory.select(job, qry);
			return DataConverter.dataSet2Server(dataSet);
        }
        public Server ClosestQueue(long wsid, long usrwsid, string target, long estimate) {
            (new CasUsers()).HasPrivilege(wsid, "admin");
            CheckContextAccess(usrwsid, target);
            JobServers job = new JobServers();
            job.SetValue(JobServers.TARGET, target);
            job.SetValue(JobServers.ESTIMATE, estimate);
            try {
                DataSet ds = SqlFactory.select(job, JobServers.ClosestQueueStatement);
                return DataConverter.dataSet2Server(ds)[0];
            }
            catch {
                throw new CasJobsException("Queue target '" + target + "' could not be found.");
            }
        }
        public bool IsInLowestQueue(long wsid, int q, string targ) {
            Server[] ss = GetTwoLowestQueues(wsid, targ);
            return q <= ss[0].Queue;
        }
	}
}
