using System;
using System.Collections;
using Server.Accounting;
using Server.Network;

namespace Server.RemoteAdmin
{
    public class RemoteAdminHandlers
    {
        private static readonly OnPacketReceive[] m_Handlers = new OnPacketReceive[256];
        static RemoteAdminHandlers()
        {
            //0x02 = login request, handled by AdminNetwork
            Register(0x04, new OnPacketReceive(ServerInfoRequest));
            Register(0x05, new OnPacketReceive(AccountSearch));
            Register(0x06, new OnPacketReceive(RemoveAccount));
            Register(0x07, new OnPacketReceive(UpdateAccount));
        }

        public enum AcctSearchType : byte
        {
            Username = 0,
            IP = 1,
        }
        public static void Register(byte command, OnPacketReceive handler)
        {
            m_Handlers[command] = handler;
        }

        public static bool Handle(byte command, NetState state, PacketReader pvSrc)
        {
            if (m_Handlers[command] == null)
            {
                Console.WriteLine("ADMIN: Invalid packet 0x{0:X2} from {1}, disconnecting", command, state);
                return false;
            }
            else
            {
                m_Handlers[command](state, pvSrc);
                return true;
            }
        }

        private static void ServerInfoRequest(NetState state, PacketReader pvSrc)
        {
            state.Send(AdminNetwork.Compress(new ServerInfo()));
        }

        private static void AccountSearch(NetState state, PacketReader pvSrc)
        {
            AcctSearchType type = (AcctSearchType)pvSrc.ReadByte();
            string term = pvSrc.ReadString();

            if (type == AcctSearchType.IP && !Utility.IsValidIP(term))
            {
                state.Send(new MessageBoxMessage("Invalid search term.\nThe IP sent was not valid.", "Invalid IP"));
                return;
            }
            else
            {
                term = term.ToUpper();
            }

            ArrayList list = new ArrayList();

            foreach (Account a in Accounts.GetAccounts())
            {
                if (!CanAccessAccount(state.Account, a))
                    continue;

                switch ( type )
                {
                    case AcctSearchType.Username:
                        {
                            if (a.Username.ToUpper().IndexOf(term) != -1)
                                list.Add(a);
                            break;
                        }
                    case AcctSearchType.IP:
                        {
                            for (int i = 0; i < a.LoginIPs.Length; i++)
                            {
                                if (Utility.IPMatch(term, a.LoginIPs[i]))
                                {
                                    list.Add(a);
                                    break;
                                }
                            }
                            break;
                        }
                }
            }

            if (list.Count > 0)
            {
                if (list.Count <= 25)
                    state.Send(AdminNetwork.Compress(new AccountSearchResults(list)));
                else
                    state.Send(new MessageBoxMessage("There were more than 25 matches to your search.\nNarrow the search parameters and try again.", "Too Many Results"));
            }
            else
            {
                state.Send(new MessageBoxMessage("There were no results to your search.\nPlease try again.", "No Matches"));
            }
        }

        static bool CanAccessAccount(IAccount beholder, IAccount beheld)
        {
            return beholder.AccessLevel == AccessLevel.Owner || beheld.AccessLevel < beholder.AccessLevel;  // Cannot see accounts of equal or greater access level unless Owner
        }

        private static void RemoveAccount(NetState state, PacketReader pvSrc)
        {
            if (state.Account.AccessLevel < AccessLevel.Administrator)
            {
                state.Send(new MessageBoxMessage("You do not have permission to delete accounts.", "Account Access Exception"));
                return;
            }

            IAccount a = Accounts.GetAccount(pvSrc.ReadString());

            if (a == null)
            {
                state.Send(new MessageBoxMessage("The account could not be found (and thus was not deleted).", "Account Not Found"));
            }
            else if (!CanAccessAccount(state.Account, a))
            {
                state.Send(new MessageBoxMessage("You cannot delete an account with an access level greater than or equal to your own.", "Account Access Exception"));
            }
            else if (a == state.Account)
            {
                state.Send(new MessageBoxMessage("You may not delete your own account.", "Not Allowed"));
            }
            else
            {
                RemoteAdminLogging.WriteLine(state, "Deleted Account {0}", a);
                a.Delete();
                state.Send(new MessageBoxMessage("The requested account (and all it's characters) has been deleted.", "Account Deleted"));
            }
        }

        private static void UpdateAccount(NetState state, PacketReader pvSrc)
        { 
            if (state.Account.AccessLevel < AccessLevel.Administrator)
            {
                state.Send(new MessageBoxMessage("You do not have permission to edit accounts.", "Account Access Exception"));
                return;
            }

            string username = pvSrc.ReadString();
            string pass = pvSrc.ReadString();

            Account a = Accounts.GetAccount(username) as Account;

            if (a != null && !CanAccessAccount(state.Account, a))
            {
                state.Send(new MessageBoxMessage("You cannot edit an account with an access level greater than or equal to your own.", "Account Access Exception"));
            }
            else
            {
                bool CreatedAccount = false;
                bool UpdatedPass = false;
                bool oldbanned = a == null ? false : a.Banned;
                AccessLevel oldAcessLevel = a == null ? 0 : a.AccessLevel;

                if (a == null)
                {
                    a = new Account(username, pass);
                    CreatedAccount = true;
                }
                else if (pass != "(hidden)")
                {
                    a.SetPassword(pass);
                    UpdatedPass = true;
                }

                if (a != state.Account)
                {
                    AccessLevel newAccessLevel = (AccessLevel)pvSrc.ReadByte();
                    if (a.AccessLevel != newAccessLevel)
                    {
                        if (newAccessLevel >= state.Account.AccessLevel)
                            state.Send(new MessageBoxMessage("Warning: You may not set an access level greater than or equal to your own.", "Account Access Level update denied."));
                        else
                            a.AccessLevel = newAccessLevel;
                    }
                    bool newBanned = pvSrc.ReadBoolean();
                    if (newBanned != a.Banned)
                    {
                        oldbanned = a.Banned;
                        a.Banned = newBanned;
                        a.Comments.Add(new AccountComment(state.Account.Username, newBanned ? "Banned via Remote Admin" : "Unbanned via Remote Admin"));
                    }
                }
                else
                {
                    pvSrc.ReadInt16();//skip both
                    state.Send(new MessageBoxMessage("Warning: When editing your own account, Account Status and Access Level cannot be changed.", "Editing Own Account"));
                }

                ArrayList list = new ArrayList();
                ushort length = pvSrc.ReadUInt16();
                bool invalid = false;
                for (int i = 0; i < length; i++)
                {
                    string add = pvSrc.ReadString();
                    if (Utility.IsValidIP(add))
                        list.Add(add);
                    else
                        invalid = true;
                }

                if (list.Count > 0)
                    a.IPRestrictions = (string[])list.ToArray(typeof(string));
                else
                    a.IPRestrictions = new string[0];

                if (invalid)
                    state.Send(new MessageBoxMessage("Warning: one or more of the IP Restrictions you specified was not valid.", "Invalid IP Restriction"));

                if (CreatedAccount)
                    RemoteAdminLogging.WriteLine(state, "Created account {0} with Access Level {1}", a.Username, a.AccessLevel);
                else
                {
                    string changes = string.Empty;
                    if (UpdatedPass)
                        changes += " Password Changed.";
                    if (oldAcessLevel != a.AccessLevel)
                        changes = string.Format("{0} Access level changed from {1} to {2}.", changes, oldAcessLevel, a.AccessLevel);
                    if (oldbanned != a.Banned)
                        changes += a.Banned ? " Banned." : " Unbanned.";
                    RemoteAdminLogging.WriteLine(state, "Updated account {0}:{1}", a.Username, changes);
                }

                state.Send(new MessageBoxMessage("Account updated successfully.", "Account Updated"));
            }
        }
    }
}