package de.lukaskoerfer.p2pchat;

import android.app.Service;
import android.content.Intent;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import de.lukaskoerfer.p2pchat.helper.WifiP2pInfoHelper;
import de.lukaskoerfer.p2pchat.data.BaseMessage;
import de.lukaskoerfer.p2pchat.data.ChatMessage;
import de.lukaskoerfer.p2pchat.connection.ChatClient;
import de.lukaskoerfer.p2pchat.connection.ChatConnection;
import de.lukaskoerfer.p2pchat.connection.ChatServer;
import de.lukaskoerfer.p2pchat.data.LeaveMessage;
import de.lukaskoerfer.p2pchat.data.JoinMessage;

public class P2pChatService extends Service implements WifiP2pManager.PeerListListener, WifiP2pManager.ConnectionInfoListener {

    private P2pChatApplication Application;

    public P2pChatService() {

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.Application = (P2pChatApplication) getApplication();
        switch (intent.getAction()) {
            case "STATE_CHANGED":
                this.IsP2pEnabled = intent.getBooleanExtra("P2P_ENABLED", false);
                // update error state
                break;
            case "PEERS_CHANGED":
                Log.d("P2P", "Request peers info");
                this.Application.P2pHandler.Manager.requestPeers(this.Application.P2pHandler.Channel, this);
                break;
            case "CONNECTION_CHANGED":
                Log.d("P2P", "Request conn info");
                this.Application.P2pHandler.Manager.requestConnectionInfo(this.Application.P2pHandler.Channel, this);
                break;
            default:
                break;
        }
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        switch (intent.getAction()) {
            case "PEER_ACTIVITY":
                return this.PeerActivityBinder;
            case "CHAT_ACTIVITY":
                return this.ChatActivityBinder;
            default:
                return null;
        }
    }

    private final PeerBinder PeerActivityBinder = new PeerBinder();
    private final ChatBinder ChatActivityBinder = new ChatBinder();

    public class PeerBinder extends Binder {

        public PeerActivity Activity = null;

        public boolean isRegistered() {
            return this.Activity != null;
        }

        public void registerActivity(PeerActivity activity) {
            this.Activity = activity;
        }

        public void unregisterActivity() {
            this.Activity = null;
        }

        public List<PeerDevice> getDevices() {
            List<PeerDevice> devices = new ArrayList<>();
            for (WifiP2pDevice p2pDevice : P2pChatService.this.Devices) {
                ClientInfo comparable = ClientInfo.GetComparable(p2pDevice.deviceAddress);
                PeerDevice device = new PeerDevice(p2pDevice, P2pChatService.this.Clients.contains(comparable));
                devices.add(device);
            }
            return devices;
        }
    }

    private void updateDevices() {
        if (this.PeerActivityBinder.isRegistered()) {
            this.PeerActivityBinder.Activity.updateDevices();
        } else {
            // inform via notification
        }
    }

    public class ChatBinder extends Binder {

        public ChatActivity Activity = null;

        public boolean isRegistered() {
            return this.Activity != null;
        }

        public void registerActivity(ChatActivity activity) {
            this.Activity = activity;
        }

        public void unregisterActivity() {
            this.Activity = null;
        }

        public int getConnectionState() {
            return 0;
        }

        public List<ClientInfo> getJoinedClients() {
            return P2pChatService.this.Clients;
        }

        public List<BaseMessage> checkoutMessages() {
            int messageCount = P2pChatService.this.Messages.size();
            List<BaseMessage> messages = P2pChatService.this.Messages.subList(0, messageCount - 1);
            P2pChatService.this.Messages.clear();
            return messages;
        }

        public boolean sendMessage(ChatMessage message) {
            if (P2pChatService.this.isConnected()) {
                P2pChatService.this.Connection.SendMessage(message);
                return true;
            } else {
                return false;
            }
        }
    }

    private int getErrorState() {
        return 0;
    }

    private boolean IsP2pEnabled = false;

    private ChatConnection Connection;

    private boolean isConnected() {
        return ((this.Connection != null) && this.Connection.IsConnected);
    }

    private List<WifiP2pDevice> Devices = new ArrayList<>();

    @Override
    public void onPeersAvailable(WifiP2pDeviceList deviceList) {
        Collection<WifiP2pDevice> foundDevices = deviceList.getDeviceList();
        boolean devicesRemoved = this.Devices.retainAll(foundDevices);
        boolean devicesAdded = this.Devices.addAll(foundDevices);
        if (devicesRemoved || devicesAdded) {
            this.updateDevices();
        }
    }

    private WifiP2pInfo PrevP2pInfo;

    @Override
    public void onConnectionInfoAvailable(WifiP2pInfo p2pInfo) {
        Log.d("P2P", "Received info");
        if (!WifiP2pInfoHelper.areInfosEquals(this.PrevP2pInfo, p2pInfo)) {
            Log.d("P2P", "New info");
            // stop possible previous connection
            if (this.Connection != null) {
                this.Connection.Stop();
            }
            if (p2pInfo != null && p2pInfo.groupFormed) {
                // start new connection
                if (p2pInfo.isGroupOwner) {
                    Log.d("P2P", "I'm master");
                    this.Connection = new ChatServer(this.MessageReceiver);
                    Log.d("P2P", "Server started");
                } else {
                    Log.d("P2P", "I'm servant");
                    ClientInfo info = new ClientInfo("deviceAddress", "user");
                    this.Connection = new ChatClient(p2pInfo.groupOwnerAddress, this.MessageReceiver, info);
                    Log.d("P2P", "Client started");
                }
            }
        }
        this.PrevP2pInfo = p2pInfo;
    }

    private List<ClientInfo> Clients = new ArrayList<>();
    private List<BaseMessage> Messages = new ArrayList<>();

    private ReceiveCallback MessageReceiver = new ReceiveCallback() {
        @Override
        public void ReceiveMessage(BaseMessage message) {
            if (message instanceof JoinMessage) {
                ClientInfo client = ((JoinMessage)message).getClient();
                if (!P2pChatService.this.Clients.contains(client)) {
                    P2pChatService.this.Clients.add(client);
                }
            } else if (message instanceof LeaveMessage) {
                ClientInfo client = ((LeaveMessage)message).getClient();
                if (!P2pChatService.this.Clients.contains(client)) {
                    P2pChatService.this.Clients.remove(client);
                }
            }
            P2pChatService.this.Messages.add(message);
            final String s = message.serialize();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(P2pChatService.this, s, Toast.LENGTH_LONG).show();
                }
            });
        }
    };

    private void runOnUiThread(Runnable runnable) {
        new Handler(Looper.getMainLooper()).post(runnable);
    }
}