package com.wifiscanner.service;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiManager;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
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.Bundle;
import android.os.ResultReceiver;

import com.wifiscanner.listener.WifiP2PConnectionCallback;
import com.wifiscanner.receiver.WiFiDirectBroadcastReceiver;
import com.wifiscanner.server.ServerDataService;
import com.wifiscanner.tasks.DataServerTask;
import com.wifiscanner.utils.PreferenceUtils;
import com.wifiscanner.utils.Utils;

import java.lang.reflect.Method;

import static com.wifiscanner.WifiConstants.ACTION_SEND_FILE;
import static com.wifiscanner.WifiConstants.DEFAULT_WIFI_PORT;
import static com.wifiscanner.WifiConstants.EXTRAS_DATA;
import static com.wifiscanner.WifiConstants.EXTRAS_GROUP_OWNER_ADDRESS;
import static com.wifiscanner.WifiConstants.EXTRAS_GROUP_OWNER_PORT;
import static com.wifiscanner.WifiConstants.EXTRAS_MESSAGE;
import static com.wifiscanner.WifiConstants.EXTRAS_RESULT_RECEIVER;
import static com.wifiscanner.WifiConstants.PREFS_CLIENT_KEY;
import static com.wifiscanner.WifiConstants.RECEIVER_PREFS_VALUE;
import static com.wifiscanner.WifiConstants.SENDER_PREFS_VALUE;
import static com.wifiscanner.WifiConstants.STATUS_CONNECTING;
import static com.wifiscanner.WifiConstants.STATUS_FAILURE;
import static com.wifiscanner.WifiConstants.STATUS_SUCCESS;

public class WifiP2PServiceImpl implements WifiP2PService {

    private Activity activity;

    private WifiP2pManager manager;
    private WifiManager wifiManager;
    private WifiP2pManager.Channel channel;

    private BroadcastReceiver wifiDirectBroadcastReceiver;
    private WifiP2PConnectionCallback wifiP2PConnectionCallback;

    private WifiP2PServiceImpl(Builder builder) {
        this.activity = builder.activity;
        this.wifiP2PConnectionCallback = builder.wifiP2PConnectionCallback;
        PreferenceUtils.setStringValues(activity, PREFS_CLIENT_KEY, builder.prefsKey);
    }

    @Override
    public synchronized void onCreate() {
        manager = (WifiP2pManager) activity.getSystemService(Context.WIFI_P2P_SERVICE);
        channel = manager.initialize(activity.getApplicationContext(), activity.getMainLooper(), null);
        this.wifiManager = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    }

    @Override
    public synchronized void onResume() {
        if (!wifiManager.isWifiEnabled()) wifiManager.setWifiEnabled(true);
        wifiDirectBroadcastReceiver = new WiFiDirectBroadcastReceiver(this);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
        activity.registerReceiver(wifiDirectBroadcastReceiver, intentFilter);
    }

    @Override
    public synchronized void onStop() {
        try {
            activity.unregisterReceiver(wifiDirectBroadcastReceiver);
            deletePersistentGroups();
            disconnectDevice();
        } catch (Exception e) {}
    }

    @Override
    public synchronized void onDestroy() {

    }

    @Override
    public synchronized void handleOnWifiStateChanged(int state) {
        if(wifiP2PConnectionCallback != null) {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    wifiP2PConnectionCallback.onInitiateDiscovery();
                }
            });
        }
        initiateDiscovery();
    }

    @Override
    public synchronized void initiateDiscovery() {
        manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                if(wifiP2PConnectionCallback != null) {
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            wifiP2PConnectionCallback.onDiscoverySuccess();
                        }
                    });
                }
            }

            @Override
            public void onFailure(int i) {
                if(wifiP2PConnectionCallback != null) {
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            wifiP2PConnectionCallback.onDiscoveryFailure();
                        }
                    });
                }
            }
        });
    }

    @Override
    public synchronized void connectDevice(WifiP2pDevice wifiP2pDevice) {
        WifiP2pConfig config = new WifiP2pConfig();
        config.deviceAddress = wifiP2pDevice.deviceAddress;
        config.wps.setup = WpsInfo.PBC;
        config.groupOwnerIntent = 15;
        manager.connect(channel, config, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {}
            @Override
            public void onFailure(int reason) {
                if(wifiP2PConnectionCallback != null) {
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            wifiP2PConnectionCallback.onPeerConnectionFailure();
                        }
                    });
                }
            }
        });
    }

    @Override
    public synchronized void disconnectDevice() {
        if(manager == null) return;
        manager.removeGroup(channel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {}
            @Override
            public void onFailure(int i) {
                if(wifiP2PConnectionCallback != null) {
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            wifiP2PConnectionCallback.onPeerDisconnectionFailure();
                        }
                    });
                }
            }
        });
    }

    @Override
    public synchronized void handleOnPeersChangedResponse() {
        manager.requestPeers(channel, new WifiP2pManager.PeerListListener() {
            @Override
            public void onPeersAvailable(final WifiP2pDeviceList wifiP2pDeviceList) {
                if(wifiP2PConnectionCallback != null) {
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            wifiP2PConnectionCallback.onPeerAvailable(wifiP2pDeviceList);
                        }
                    });
                }
            }
        });
    }

    @Override
    public synchronized void handleOnPeerConnected() {
        if(wifiP2PConnectionCallback != null) {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    wifiP2PConnectionCallback.onPeerConnectionSuccess();
                    boolean isClient = RECEIVER_PREFS_VALUE.equalsIgnoreCase(PreferenceUtils.getStringValues(activity, PREFS_CLIENT_KEY));
                    if(isClient) startDataTransfer(null);
                }
            });
        }
    }

    @Override
    public synchronized void handleOnPeerDisconnected() {
        if(wifiP2PConnectionCallback != null) {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    wifiP2PConnectionCallback.onPeerDisconnectionSuccess();
                }
            });
        }
    }

    @Override
    public synchronized void handleOnDeviceStatusChanged(WifiP2pDevice wifiP2pDevice) {
        if(wifiP2pDevice.status == WifiP2pDevice.CONNECTED) {
//            handleOnPeerConnected();
        }
    }

    @Override
    public void startDataTransfer(final String message) {
        manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
            @Override
            public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {
                boolean isGroupOwner = wifiP2pInfo.groupFormed && wifiP2pInfo.isGroupOwner;
                boolean isClient = RECEIVER_PREFS_VALUE.equalsIgnoreCase(PreferenceUtils.getStringValues(activity, PREFS_CLIENT_KEY));

                if(isClient && isGroupOwner) {
                    handleOnPeerServer(wifiP2pInfo);

                } else if(!isClient && !isGroupOwner && message != null) {
                    handleOnPeerClient(wifiP2pInfo, message);
                }
            }
        });
    }

    @Override
    public void handleOnPeerServer(WifiP2pInfo wifiP2pInfo) {
        new DataServerTask(wifiP2PConnectionCallback, DEFAULT_WIFI_PORT).execute();
    }

    @Override
    public void handleOnPeerClient(WifiP2pInfo wifiP2pInfo, String message) {
        Intent serviceIntent = new Intent(activity, ServerDataService.class);
        serviceIntent.setAction(ACTION_SEND_FILE);
        serviceIntent.putExtra(EXTRAS_DATA, message);
        serviceIntent.putExtra(EXTRAS_GROUP_OWNER_ADDRESS, wifiP2pInfo.groupOwnerAddress == null ? "" : wifiP2pInfo.groupOwnerAddress.getHostAddress());
        serviceIntent.putExtra(EXTRAS_GROUP_OWNER_PORT, DEFAULT_WIFI_PORT);
        serviceIntent.putExtra(EXTRAS_RESULT_RECEIVER, new ResultReceiver(null) {
            @Override
            protected void onReceiveResult(int resultCode, final Bundle resultData) {
                if(resultCode == DEFAULT_WIFI_PORT && resultData != null) {
                    int status = Integer.valueOf(String.valueOf(resultData.get(EXTRAS_MESSAGE)));
                    switch (status) {
                        case STATUS_CONNECTING:
                            activity.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    wifiP2PConnectionCallback.onDataTransferring();
                                }
                            });
                            break;

                        case STATUS_SUCCESS:
                            activity.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    wifiP2PConnectionCallback.onDataTransferredSuccess();
                                }
                            });
                            break;

                        case STATUS_FAILURE:
                            activity.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    wifiP2PConnectionCallback.onDataTransferredFailure();
                                }
                            });
                            break;
                    }
                }
            }
        });
        activity.startService(serviceIntent);
    }


    private void deletePersistentGroups() {
        try {
            Method[] methods = WifiP2pManager.class.getMethods();
            for (int i = 0; i < methods.length; i++) {
                if (methods[i].getName().equals("deletePersistentGroup")) {
                    // Delete any persistent group
                    for (int netid = 0; netid < 32; netid++) {
                        methods[i].invoke(manager, channel, netid, null);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class Builder {
        private String prefsKey;
        private Activity activity;
        private WifiP2PConnectionCallback wifiP2PConnectionCallback;

        public Builder setSender(Activity activity) {
            this.activity = activity;
            this.prefsKey = RECEIVER_PREFS_VALUE;
            return this;
        }

        public Builder setReceiver(Activity activity) {
            this.activity = activity;
            this.prefsKey = SENDER_PREFS_VALUE;
            return this;
        }

        public Builder setWifiP2PConnectionCallback(WifiP2PConnectionCallback wifiP2PConnectionCallback) {
            this.wifiP2PConnectionCallback = wifiP2PConnectionCallback;
            return this;
        }

        public WifiP2PServiceImpl build() {
            return new WifiP2PServiceImpl(this);
        }
    }
}