//
// Session.cs
//
// Copyright (c) 2007 Lukas Lipka.
//

using System;
using System.Collections.Generic;
using System.Text;

using IcqSharp.Base;
using IcqSharp.Packets;
using IcqSharp.Connections;
using IcqSharp.Util;

namespace IcqSharp
{
    public class Session : IDisposable
    {
        private ContactList contact_list;
        private Messaging messaging;
        private StatusManager status_manager;
        private PrivacyManager privacy_manager;

        private string uin;
        private string password;

        private LoginConnection login_connection;
        private ServerConnection server_connection;

        private bool keepalive;

        // FLAP sequence numbers
        private const ushort sequence_upper_bound = 0x8000;
        private ushort internal_sequence;

        public event EventHandler Connected;
        public event EventHandler Disconnected;
        public event EventHandler Connecting;
        public event EventHandler ConnectionError;
        public event EventHandler LoginFailed;

        public Session (string uin, string password)
        {
            Log.Debug ("Opening new session for: {0}", uin);

            this.uin = uin;
            this.password = password;

            this.contact_list = new ContactList (this);
            this.messaging = new Messaging (this);
            this.status_manager = new StatusManager (this);
            this.privacy_manager = new PrivacyManager (this);

            this.login_connection = new SecureLogin(this, uin, password);
            this.login_connection.ConnectionError += OnConnectionError;
            this.login_connection.LoginComplete += OnLoginComplete;
            this.login_connection.LoginFailed += OnLoginFailed;
        }

        public void Dispose ()
        {
            Disconnect();

            Log.Debug("Dispose session...");
        }

        public void Connect ()
        {
            OnConnecting(this, EventArgs.Empty);
            
            if (server_connection != null)
                server_connection.Disconnect();

            login_connection.Connect();
        }

        public void Disconnect()
        {
            if (login_connection != null)
                login_connection.Disconnect();

            if (server_connection != null)
                server_connection.Disconnect();
        }

        public void Send (FlapPacket p)
        {
            if (server_connection == null)
                return;

            server_connection.Send (p);
        }

        public ushort GetSequenceNumber ()
        {
            return (ushort)(++internal_sequence % sequence_upper_bound);
        }

        private void OnLoginComplete (ServerConnection server_connection)
        {
            this.server_connection = server_connection;
            server_connection.Disconnected += OnDisconnected;
            server_connection.ConnectionError += OnConnectionError;
            server_connection.Connect();
        }

        ///////////////////////////////////////////////////

        public void OnConnected (object o, EventArgs args)
        {
            Log.Debug("EVENT -> Session::OnConnected");

            if (Connected != null)
                Connected (o, args);
        }

        public void OnDisconnected(object o, EventArgs args)
        {
            Log.Debug("EVENT -> Session::OnDisconnected");

            if (Disconnected != null)
                Disconnected (o, args);
        }

        public void OnConnecting(object o, EventArgs args)
        {
            Log.Debug("EVENT -> Session::OnConnecting");

            if (Connecting != null)
                Connecting(o, args);
        }

        public void OnConnectionError(object o, EventArgs args)
        {
            Log.Debug("EVENT -> Session::OnConnectionError");

            if (ConnectionError != null)
                ConnectionError(o, args);
        }

        public void OnLoginFailed(object o, EventArgs args)
        {
            Log.Debug("EVENT -> Session::OnLoginError");

            if (LoginFailed != null)
                LoginFailed(o, args);
        }

        ///////////////////////////////////////////////////

        public string Uin
        {
            get { return uin; }
        }

        public bool IsConnected
        {
            get { return (server_connection != null && server_connection.IsConnected); }
        }

        public ContactList ContactList
        {
            get { return contact_list; }
        }

        public Messaging Messaging
        {
            get { return messaging; }
        }

        public StatusManager StatusManager
        {
            get { return status_manager; }
        }

        public PrivacyManager PrivacyManager
        {
            get { return privacy_manager; }
        }

        public Status Status
        {
            get { return status_manager.Status; }
            set { status_manager.Status = value; }
        }

        public bool Keepalive
        {
            get { return keepalive; }
            set { keepalive = true; }
        }
    }
}
