package hr.fer.labust.diverapp;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import hr.fer.labust.diverapp.common.BluetoothMessage;

public class RemoteDevice {
    private final String mName;
    private final String mAddress;
    private final BluetoothDevice mDevice;
    private boolean mIsBonded;
    private final Handler mBluetoothServiceHandler;
    private final Handler mMainHandler;
    private int mState;

    private ConnectingThread mConnectingThread = null;
    private ConnectedThread mConnectedThread = null;

    public RemoteDevice(BluetoothDevice device, Handler btServiceHandler, Handler mainHandler) {
        mDevice = device;
        mName = device.getName();
        mAddress = device.getAddress();
        mBluetoothServiceHandler = btServiceHandler;
        mMainHandler = mainHandler;

        checkBonded();

        mState = BluetoothService.STATE_NONE;
    }

    private void checkBonded() {
        if (mDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
            mIsBonded = true;
        }
        else {
            mIsBonded = false;
        }
    }

    private synchronized void setState(int state) {
        mState = state;

        mBluetoothServiceHandler.obtainMessage(BluetoothService.STATE_CHANGED, state, 0, mDevice).sendToTarget();
        mMainHandler.obtainMessage(BluetoothService.STATE_CHANGED, state, 0, mDevice).sendToTarget();
    }

    public synchronized void connect(UUID uuid, boolean secure) {
        if (mState == BluetoothService.STATE_NONE) {
            mBluetoothServiceHandler.obtainMessage(BluetoothService.CANCEL_DISCOVERY).sendToTarget();

            setState(BluetoothService.STATE_CONNECTING);
            mConnectingThread = new ConnectingThread(uuid, secure);
            mConnectingThread.start();
        }
    }

    public synchronized void disconnect() {
        if (mConnectingThread != null) {
            mConnectingThread.cancel();
            mConnectingThread = null;
        }

        if (mConnectedThread != null) {
            mConnectedThread.disconnect();
            mConnectedThread = null;
        }
    }

    public synchronized void connected(BluetoothSocket socket) {
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }

    public int getState() {
        return mState;
    }

    public boolean isBonded() {
        return mIsBonded;
    }

    public String getAddress() {
        return mAddress;
    }

    public String getName() {
        return mName;
    }

    public BluetoothDevice getDevice() {
        return mDevice;
    }

    public void send(byte[] buffer) {
        if (mConnectedThread != null) {
            mConnectedThread.write(buffer);
        }
    }

    public void send(String message) {
        if (mConnectedThread != null) {
            mConnectedThread.write(message);
        }
    }

    private class ConnectingThread extends Thread {
        private final BluetoothSocket mSocket;

        ConnectingThread(UUID uuid, boolean secure) {
            BluetoothSocket tempSocket = null;
            try {
                if (secure) {
                    tempSocket = mDevice.createRfcommSocketToServiceRecord(uuid);
                }
                else {
                    tempSocket = mDevice.createInsecureRfcommSocketToServiceRecord(uuid);
                }
            }
            catch(IOException e) {
                setState(BluetoothService.STATE_NONE);
            }

            mSocket = tempSocket;
        }

        @Override
        public void run() {
            try {
                mSocket.connect();
            }
            catch(Exception e) {
                setState(BluetoothService.STATE_NONE);
                mConnectingThread = null;
                return;
            }

            mConnectingThread = null;
            connected(mSocket);
        }

        public void cancel() {
            try {
                mSocket.close();
            }
            catch(Exception e) {
                setState(BluetoothService.STATE_NONE);
            }
        }

    }

    private class ConnectedThread extends Thread {
        private final BluetoothSocket mSocket;
        private final InputStream mInputStream;
        private final OutputStream mOutputStream;
        private boolean disconnect = false;

        ConnectedThread(BluetoothSocket socket) {
            mSocket = socket;
            InputStream tmpInputStream = null;
            OutputStream tmpOutputStream = null;

            try {
                tmpInputStream = mSocket.getInputStream();
                tmpOutputStream = mSocket.getOutputStream();
            }
            catch(IOException e) {
                setState(BluetoothService.STATE_NONE);
            }

            mInputStream = tmpInputStream;
            mOutputStream = tmpOutputStream;
            setState(BluetoothService.STATE_CONNECTED);
        }

        @Override
        public void run() {
            byte[] buffer = new byte[10240];
            int bytes;

            while(true) {
                try {
                    bytes = mInputStream.read(buffer);
                    mMainHandler.obtainMessage(BluetoothService.MESSAGE_RECEIVED,
                            new BluetoothMessage(mDevice, new String(buffer, 0, bytes))).sendToTarget();
                }
                catch(IOException e) {
                    setState(BluetoothService.STATE_NONE);

                    if (!disconnect) {
                        setState(BluetoothService.LOST_CONNECTION);
                    }
                    break;
                }
            }

            mConnectedThread = null;
        }

        public void write(byte[] buffer) {
            try {
                mOutputStream.write(buffer);
                mMainHandler.obtainMessage(BluetoothService.MESSAGE_SENT,
                        new BluetoothMessage(mDevice, new String(buffer))).sendToTarget();
            }
            catch(IOException e) {
            }
        }

        public void write(String message) {
            try {
                mOutputStream.write(message.getBytes());
                mMainHandler.obtainMessage(BluetoothService.MESSAGE_SENT,
                        new BluetoothMessage(mDevice, message)).sendToTarget();
            }
            catch(IOException e) {
            }
        }

        public void disconnect() {
            disconnect = true;

            try {
                mSocket.close();
            }
            catch(IOException e) {
                setState(BluetoothService.STATE_NONE);
            }
        }
    }
}

