package org.imaginer.scol;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Build;
import android.util.Log;
import android.net.Uri;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.Gravity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.PopupWindow;
import android.widget.ImageView;
import android.bluetooth.*;
import android.os.Handler;
import android.os.Bundle;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.UUID;
import java.util.List;
import java.util.Set;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Pattern;
import android.content.res.AssetManager;
import android.webkit.MimeTypeMap;

import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;

import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class ScolExternal implements LocationListener //, SurfaceHolder.Callback
{
  private NativeWrap mActivity;
  private Bitmap mSplashBitmap;
  private PopupWindow mSplashPopup;
  private ImageView mSplashView;

  private LocationManager mLocationManager = null;
  private float mLongitude;
  private float mLatitude;
  private float mAltitude;

  private static final UUID RS232_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Serial RFCOMM profile
  private BluetoothAdapter mBluetoothAdapter = null;
  private BluetoothSocket mBtSocket = null;
  private OutputStream mOutStream = null;  
  private InputStream mBtInStream = null;
  private Thread mBtWorkerThread = null;
  private Handler mBtHandler = new Handler();
  private final Lock mBtMutex = new ReentrantLock(true);

  private byte[] mReadBytes = null;
  boolean stopBtWorker = false;
  private String mAddress = "";

  private static Pattern fileExtnPtrn = Pattern.compile("([^\\s]+(\\.(?i)(jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv))$)");

  public ScolExternal()
  {
    mActivity = null;
  }

  // This method has to be called from Activity's onCreate method.
  public void setup(NativeWrap activity)
  {
    if (activity == null)
      Log.d("ScolApp", "activity == null");

    mActivity = activity;
  }

  public float getDeviceScreenDensity()
  {
    return mActivity.getApplicationContext().getResources().getDisplayMetrics().density;
  }

  private String getIntentTypeByFileExtension(String type, String ext)
  {
    if (type.equals("") || type.equals("*/*"))
    {
      type = null;
      if (ext != null)
        type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
      if (type == null)
        type = "*/*";
    }
    return type;
  }
   
  public void openUrl(String url, boolean choose)
  {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);

    try {
      Uri uri;
      if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file://") && !url.startsWith("ftp://")) {
        String buf = url;
        if (buf.startsWith("APK/")) {
          buf = buf.substring(4);
          if (fileExtnPtrn.matcher(buf).matches())
            uri = Uri.parse("content://" + mActivity.getApplicationContext().getPackageName() + ".assetprovider/" + buf);
          else
            uri = getCompressedFileUri(buf);
        } else {
          File file = new File(buf);
          uri = FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider", file);
        }

        if (uri != null) {
          intent.putExtra(Intent.EXTRA_STREAM, uri);
          intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        String filename = uri.getLastPathSegment();
        String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();

        String type = getIntentTypeByFileExtension("", ext);

        intent.setDataAndType(uri, type);
        choose = true;
      } else {
        uri = Uri.parse(url);
        intent.setData(uri);
      }

      if (uri != null) {
        if (!choose)
          mActivity.startActivity(intent);
        else {
          mActivity.startActivity(Intent.createChooser(intent, "Choose application"));
        }
      }
    }
    catch (Exception e)
    {
      Log.d("ScolApp", "Open url exception: " + e.getMessage());
    }
  }

  public void setFullScreen(final boolean state)
  {
    mActivity.runOnUiThread(new Runnable()
    {
      public void run()
      {
        View decorView = mActivity.getWindow().getDecorView();

        if (state)
        {
          decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                  | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                  | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                  | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                  | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                  | View.SYSTEM_UI_FLAG_FULLSCREEN);
        }
        else
        {
          decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        }
      }
    });
  }

  public void showSplash(final String imgpath)
  {
    mActivity.runOnUiThread(new Runnable()
    {
      @Override
      public void run()
      {
        InputStream bmpfile = null;

        try
        {
          Log.i("ScolApp", "showSplash start");
          bmpfile = mActivity.getAssets().open(imgpath);
          mSplashBitmap = BitmapFactory.decodeStream(bmpfile);

          // view
          mSplashView = new ImageView(mActivity);
          mSplashView.setScaleType(ImageView.ScaleType.FIT_XY);
          mSplashView.setImageBitmap(mSplashBitmap);

          View parentView = mActivity.getWindow().getDecorView();
          //Rect parentRect = new Rect();
          //parentView.getGlobalVisibleRect(parentRect);

          mSplashPopup = new PopupWindow(mSplashView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, false);
          mSplashPopup.setClippingEnabled(false);
          //mSplashPopup.setAttachedInDecor(true);
          //mSplashPopup.setOverlapAnchor(false);
          //mSplashPopup.setWidth(parentRect.width());
          //mSplashPopup.setHeight(parentRect.height());
          mSplashPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
          mSplashPopup.showAtLocation(parentView, Gravity.CENTER,0,0);
          //mSplashPopup.showAtLocation(mActivity.getWindow().getDecorView(), Gravity.NO_GRAVITY, parentRect.left, parentRect.top);

          Log.i("ScolApp", "showSplash done");
        }
        catch (IOException e)
        {
          Log.i("ScolApp", "showSplash error");
          e.printStackTrace();
        }
        finally
        {
          try
          {
            if(bmpfile != null)
              bmpfile.close();
          }
          catch (IOException e)
          {
            e.printStackTrace();
          }
        }
      }
    });
  }

  public void hideSplash()
  {
    mActivity.runOnUiThread(new Runnable()
    {
      @Override public void run()
      {
        if (mSplashPopup != null)
          mSplashPopup.dismiss();
      }
    });
  }

  public void initLocationService()
  {
    if (mLocationManager == null)
    {
      if (ActivityCompat.checkSelfPermission(mActivity.getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
      {
        mLocationManager = (LocationManager) mActivity.getSystemService(Context.LOCATION_SERVICE);
        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 250, 0.00001f, this);
      }
    }
  }

  public void startLocationService()
  {
    if (mLocationManager == null)
    {
      if (mActivity.CheckLocationPermission())
      {
        mActivity.runOnUiThread(new Runnable()
        {
          @Override public void run()
          {
            initLocationService();
          }
        });
      }
    }
  }

  public void removeLocationUpdate()
  {
    mLocationManager.removeUpdates(this);
    mLocationManager = null;
  }

  public void stopLocationService()
  {	
    if (mLocationManager != null)
    {
      mActivity.runOnUiThread(new Runnable()
      {
        @Override
        public void run()
        {
          removeLocationUpdate();
        }
      });
    }
  }

  @Override public void onLocationChanged(Location location)
  {
    mLongitude = (float)location.getLongitude();
    mLatitude = (float)location.getLatitude();
    mAltitude = (float)location.getAltitude();
  }

  @Override public void onProviderDisabled(String provider)
  {
  }
         
  @Override public void onProviderEnabled(String provider)
  {
  }
           
  @Override public void onStatusChanged(String provider, int status, Bundle extras)
  {
  }

  public float getLocationLongitude()
  {
    if (mLocationManager == null)
      return 0.0f;	
    return mLongitude;
  }

  public float getLocationLatitude()
  {
    if (mLocationManager == null)
      return 0.0f;
    return mLatitude;
  }

  public float getLocationAltitude()
  {
    if (mLocationManager == null)
      return 0.0f;
    return mAltitude;
  }

  //check bluetooth is present and enable it if needed
  public boolean haveBluetooth()
  {
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null)
    {
      return false;
    }
    else
    {
      mActivity.RequestBluetoothPermission();
      
      return mBluetoothAdapter.isEnabled();
    }
  }

  public byte[] getBluetoothData()
  {
    byte[] data = null;
    mBtMutex.lock(); 
    try
    {
      if (mReadBytes != null)
      {
        data = new byte[mReadBytes.length];
        System.arraycopy(mReadBytes, 0, data, 0, mReadBytes.length);
        //Log.d("ScolApp", "Bluetooth read: " + Integer.toString(mReadBytes.length) + " >" + new String(data));
        mReadBytes = null;
      }
    }
    finally
    {
      mBtMutex.unlock();
    }
    return data;
  }

  private void beginBluetoothListenForData()
  {   
    if (mBtSocket == null)
      return;

    try
    {
      mBtInStream = mBtSocket.getInputStream();
    }
    catch (IOException e)
    {
    }
		
    if (mBtInStream == null)
      return;

    mBtWorkerThread = new Thread(new Runnable()
    {
      public void run()
      {
	      while(!Thread.currentThread().isInterrupted() && !stopBtWorker)
	      {
	        try 
	        {
	          int bytesAvailable = mBtInStream.available();                        
	          if(bytesAvailable > 0)
	          {
	            byte[] packetBytes = new byte[bytesAvailable];
	            mBtInStream.read(packetBytes, 0, bytesAvailable);

              //mutex cp buffer to readbuffer   	
              mBtMutex.lock(); 
              try
              {
                if (mReadBytes != null)
                {
                  // merge with previous content
                  byte[] c = new byte[packetBytes.length + mReadBytes.length];
                  System.arraycopy(mReadBytes, 0, c, 0, mReadBytes.length);
                  System.arraycopy(packetBytes, 0, c, mReadBytes.length, packetBytes.length);
                  mReadBytes = c;
                }
                else
                {
                  mReadBytes = new byte[packetBytes.length];
                  System.arraycopy(packetBytes, 0, mReadBytes, 0, packetBytes.length);
                }
              }
              finally
              {
                mBtMutex.unlock();
              } 

              /*for(int i=0;i<bytesAvailable;i++)
	            {
	              byte b = packetBytes[i];

	              if(b == delimiter)
	              {
	                byte[] encodedBytes = new byte[readBufferPosition];
	                System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
	                final String data = new String(encodedBytes, "ISO-8859-1");
	                readBufferPosition = 0;
	                mBtHandler.post(new Runnable()
	                {
	                  public void run()
	                  {
                      //mutex cp buffer to readbuffer
                      //read data           	
                      mBtMutex.acquire(); 
                      try
                      {
                        mReadBufferCpy += data;
                      }
                      catch (InterruptedException ie)
                      { 
                        Log.d("ScolApp", "Bluetooth read data exception: " + ie.what());
                      } 
                      finally
                      {
                        mBtMutex.release();
                      } 
	                  }
	                });
	              }
	              else
	              {
	                readBuffer[readBufferPosition++] = b;
	              }
	            }*/
	          }
	        } 
	        catch (IOException ex) 
	        {
	          stopBtWorker = true;
	        }
	      }
	    }
	  });

    mBtWorkerThread.start();
  }

  private boolean writeBluetoothData(byte[] data)
  {
    if (mBtSocket == null || data == null)
      return false;
    
    try
    {
      mOutStream = mBtSocket.getOutputStream();
    }
    catch (IOException e)
    {
      return false;
    }

    try
    {
      //Log.d("ScolApp", "Bluetooth write: " + new String(data));
      //byte[] cpdata = new byte[data.length];
      //System.arraycopy(data, 0, cpdata, 0, data.length);
			mOutStream.write(data);
    }
    catch (IOException e)
    {
      return false;
    }
    return true;
	}

  public boolean connectBluetooth(String address)
  {
    disconnectBluetooth();
    mAddress = "";
    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

    List<String> BtDevices = new ArrayList<String>();
    for(BluetoothDevice bt : pairedDevices)
    {
       String dAddress = bt.getAddress();
       BtDevices.add(dAddress);

       //use address mode
       if (address.compareTo(dAddress) == 0)
         mAddress = dAddress;
    }

    int index = -1;
    //address not found use index mode
    if ((mAddress.compareTo("") == 0))
    {
      index = (Integer.parseInt(address.replaceAll("[^-?0-9]+", "")));
      if (index < 0)
        index = 0;
      
      if (index >= BtDevices.size())
      {
        Log.d("ScolApp", "Bluetooth index out of bounds.");
        return false;
      }
      else
      {
        mAddress = BtDevices.get(index);
      }
    }

    if (index == 0)
    {
      //test all until it connects
      int i = 0;
      while (i < BtDevices.size() && mBtSocket == null)
      {
        mAddress = BtDevices.get(i);
        i++;

        Log.d("ScolApp", "Bluetooth try to connect on: " + mAddress);

        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mAddress);
        if (device == null)
          continue;

        mBluetoothAdapter.cancelDiscovery();
        
        try
        {
          mBtSocket = device.createRfcommSocketToServiceRecord(RS232_UUID);
          //here is the part the connection is made, by asking the device to create a RfcommSocket
          if (mBtSocket != null)
            mBtSocket.connect();
        }
        catch (IOException e)
        {
          try
          {
            mBtSocket.close();
          }
          catch (IOException e2)
          {
            continue;
          }
          Log.d("ScolApp", "Bluetooth Socket creation failed");
      
          mBtSocket = null;
          continue;
        }

        if (mBtSocket != null)
        {
          Log.d("ScolApp", "Bluetooth connected on index " + String.valueOf(i - 1));
          break;
        }
      }
    }
    else
    {
      BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mAddress);
      if (device == null)
      {
        Log.d("ScolApp", "Bluetooth device not found.");
        return false;
      }

      mBluetoothAdapter.cancelDiscovery();
      try
      {
        mBtSocket = device.createRfcommSocketToServiceRecord(RS232_UUID);
        //here is the part the connection is made, by asking the device to create a RfcommSocket
        if (mBtSocket != null)
          mBtSocket.connect();
        else
          return false;
      }
      catch (IOException e)
      {
        try
        {
           mBtSocket.close();
        }
        catch (IOException e2)
        {
          Log.d("ScolApp", "Unable to end Bluetooth connection");
        }
        Log.d("ScolApp", "Bluetooth Socket creation failed");
      
        mBtSocket = null;
        return false;
      }
    }

    //this is a method used to read what the Arduino says for example when you write Serial.print("Hello world.") in your Arduino code
    if (mBtSocket == null)
      return false;

    beginBluetoothListenForData();
    return true;
  }

  public void disconnectBluetooth()
  {
    stopBtWorker = true;

    try
    {
      if (mBtWorkerThread != null)
        mBtWorkerThread.join();
    }
    catch(InterruptedException ei)
    {
      Log.d("ScolApp", "Unable to join Bluetooth thread");
    }

    if (mBtSocket != null)
    try
    {
      mBtSocket.close();
    }
    catch (IOException e2)
    {
      Log.d("ScolApp", "Unable to end Bluetooth connection");
    }
    stopBtWorker = false;
    mBtSocket = null;
    mReadBytes = null;
    mOutStream = null;
    mBtInStream = null;

    mAddress = "";
  }

  private void copyFile(InputStream in, OutputStream out) throws IOException
  {
    byte[] buffer = new byte[1024];
    int read;

    while ((read = in.read(buffer)) != -1)
    {
      out.write(buffer, 0, read);
    }
  }

  private Uri getCompressedFileUri(String fileName)
  {
    AssetManager assetManager =  mActivity.getApplicationContext().getAssets();

    File baseFile = new File(fileName);
    File file = new File(mActivity.getFilesDir().getPath(), baseFile.getName());
    try
    {
      InputStream in = assetManager.open(fileName);
      OutputStream out = mActivity.getApplicationContext().openFileOutput(baseFile.getName(), android.content.Context.MODE_PRIVATE);

      copyFile(in, out);
      in.close();
      out.close();
    }
    catch (Exception e)
    {
      Log.d("ScolApp", "Failed to copy asset file: " + fileName);
    }

    return FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider", file);
  }

  public void shareIntent(String type, HashMap<String,String> params, String title)
  {
    Intent intent = new Intent();
    Uri uri = null;

    intent.setAction(Intent.ACTION_SEND);
    try
    {
      String buf = params.get("path");
      if (buf != null && !buf.isEmpty())
      {
        if (buf.startsWith("APK/"))
        {
          buf = buf.substring(4);
          if (fileExtnPtrn.matcher(buf).matches())
            uri = Uri.parse("content://" + mActivity.getApplicationContext().getPackageName() + ".assetprovider/" + buf);
          else
            uri = getCompressedFileUri(buf);
        }
        else
        {
          File file = new File(buf);
          uri = FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider", file);
        }

        if (uri != null)
        {
          intent.putExtra(Intent.EXTRA_STREAM, uri);
          intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
          String filename = uri.getLastPathSegment();
          String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
          type = getIntentTypeByFileExtension(type, ext);
        }
      }

      buf = params.get("text");
      if (buf != null && !buf.isEmpty())
      {
        intent.putExtra(Intent.EXTRA_TEXT, buf);
        if (uri == null && (type.equals("") || type.equals("*/*")))
          type = "text/plain";
      }

      buf = params.get("subject");
      if (buf != null && !buf.isEmpty())
        intent.putExtra(Intent.EXTRA_SUBJECT, buf);

      buf = params.get("mail");
      if (buf != null && !buf.isEmpty())
      {
        String[] mailArray = new String[]{buf};
        intent.putExtra(Intent.EXTRA_EMAIL, mailArray);
      }

      intent.setType(type);

      mActivity.startActivity(Intent.createChooser(intent, (title.isEmpty()) ? "Share" : title));
    }
    catch (Exception e)
    {
      Log.d("ScolApp", "Chooser exception: " + e.getMessage());
    }
  }

/*  public void shareIntentMultiple(String types, String[] filesToSend)
  {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_SEND_MULTIPLE);
    intent.putExtra(Intent.EXTRA_SUBJECT, "Here are some files.");
    intent.setType("image/jpeg");
    ArrayList<Uri> files = new ArrayList<Uri>();

    for(String path : filesToSend) {
      File file = new File(path);
      Uri uri = Uri.fromFile(file);
      files.add(uri);
    }
    intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files);
    mActivity.startActivity(intent);
  }*/
}