package cz.navtom.elektromer;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.provider.Settings.Secure;

import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.Vector;

import static cz.navtom.elektromer.UsbService.ACTION_USB_DISCONNECTED;
import static java.security.AccessController.getContext;

public class MainActivity extends AppCompatActivity {

    /*
    * Notifications from UsbService will be received here.
    */
    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            TextView usbText = (TextView) findViewById(R.id.usbConnection);
            int errC = ContextCompat.getColor(context, R.color.err);
            int okC = ContextCompat.getColor(context, R.color.ok);

            switch (intent.getAction()) {

                //udalosti z USB/seriove linky
                case UsbService.ACTION_USB_PERMISSION_GRANTED: // USB PERMISSION GRANTED
                    //Toast.makeText(context, "USB Ready", Toast.LENGTH_SHORT).show();
                    usbText.setText("Připraveno");
                    usbText.setTextColor(okC);
                    break;

                case UsbService.ACTION_USB_PERMISSION_NOT_GRANTED: // USB PERMISSION NOT GRANTED
                    //Toast.makeText(context, "USB Permission not granted", Toast.LENGTH_SHORT).show();
                    usbText.setText("Opravneni");
                    usbText.setTextColor(errC);
                    setConnectionStatus(false);
                    break;

                case UsbService.ACTION_NO_USB: // NO USB CONNECTED
                    //Toast.makeText(context, "No USB connected", Toast.LENGTH_SHORT).show();
                    usbText.setText("Nepřipojeno");
                    usbText.setTextColor(errC);
                    setConnectionStatus(false);
                    break;

                case ACTION_USB_DISCONNECTED: // USB DISCONNECTED
                    //Toast.makeText(context, "USB disconnected", Toast.LENGTH_SHORT).show();
                    usbText.setText("Odpojeno");
                    usbText.setTextColor(errC);
                    setConnectionStatus(false);
                    break;

                case UsbService.ACTION_USB_NOT_SUPPORTED: // USB NOT SUPPORTED
                    //Toast.makeText(context, "USB device not supported", Toast.LENGTH_SHORT).show();
                    usbText.setText("Nepodporovano");
                    usbText.setTextColor(errC);
                    setConnectionStatus(false);
                    break;

                //udalosti z HTTP
                case HttpReq.HTTP_INFO_READY:
                    String info = intent.getStringExtra("info");
                    boolean found = intent.getBooleanExtra("found", false);


                    TextView zakInfo = (TextView) findViewById(R.id.zakInfo);
                    zakInfo.setText(info);

                    if (found) {

                        readData.cid = cid;

                    }
                    else {

                        readData.cid = 0;

                    }

                    break;

                case HttpReq.HTTP_SAVE_OK:

                    //skryt progressBar kolecko
                    ProgressBar bar = (ProgressBar) findViewById(R.id.saveBar);
                    bar.setVisibility(View.GONE);

                    //zobrazit OK fajfku
                    ImageView saveCheckMark = (ImageView) findViewById(R.id.saveCheckMark);
                    saveCheckMark.setVisibility(View.VISIBLE);

                    //fade in animace
                    Animation fadeIn = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fadein);
                    saveCheckMark.startAnimation(fadeIn);

                    break;

                case HttpReq.HTTP_SAVE_ERR:

                    //skryt progressBar kolecko
                    bar = (ProgressBar) findViewById(R.id.saveBar);
                    bar.setVisibility(View.GONE);

                    //zobrazit ERR symbol
                    ImageView saveErrSymbol = (ImageView) findViewById(R.id.saveErrSymbol);
                    saveErrSymbol.setVisibility(View.VISIBLE);

                    //fade in animace
                    fadeIn = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fadein);
                    saveErrSymbol.startAnimation(fadeIn);

                    //znovu zprovoznit tlačítko
                    Button saveButton = (Button) findViewById(R.id.saveButton);
                    saveButton.setClickable(true);
                    break;
            }
        }
    };

    private UsbService usbService;
    private MyHandler mHandler;
    private Vector<Byte> receivedBytes = new Vector<Byte>();
    private boolean connected = false;
    private Vector<Byte> dataBuffer = new Vector<Byte>();

    private int dataOffset;

    private int GET                         = 0;
    private final static int GET_VENDOR     = 1;
    private final static int GET_APLUS      = 2;

    private DataToSave readData             = new DataToSave();

    private int cid;

    private boolean fetchComplete           = false;

    EditText IDzak;

    private final ServiceConnection usbConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
            usbService = ((UsbService.UsbBinder) arg1).getService();
            usbService.setHandler(mHandler);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            usbService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(this);

        //Text input ID zakaznika - ziska info o zakaznikovi po stisknuti OK na SW klavesnici
        IDzak = (EditText) findViewById(R.id.IDzak);
        IDzak.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
                if (id == EditorInfo.IME_ACTION_DONE) {
                    //zavolani metody pro ziskani udaju o zakaznikovi
                    getCustomerInfo();
                    //schovani klavesnice
                    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
                }
                return false;
            }
        });

    }

    private void getCustomerInfo() {

        EditText IDzak = (EditText) findViewById(R.id.IDzak);
        try {
            int cid = Integer.parseInt(IDzak.getText().toString());
            this.cid = cid;
            //HTTP dotaz na API zpracovavany asynchronne, info se vrati pres Intent
            new HttpReq(getApplicationContext(), HttpReq.HTTP_REQ_INFO).execute(cid);
        }
        catch (Exception e){

            toastAlert("ID zákazníka přesáhlo maximální povolenou délku");

        }


    }

    @Override
    public void onResume() {
        super.onResume();
        setFilters();  // Start listening notifications from UsbService
        startService(UsbService.class, usbConnection, null); // Start UsbService(if it was not started before) and Bind it
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(mUsbReceiver);
        unbindService(usbConnection);
    }

    private void startService(Class<?> service, ServiceConnection serviceConnection, Bundle extras) {
        if (!UsbService.SERVICE_CONNECTED) {
            Intent startService = new Intent(this, service);
            if (extras != null && !extras.isEmpty()) {
                Set<String> keys = extras.keySet();
                for (String key : keys) {
                    String extra = extras.getString(key);
                    startService.putExtra(key, extra);
                }
            }
            startService(startService);
        }
        Intent bindingIntent = new Intent(this, service);
        bindService(bindingIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    private void setFilters() {
        IntentFilter filter = new IntentFilter();

        //filtry akci z USB/seriove linky
        filter.addAction(UsbService.ACTION_USB_PERMISSION_GRANTED);
        filter.addAction(UsbService.ACTION_NO_USB);
        filter.addAction(ACTION_USB_DISCONNECTED);
        filter.addAction(UsbService.ACTION_USB_NOT_SUPPORTED);
        filter.addAction(UsbService.ACTION_USB_PERMISSION_NOT_GRANTED);

        //filtry ze zpracovani HTTP dotazu
        filter.addAction(HttpReq.HTTP_INFO_READY);
        filter.addAction(HttpReq.HTTP_SAVE_OK);
        filter.addAction(HttpReq.HTTP_SAVE_ERR);


        registerReceiver(mUsbReceiver, filter);
    }

    /*
     * This handler will be passed to UsbService. Data received from serial port is displayed through this handler
     */
    private class MyHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;

        public MyHandler(MainActivity activity) {
            mActivity = new WeakReference<>(activity);
        }


        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {

                case UsbService.MESSAGE_FROM_SERIAL_PORT:

                    if (receivedBytes == null) return;

                    byte[] data = (byte[]) msg.obj;

                    for (byte b : data) {

                        receivedBytes.add(b);
                    }

                    manageReceivedData();

                    break;

            }
        }
    }

    private void toastAlert(String text) {

        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();

    }

    public void PripojitButton(View v) {


        if (usbService.isBusy()) {
            this.toastAlert("Linka je zaneprázdněná");
            return;
        }

        if (this.connected) {
            //pripojeno = provest odpojeni

            printDebugText("Odpojovani");
            usbService.Obsluha(UsbService.ACTION_SERIAL, UsbService.ACTION_SENS_DISCO);         // odeslani sekvence odpojeni
            setConnectionStatus(false);
            fetchComplete = false;                                                              // povoleni odečítacího tlačítka
        }
        else {
            //odpojeno = provest pripojeni
            this.receivedBytes.clear();

            printDebugText("Pripojovani");
            GET = GET_VENDOR;                                                                   // zadost o ziskani nazvu vyrobce
            usbService.Obsluha(UsbService.ACTION_SERIAL, UsbService.ACTION_SENS_CON);           // odeslani sekvence pripojeni
        }
    }


    public void readButton(View v) {

        if (usbService.isBusy()) {
            this.toastAlert("Linka je zaneprázdněná");
            return;
        }

        if (!this.connected) {
            this.toastAlert("Zařízení není připojeno");
            return;
        }

        if (fetchComplete) {
            this.toastAlert("Údaje již byly odečteny");
            return;
        }

        readData.aplus = 0;

        //obsluha checkboxu "celkova spotreba"
        CheckBox aplus = (CheckBox) findViewById(R.id.checkAplus);

        if (aplus.isChecked()) {

            this.readAplus();

        }


    }

    public void saveButton(View v) {

        if (readData.aplus == 0 || !fetchComplete) {

            toastAlert("Data nebyla odečtena");
            return;

        }

        if (readData.cid == 0) {

            toastAlert("Neznámý zákazník");
            return;

        }

        readData.aid =  Secure.getString(getContentResolver(), Secure.ANDROID_ID);

        //HTTP dotaz na API zpracovavany asynchronne, info se vrati pres Intent
        new HttpReq(getApplicationContext(), HttpReq.HTTP_SAVE_DATA).execute(readData);

        //skryt ERR symbol
        ImageView saveErrSymbol = (ImageView) findViewById(R.id.saveErrSymbol);
        saveErrSymbol.setVisibility(View.GONE);

        //skryt check mark
        ImageView saveCheckMark = (ImageView) findViewById(R.id.saveCheckMark);
        saveCheckMark.setVisibility(View.GONE);

        //zobrazit progress bar
        ProgressBar bar = (ProgressBar) findViewById(R.id.saveBar);
        bar.setVisibility(View.VISIBLE);

        //tlacitko "ulozit" nebude mozne stisknout
        Button saveButton = (Button) findViewById(R.id.saveButton);
        saveButton.setClickable(false);


    }

    //pripraveni operace na odecteni celkove spotreby
    private void readAplus() {
        printDebugText("Stahovani Aplus");

        dataOffset = receivedBytes.size();

        usbService.Obsluha(UsbService.ACTION_SERIAL, UsbService.ACTION_GET_APLUS);         // odeslani pozadavku na seriovou linku
        GET = GET_APLUS;
    }

    //navrat do stavu po spusteni apliakce
    private void reset() {

        //smazat ziskane udaje
        readData = new DataToSave();

        //vymazat ID zakaznika z inputu
        IDzak = (EditText) findViewById(R.id.IDzak);
        IDzak.setText("");

        //vymazat odectenou hodnotu spotreby
        TextView aplusText = (TextView) findViewById(R.id.tvAplusVal);
        aplusText.setText("");

        //vymazat jmeno zakaznika
        TextView zakInfo = (TextView) findViewById(R.id.zakInfo);
        zakInfo.setText("");

        //skryt OK fajfku
        ImageView saveCheckMark = (ImageView) findViewById(R.id.saveCheckMark);
        saveCheckMark.setVisibility(View.GONE);

        //skryt ERR ikonu
        ImageView saveErrSymbol = (ImageView) findViewById(R.id.saveErrSymbol);
        saveErrSymbol.setVisibility(View.GONE);

    }

    //zpracovani prijatych dat ulozenych v bufferu
    private void manageReceivedData() {

        //zadna operace s daty neni pozadovana
        if (GET == 0) return;

        if (receivedBytes == null) {
            printDebugText("receivedBytes neni; operace=" + this.GET);
            return;
        }


        //ziskani jmena vendora pri pripojovani
        if (GET == GET_VENDOR  && receivedBytes.size() > 30) {
            printRawData(receivedBytes);

            if (receivedBytes.contains((byte) 13) && receivedBytes.contains((byte) 10)) {

                getDeviceName();
                setConnectionStatus(true);
                GET = 0;

            }
        }

        //ziskani celkove spotreby (aplus)
        if (GET == GET_APLUS && receivedBytes.size() > dataOffset + 30) {

            TextView aplusText = (TextView) findViewById(R.id.tvAplusVal);

            dataBuffer.clear();

            //4 bajty obsahujici celkovou spotrebu jsou ulozene v bufferu prijatych zprav
            int offset = dataOffset + 17;

            //ulozeni aplus bajtu do jineho bufferu
            for (int i = 0; i < 4; i++) {
                dataBuffer.add(receivedBytes.get(offset + i));
            }

            //prevod bajtu z bufferu na long int
            long val = unsignedIntToLong(tobyteArr(dataBuffer));

            //ulozeni celkove spotreby do struktury, ktera se zapisuje do DB
            readData.aplus = val;

            //zobrazeni spotreby
            aplusText.setText(String.valueOf(val));

            fetchComplete = true;

            GET = 0;
        }
    }

    //prevod z vektoru na pole
    byte[] tobyteArr(Vector<Byte> oBytes)
    {
        byte[] bytes = new byte[oBytes.size()];

        for(int i = 0; i < oBytes.size(); i++) {
            bytes[i] = oBytes.get(i).byteValue();
        }

        return bytes;
    }

    /**
     * Converts a 4 byte array of unsigned bytes to an long
     * @param b an array of 4 unsigned bytes
     * @return a long representing the unsigned int
     */
    public static final long unsignedIntToLong(byte[] b)
    {
        long l = 0;
        l |= b[0] & 0xFF;
        l <<= 8;
        l |= b[1] & 0xFF;
        l <<= 8;
        l |= b[2] & 0xFF;
        l <<= 8;
        l |= b[3] & 0xFF;
        return l;
    }

    //ziskani jmena vendora z bufferu prijatych dat
    private void getDeviceName() {

        if (receivedBytes.size() > 10) {

            String msg = new String();

            for (byte b : receivedBytes) {

                if (b == (byte) 13) break;      // \r ukoncuje jmeno

                msg += (char) b;

            }

            String vendor = msg.substring(7);   //jmeno zacina na 8. pozici

            //zobrazeni jmena v GUI
            TextView vendorText = (TextView) findViewById(R.id.vendorText);
            vendorText.setText(vendor);

        }
    }

    //zobrazuje objekty v GUI podle stavu pripojeni
    private void setConnectionStatus(boolean status) {
        TextView elektroText = (TextView) findViewById(R.id.elektroText);
        TextView vendorText = (TextView) findViewById(R.id.vendorText);
        Button connectionButton = (Button) findViewById(R.id.connectionButton);

        int errC = ContextCompat.getColor(getApplicationContext(), R.color.err);
        int okC = ContextCompat.getColor(getApplicationContext(), R.color.ok);

        if (status) {
            //pripojit

            this.connected = true;
            connectionButton.setText("Odpojit");
            elektroText.setText("Připojen");
            elektroText.setTextColor(okC);
        }
        else {
            //odpojit

            this.connected = false;
            connectionButton.setText("Připojit");
            elektroText.setText("Odpojen");
            elektroText.setTextColor(errC);
            vendorText.setText("");

            reset();

        }

    }

    //----------------------------------------------------------------------------------------------
    //debugovaci metody
    private void printDebugText(String text)  {

        TextView tv = (TextView) findViewById(R.id.DebugText);
        tv.setText(text);

    }

    private void printRawData(Vector<Byte> data) {
        String text = "";
        String add;

        char c;

        for (byte b : data) {

            c = (char) (b & 0xFF);

            switch (c) {
                case 6:
                    add = "ACK";
                    break;
                case 10:
                    add = "LF";
                    break;
                case 13:
                    add = "CR";
                    break;
                default:
                    add = "" + c;
                    break;
            }
            text += (b & 0xFF) + " (" + add + ") | ";

        }

        printDebugText(text);
    }

}
