package ui;

import android.Manifest;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;

import Localization.ClientThread;
import Localization.KeyMaster;
import sensors.WifiReceiver;

import static edu.fiu.reu2017.R.*;


public class MainActivity extends AppCompatActivity
{
    public final static String SQLDatabase = "160.39.57.71";
    public final static int portNumber = 9254;
    public final static int TIMEOUT = 2 * 1000;
    public final static boolean multi_phone = false;
    private static final int PERMISSIONS_REQUEST_CODE_ACCESS_COARSE_LOCATION = 1001;
    public static int VECTOR_SIZE = -1;

    //public final static int KEY_SIZE = 1024;
    public final static long BILLION = 1000000000; // 10^9
    public final static long FACTOR = 10; // Applies for DMA/MCA ONLY!
    public final static int k = 2;//Number of Minimum distances used!

    protected WifiReceiver mWifiManager;
    protected Button StartScan;
    protected Button TrainData;
    protected Button Update;
    protected Button Localize;
    protected Button UNDO;
    protected Button RESET;
    private NumberPicker LocalizationSelect;
    protected TextView output;
    protected int LOCALIZATION_SCHEME = -1;

    protected String[] options = new String[] {
            "1: PlainText/Min.",
            "2: DGK/Min.",
            "3: Paillier/Min.",
            "4: PlainText/MCA",
            "5: DGK/MCA",
            "6: Paillier/MCA",
            "7: PlainText/DMA",
            "8: DGK/DMA",
            "9: Paillier/DMA",
            "10: ElGamal/Min.",
            "11: ElGamal/MCA",
            "12: ElGamal/DMA"
    };

    // IP Communication
    private Socket ClientSocket;
    private ObjectInputStream fromServer;
    private ObjectOutputStream toServer;

    // Print Process DB Results
    public static Toast process_good;
    public static Toast process_bad;
    public static Toast Wifi_needed;
    public static Toast good_train;
    public static Toast bad_train;
    public static HashMap<String, String> AP_map = new HashMap<>();

    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(layout.activity_main);
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

        if (!KeyMaster.finished)
        {
            KeyMaster.init();
        }
        KeyMaster.last_device = getDeviceName();

        good_train = Toast.makeText(getApplicationContext(), "Training Successful!", Toast.LENGTH_SHORT);
        bad_train = Toast.makeText(getApplicationContext(), "Training NOT Successful!", Toast.LENGTH_SHORT);
        process_good = Toast.makeText(getApplicationContext(), "Process DB successful!", Toast.LENGTH_SHORT);
        process_bad = Toast.makeText(getApplicationContext(), "Process DB failed!", Toast.LENGTH_SHORT);
        Wifi_needed = Toast.makeText(getApplicationContext(), "Are you connected to Wi-Fi?", Toast.LENGTH_LONG);

        // Connect Variables to GUI
        StartScan = findViewById(id.scan);
        Localize = findViewById(id.Localize);
        Update = findViewById(id.Process);
        RESET = findViewById(id.RESET);
        UNDO = findViewById(id.UNDO);
        TrainData = findViewById(id.train);
        output = findViewById(id.output);
        LocalizationSelect = findViewById(id.numberPicker);

        /*
         * This is only for scanning Wifi!
         * Use this if you need functioning code for Android to
         * scan APs and Wifi Signal Strength
         */
        mWifiManager = new WifiReceiver(this, null);
        LocalizationSelect.setMinValue(1);
        LocalizationSelect.setMaxValue(options.length);
        LocalizationSelect.setDisplayedValues(options);

        /*
         * This is critical! You should only need this once!
         * But you need to give the app runtime application permission!
         * Otherwise this will NOT work for Android 6.0 and up.
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED)
        {
            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                    PERMISSIONS_REQUEST_CODE_ACCESS_COARSE_LOCATION);
        }

        StartScan.setOnClickListener(new start_scan());

        /*
         * Use this Button to go to Training Option
         * Open the map and Train data points!
         */
        TrainData.setOnClickListener(new train_data());

        /*
        Input: Command to force Database to recompute Lookup Table
        Return: NOTHING. Kill Thread after
        */
        Update.setOnClickListener(new process_db());

        /*
        Client choose to use the REU 2015 method
        This method is having the phone
        decrypt and sort the distances
         */
        Localize.setOnClickListener(new localize());

        RESET.setOnClickListener(new reset());

        UNDO.setOnClickListener(new undo());

        // Get Mapping
        // this.MAC_to_AP_Name();

        String msg = getString(string.key_gen) + KeyMaster.duration/BILLION + " " + getString(string.seconds);
        //msg += " " + KEY_SIZE;
        msg += " K =" + k;
        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
        output.setText(msg);
    }

    private static class process_db implements View.OnClickListener
    {
        public void onClick(View v)
        {
            test_connection t = new test_connection();
            Thread th = new Thread(t);
            th.start();
            try
            {
                th.join();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            if(t.connected)
            {
                new Thread(new ClientThread()).start();
            }
            else
            {
                Wifi_needed.show();
            }
        }
    }

    private class train_data implements View.OnClickListener, Runnable
    {
        boolean connected = false;
        public void onClick(View v)
        {
            Thread conn = new Thread(this);
            conn.start();
            try
            {
                conn.join();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            if(connected)
            {
                if(KeyMaster.map_name.length() == 0 || KeyMaster.map == null)
                {
                    Toast.makeText(getApplicationContext(), "PICK A MAP!", Toast.LENGTH_LONG).show();
                }
                else
                {
                    Toast.makeText(getApplicationContext(), "Loading Map: " + KeyMaster.map_name, Toast.LENGTH_LONG).show();
                    Intent train = new Intent(MainActivity.this, TrainActivity.class);
                    startActivity(train);
                }
            }
        }

        public void run()
        {
            try
            {
                ClientSocket = new Socket();
                ClientSocket.connect(new InetSocketAddress(SQLDatabase, portNumber), TIMEOUT);
                toServer = new ObjectOutputStream(ClientSocket.getOutputStream());
                toServer.writeObject("Hello");
                toServer.flush();
                connected = true;
            }
            catch (IOException e)
            {
                Wifi_needed.show();
                connected = false;
            }
        }
    }

    private class localize implements View.OnClickListener, Runnable
    {
        boolean connected = false;
        public void onClick(View v)
        {
            Thread conn = new Thread(this);
            conn.start();
            try
            {
                conn.join();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            if(connected)
            {
                LOCALIZATION_SCHEME = LocalizationSelect.getValue();
                // Check if elgamal is set and keys are null
                switch (LOCALIZATION_SCHEME)
                {
                    // These map to EL-Gamal Min, MCA, DMA
                    case 10:
                    case 11:
                    case 12:
                        if (!KeyMaster.ElGamal)
                        {
                            Toast.makeText(getApplicationContext(), "El Gamal Keys not generated!", Toast.LENGTH_SHORT).show();
                            return;
                        }
                        break;
                }
                if(KeyMaster.map == null || KeyMaster.map_name.length() == 0)
                {
                    Toast.makeText(getApplicationContext(), "PICK A MAP!", Toast.LENGTH_LONG).show();
                }
                else
                {
                    Toast.makeText(getApplicationContext(), "Loading Map: " + KeyMaster.map_name, Toast.LENGTH_LONG).show();
                    Intent Localize = new Intent(MainActivity.this, LocalizeActivity.class);
                    Localize.putExtra("Localization", LOCALIZATION_SCHEME);
                    startActivity(Localize);
                }
            }
        }

        public void run()
        {
            try
            {
                ClientSocket = new Socket();
                ClientSocket.connect(new InetSocketAddress(SQLDatabase, portNumber), TIMEOUT);
                toServer = new ObjectOutputStream(ClientSocket.getOutputStream());
                toServer.writeObject("Hello");
                toServer.flush();
                connected = true;
            }
            catch (IOException e)
            {
                Wifi_needed.show();
                connected = false;
            }
        }
    }

    private class reset implements View.OnClickListener, Runnable
    {
        public void onClick(View v)
        {
            new Thread(this).start();
        }

        public void run()
        {
            try
            {
                ClientSocket = new Socket();
                ClientSocket.connect(new InetSocketAddress(SQLDatabase, portNumber), TIMEOUT);

                //Prepare I/O Stream
                toServer = new ObjectOutputStream(ClientSocket.getOutputStream());
                fromServer = new ObjectInputStream(ClientSocket.getInputStream());
                toServer.writeObject("RESET");
                toServer.flush();
                if (fromServer.readBoolean())
                {
                    runOnUiThread(new Runnable()
                    {
                        public void run()
                        {
                            Toast.makeText(getApplicationContext(), "RESET COMPLETE!", Toast.LENGTH_LONG).show();
                        }
                    });
                }
                else
                {
                    runOnUiThread(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            Toast.makeText(getApplicationContext(), "RESET FAILED!", Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
            catch (IOException e)
            {
                Wifi_needed.show();
            }
        }
    }

    private class undo implements View.OnClickListener, Runnable
    {
        public void onClick(View v)
        {
            new Thread(this).start();
        }

        public void run()
        {
            try
            {
                ClientSocket = new Socket();
                ClientSocket.connect(new InetSocketAddress(SQLDatabase, portNumber), TIMEOUT);
                //Prepare I/O Stream
                toServer = new ObjectOutputStream(ClientSocket.getOutputStream());
                fromServer = new ObjectInputStream(ClientSocket.getInputStream());
                toServer.writeObject("UNDO");
                // Need to send Coordinates, Phone and DeviceName
                toServer.writeObject(KeyMaster.last_coordinates);
                toServer.writeObject(KeyMaster.map_name);
                toServer.writeObject(KeyMaster.last_device);
                toServer.flush();

                if (fromServer.readBoolean())
                {
                    runOnUiThread(() -> Toast.makeText(getApplicationContext(),"UNDO COMPLETE!", Toast.LENGTH_LONG).show());
                }
                else
                {
                    runOnUiThread(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            Toast.makeText(getApplicationContext(),"UNDO FAILED!", Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
            catch (IOException e)
            {
                Wifi_needed.show();
            }
        }
    }

    private class start_scan implements View.OnClickListener
    {
        public void onClick(View v)
        {
            /*
            if(mWifiManager.startScan())
            {
                Toast.makeText(getApplicationContext(), "Got reading from Wifi Manager", Toast.LENGTH_LONG).show();
            }
            else
            {
                Toast.makeText(getApplicationContext(), "Scanning Wi-Fi failed!", Toast.LENGTH_LONG).show();
            }
            */
            Intent map = new Intent(MainActivity.this, AddMapActivity.class);
            startActivity(map);
        }
    }

    public void onResume()
    {
        super.onResume();
        mWifiManager.registerReceiver(this);
    }

    public void onPause()
    {
        super.onPause();
        mWifiManager.unregisterReceiver(this);
    }

    public static String [] getPhoneData()
    {
        String [] phones = new String[4];
        phones[0] = System.getProperty("os.version");  // OS version
        phones[1] = android.os.Build.DEVICE;           // Device
        phones[2] = getDeviceName();                   // Manufactuer/Model
        phones[3] = android.os.Build.PRODUCT;          // Product
        return phones;
    }

    // source: https://stackoverflow.com/questions/1995439/get-android-phone-model-programmatically
    // Better way to get model
    protected static String getDeviceName()
    {
        String manufacturer = Build.MANUFACTURER;
        String model = Build.MODEL;
        if (model.toLowerCase().startsWith(manufacturer.toLowerCase()))
        {
            return capitalize(model);
        }
        else
        {
            return capitalize(manufacturer) + " " + model;
        }
    }

    private static String capitalize(String s)
    {
        if (s == null || s.length() == 0)
        {
            return "";
        }
        char first = s.charAt(0);
        if (Character.isUpperCase(first))
        {
            return s;
        }
        else
        {
            return Character.toUpperCase(first) + s.substring(1);
        }
    }

    /*
    protected void MAC_to_AP_Name()
    {
        String line;
        String [] tuple;
        try
        {
            Resources res = getResources();
            InputStream in_s = res.openRawResource(R.raw.broadwayfloormap);
            BufferedReader reader = new BufferedReader(new InputStreamReader(in_s));
            while((line = reader.readLine()) != null)
            {
                tuple = line.split(",");
                AP_map.put(tuple[0], tuple[1]);
                //answer.put(tuple[0], tuple[1]);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
    */
}