package com.decoding.twodtg.demobarcodereader2dtg;

/**
 * Created by AAfanasiev on 07.12.2016.
 */
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;

public abstract class Communications
{
  static final String ComCharsetOptionName = "BLT_CHARSET";
  static final String ReturnOptionName = "RETURN_SEND";
  static final String RemoteAddressOptionName = "REMOTE_ADDRESS";
  static final String ConnectedOptionName = "CONNECTED_SESSION";

  protected static BltSerial bltSerial = null;
  protected static BltSerialToKeyboard bltSerialToKeyboard = null;
  protected static String address = null;
  protected static Charset characterSet = null;
  protected static boolean sendReturn = false;
  protected static boolean zeroAsEndString = false;
  protected static boolean connectedSession = true;

  protected Communications(Context context)
  {
    characterSet = Charset.forName(ReadCharsetName(context));
    sendReturn = ReadSendReturn(context);
    address = ReadRemoteAddress(context);
    connectedSession = ReadConnectedSession(context);
  }

  public static Communications GetBltSerial(Context context) throws ConnectionException
  {
    if(bltSerial == null)
      bltSerial = new BltSerial(context);
    return bltSerial;
  }

  public static Communications GetBltSerialToKeyboard(Context context) throws ConnectionException
  {
    if(bltSerialToKeyboard == null)
      bltSerialToKeyboard = new BltSerialToKeyboard(context);
    return bltSerialToKeyboard;
  }

  public abstract void SendString(String str) throws ConnectionException;
  public abstract void Close();
  public abstract void Discovery(Activity activity);
  public abstract void StopDiscovery();

  public static void setConnectedSession(boolean val, Context context)
  {
    connectedSession = val;
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = SP.edit();
    editor.putBoolean(ConnectedOptionName, val);
    editor.commit();
  }

  public static void setRemoteAddress(String val, Context context)
  {
    address = val;
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = SP.edit();
    editor.putString(RemoteAddressOptionName, val);
    editor.commit();
  }

  public static void setCharset(String name, Context context)
  {
    characterSet = Charset.forName(name);
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = SP.edit();
    editor.putString(ComCharsetOptionName, name);
    editor.commit();
  }

  public static void setSendReturn(boolean val, Context context)
  {
    sendReturn = val;
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = SP.edit();
    editor.putBoolean(ReturnOptionName, val);
    editor.commit();
  }

  public static boolean ReadConnectedSession(Context context)
  {
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    return SP.getBoolean(ConnectedOptionName, true);
  }

  public static String ReadCharsetName(Context context)
  {
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    return SP.getString(ComCharsetOptionName, "US-ASCII");
  }

  public static boolean ReadSendReturn(Context context)
  {
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    return SP.getBoolean(ReturnOptionName, true);
  }

  public static String ReadRemoteAddress(Context context)
  {
    SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(context);
    return SP.getString(RemoteAddressOptionName, null);
  }

  public class ConnectionException extends IOException
  {
    private static final long serialVersionUID = 2L;
    public ConnectionException(String er)
    {
      super(er);
    }
  }
}

class BltSerial extends Communications
{
  private static final String FAILED_DATA_MSG = "Failed data transmission!";
  private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

  public BluetoothAdapter mBluetoothAdapter = null;
  private BluetoothSocket btSocket = null;

  public BltSerial(Context context) throws ConnectionException
  {
    super(context);
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null)
      throw new ConnectionException("Bluetooth is not available.");
    if (!mBluetoothAdapter.isEnabled())
      throw new ConnectionException("Please enable your BT.");

  }
  //static int id = 1;
  public void SendString(String str) throws ConnectionException
  {
    if(address == null)
      throw new ConnectionException("Please select computer to send data!");
    try
    {
      BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
      //btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
      if(btSocket == null)
      {
        btSocket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
        mBluetoothAdapter.cancelDiscovery();
        btSocket.connect();
      }else if(!btSocket.isConnected())
        btSocket.connect();
      OutputStream outStream = btSocket.getOutputStream();
      //str += "  " + id++;
      if(sendReturn)
        str += "\r\n";
      byte[] array = str.getBytes(characterSet);

      //for(int i=0; i < array.length; i++)
       // Utils.Trace(Integer.toString(array[i]));

      outStream.write(array);
      array = null;
      if(zeroAsEndString)
      {
        byte[] end = new byte[2];
        outStream.write(end);
      }
      outStream.flush();
      Thread.sleep(200);
      //Utils.Trace("To BLT: " + str);
    }
    catch(Exception e)
    {
      if(btSocket != null)
      {
        try
        {
          btSocket.close();
        } catch(Exception ee) {}
        btSocket = null;
      }
      Utils.Failed(e);
      throw new ConnectionException(FAILED_DATA_MSG);
    }
    try
    {
      if(!connectedSession)
      {
        if(btSocket != null)
          btSocket.close();
        btSocket = null;
      }
    }catch(Exception e2) {}
  }

  public void Discovery(Activity activity)
  {
    mBluetoothAdapter.startDiscovery();
  }

  public void StopDiscovery()
  {
    mBluetoothAdapter.cancelDiscovery();
  }

  public void Close()
  {
    try
    {
      if(btSocket != null)
      {
        btSocket.close();
        btSocket = null;
      }
    }catch(Exception e)
    {
    }
    mBluetoothAdapter = null;
    bltSerial = null;
  }
}


class BltSerialToKeyboard extends BltSerial
{
  public BltSerialToKeyboard(Context context) throws ConnectionException
  {
    super(context);

    try
    {
      characterSet = Charset.forName("UTF_16LE");
    }catch(Exception e)
    {
      characterSet = Charset.forName("UTF_16");
    }
    zeroAsEndString = true;
    sendReturn = false;
  }

  @Override
  public void Close()
  {
    super.Close();
    bltSerialToKeyboard = null;
  }
}

class BltKeyboard extends BltSerial
{
  public BltKeyboard(Context context) throws ConnectionException
  {
    super(context);
  }

  @Override
  public void SendString(String str) throws ConnectionException
  {
  }
}
