using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services;
using edu.jhu.Cas.Util;
using edu.jhu.Cas.Users;

namespace edu.jhu.Cas.Services {
    [WebService(Namespace = "http://Services.Cas.jhu.edu", Name = "Servers", Description = "Provides server management functionality.")]
    public class Servers : System.Web.Services.WebService {
        CasUsers cu = new CasUsers();
        [WebMethod(Description = @"
Returns an array of CJServer objects representing all servers that match the criteria in conditions.<br/>
Conditions is a formatted string of the same format of that used in Jobs.FindJobs() (in the jobs.asmx web service).<br/>
Please see Jobs.FindJobs() for further documentation on the conditions string.<br/>
The wsid of the user calling this method must have the admin priv.<br/>
<br/>
The following are valid conditions keys:<br/>
string name : unique identifier of dbmachine/cjqueue assignment<br/>
int timeout : maximum query execution time<br/>
string context : the context this server belongs to<br/>
string dbuser : database user with which to login<br/>
string dbmachine : machine name of the database server<br/>
string dbcatalog : name of the database<br/>
int execcap : maximum number of concurrent executions<br/>
string gpriv : server is only available to users who are a member of a group with this priv<br/>
string dbpw : password with of dbuser<br/>
<br/>
<b>Parameters:</b><br/>
long admin_wsid : WebServicesID of admin account.<br/>
string admin_pw : Password of that account<br/>
string conditions : A formatted string describing the conditions for the search.  See above for details on this.<br/>
<br/>
<b>Output:</b><br/>
CJServer[] : Contains objects matching conditions<br/>
<br/>
A CJServer object contains:<br/>
<i>Fields in CJServer are the same as GetServers() conditions</i><br/>
")]
        public CJServer[] GetServers(long admin_wsid, string admin_pw, string conditions) {
            Users.Authenticate(admin_wsid, admin_pw);
            cu.HasPrivilege(admin_wsid, PRIVS.ADMIN);
            if (conditions == null) conditions = "";
            conditions = conditions.Trim();
            string qry = @"
select [name], [queue] as timeout, [target] as context, [usr] as dbuser, [host] as dbmachine, [cat] as dbcatalog, [execlimit] as execcap, [privs] as gpriv, 
    [cstringextra] as params, [password] as dbpw
from servers";
            if (conditions.Length > 1) {
                qry = qry + "\nwhere ";
                qry = qry + ConditionFactory.ServerConditions2SQL(conditions);
            }
            return DataConverter.dataTable2CJServer(SqlFactory.select(qry).Tables[0]);
        }
        const string UPDATE_QUERY = @"
update servers
set [name] = @NAME,
[queue] = @QUEUE,
[target] = @TARGET,
[usr] = @USR,
[host] = @HOST,
[cat] = @CAT,
[execlimit] = @EXECLIMIT,
[privs] = @PRIVS,
[password] = @PASSWORD 
where [name] = @OLDNAME
";
        const string ADD_QUERY = @"
INSERT INTO servers(name, queue, target, usr, host, cat, execlimit, privs, password)
VALUES (@NAME, @QUEUE, @TARGET, @USR, @HOST, @CAT, @EXECLIMIT, @PRIVS, @PASSWORD)
";
        const string DELETE_QUERY = @"
DELETE FROM servers
where name = @NAME
";
        static readonly string[] UQ_PARAM_NAMES = new string[]{
            "@NAME","@QUEUE","@TARGET","@USR","@HOST","@CAT","@EXECLIMIT","@PRIVS","@PASSWORD","@OLDNAME"
        };
        static readonly string[] DQ_PARAM_NAMES = new string[] {@"NAME"};
        static readonly SqlDbType[] UQ_PARAM_TYPES = new SqlDbType[]{
            SqlDbType.VarChar,SqlDbType.Int,SqlDbType.VarChar,SqlDbType.VarChar,SqlDbType.VarChar,SqlDbType.VarChar,SqlDbType.Int,SqlDbType.VarChar,SqlDbType.VarChar,SqlDbType.VarChar
        };
        static readonly SqlDbType[] DQ_PARAM_TYPES = new SqlDbType[] { SqlDbType.VarChar };
        static readonly int[] UQ_PARAM_SIZES = new int[]{
            200,8,200,50,50,50,8,50,200,200
        };
        static readonly int[] DQ_PARAM_SIZES = new int[]{200};
        [WebMethod(Description = @"
Update server described identified by 'server_name' with information described in 'cjs' param.<br/>  
Fields left null in the cjs object will also be updated as null in the db.<br/>
This method will fail if an attempt is made to set non-nullable fields to null<br/>
This method requires a user with the admin priv<br/>
This method can be used also to add or delete server entries<br/>
If in delete mode, only the 'name' in CJServer is functional; all others are ignored.<br/>
<br/>
<b>Parameters:</b><br/>
long admin_wsid : WebServicesID of admin account.<br/>
string admin_pw : Password of that account<br/>
string server_name : identifier of the server entry to update
CJServer cjs: Object containing server parameters to update.  See GetServers() for CJServer field documentation.<br/>
int mode : The type of operation.  0 will update, 1 will add, 2 will delete.
<br/>
<b>Output:</b><br/>
void if succesful, exceptions if not
")]
        public void UpdateServer(long admin_wsid, string admin_pw, string server_name, CJServer cjs, int mode) {
            Users.Authenticate(admin_wsid, admin_pw);
            cu.HasPrivilege(admin_wsid, PRIVS.ADMIN);
            object[] pvals;
            switch (mode) {
                case 0:
                    pvals = new object[UQ_PARAM_TYPES.Length];
                    pvals[0] = dbd(cjs.Name);
                    pvals[1] = dbd(cjs.TimeOut);
                    pvals[2] = dbd(cjs.Context);
                    pvals[3] = dbd(cjs.DBUser);
                    pvals[4] = dbd(cjs.DBMachine);
                    pvals[5] = dbd(cjs.DBCatalog);
                    pvals[6] = dbd(cjs.ExecCap);
                    pvals[7] = dbd(cjs.GPriv);
                    pvals[8] = dbd(cjs.DBPw);
                    pvals[9] = server_name;
                    SqlFactory.nonselect_param(UPDATE_QUERY, UQ_PARAM_NAMES, UQ_PARAM_TYPES, UQ_PARAM_SIZES, pvals);
                    break;
                case 1:
                    pvals = new object[UQ_PARAM_TYPES.Length];
                    pvals[0] = dbd(cjs.Name);
                    pvals[1] = dbd(cjs.TimeOut);
                    pvals[2] = dbd(cjs.Context);
                    pvals[3] = dbd(cjs.DBUser);
                    pvals[4] = dbd(cjs.DBMachine);
                    pvals[5] = dbd(cjs.DBCatalog);
                    pvals[6] = dbd(cjs.ExecCap);
                    pvals[7] = dbd(cjs.GPriv);
                    pvals[8] = dbd(cjs.DBPw);
                    pvals[9] = server_name;
                    SqlFactory.nonselect_param(ADD_QUERY, UQ_PARAM_NAMES, UQ_PARAM_TYPES, UQ_PARAM_SIZES, pvals);
                    break;
                case 2:
                    pvals = new object[1];
                    pvals[0] = dbd(cjs.Name);
                    SqlFactory.nonselect_param(DELETE_QUERY, DQ_PARAM_NAMES, DQ_PARAM_TYPES, DQ_PARAM_SIZES, pvals);
                    break;
            }
        }
        object dbd(object o) {
            return o != null ? o : DBNull.Value;
        }
    }
}