package com.milestonesys.mobilesdk.livevideosample;

import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;

import com.milestonesys.xpmobilesdk.*;
import com.milestonesys.xpmobilesdk.callbacks.*;
import com.milestonesys.xpmobilesdk.communication.*;

import java.io.ByteArrayInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Vector;

/**
 * An activity that plays live video using XPMobileSDK
 */

public class MainActivity extends AppCompatActivity implements VideoReceiver {

    // The predefined id of the "all cameras" view
    private static final String ALL_CAMERAS_VIEW_ID = "bb16cc8f-a2c5-44f8-9d20-6e9ac57806f5";

    // Communication paths (appended to server address; normally shouldn't be changed)
    private static final String communicationAlias = "XProtectMobile";

    // Connection settings (server address, user and password)
    private String address = "";
    private String user = "";
    private String pass = "";
    private int port = 8081;    // The default value for HTTP

    // Keys used to store connection settings into shared preferences
    private final String address_key = "address";
    private final String port_key = "port";
    private final String user_key = "user";
    private final String pass_key = "pass";

    // Connection object (MobileSDK instance)
    private XPMobileSDK xpMobileSDK;

    // Image used to display video
    private ImageView videoview = null;

    private static final String LOG_TAG = MainActivity.class.getSimpleName();

    // Activity lifecycle =========================================================================

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        videoview = (ImageView) findViewById(R.id.videoView);
        loadSettings();
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Make sure to stop video and disconnect so we don't waste bandwidth while in background
        disconnect();
    }

   @Override
    protected void onResume() {
        super.onResume();
        // If we do not have connection details, bring up the settings dialog
        if( address == null || address.isEmpty() ){
            showSettings();
        } else {
            // Auto-connect to the mobile server
            connect();
        }
    }

    // Connection Management ======================================================================

    private void connect() {
        URL connectionUrl = null;

        try {
            // For this sample, we assume using HTTP
            connectionUrl = new URL("http", address, port, "" );
        } catch (MalformedURLException e) {
            e.printStackTrace();
            showStatus(getString(R.string.bad_address));
            return;
        }

        // Create global connection object. We're using HTTP so there is no need to specify HTTPS callback and certificate
        xpMobileSDK = new XPMobileSDK(this, connectionUrl, communicationAlias,
            null, null, false,
                null,
                /* Uncomment this if you want to process Live Messages from server

                new LiveMsgCallback() {
                    @Override
                    public void liveMessageResponse(CommunicationCommand communicationCommand) {
                        Log.d(LOG_TAG, "LiveMessage");
                    }

                    @Override
                    public void messageFromServer(String s, String s1, List<String> list, String s2) {
                        Log.d(LOG_TAG, "messageFromServer");
                    }
                },
                */
            false);

        // Start the connection sequence (better do it from a separate thread so we do not block the UI)
        showStatus(getString(R.string.connecting));

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    xpMobileSDK.connect(new SuccessCallback() {
                        @Override
                        public void onSuccess(CommunicationCommand response) {
                            // Connected. Proceed with login.
                            login();
                        }
                    }, new ErrorCallback() {
                   @Override
                   public void onErrorOccurred(CommunicationCommand response) {
                            Log.d(LOG_TAG,"Error connecting: " + (response == null ? "?" : response.getError()));
                            showStatus(getString(R.string.connect_fail));
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    showStatus(getString(R.string.connect_fail));
                }
            }
        }).start();
    }


    /**
     * Login to the connected server (use stored user credentials)
     */
    private void login(){
        showStatus(getString(R.string.loggin_in));

        xpMobileSDK.logIn( user, pass, new SuccessCallback() {
            @Override
            public void onSuccess(CommunicationCommand response) {
                showStatus(getString(R.string.loading_cameras));

                // Get All Cameras view (the view that holds all the cameras in the system)
                xpMobileSDK.getViews(ALL_CAMERAS_VIEW_ID,
                    new SuccessCallback()
                    {
                        @Override
                        public void onSuccess(CommunicationCommand response) {
                            Vector<CommunicationCommand.SubItem> cameras = response.getSubItems();
                            Log.d( LOG_TAG, "Received All Cameras view with " + cameras.size() + " cameras");
                            // Select a random camera to show
                            Random r = new Random();
                            int camera_n = r.nextInt(cameras.size());
                            CommunicationCommand.SubItem camera = cameras.get(camera_n);
                            showStatus(getString(R.string.loading_camera) + camera.getName());
                            requestVideo(camera.getId());
                        }
                    },
                    new ErrorCallback() {
                        @Override
                        public void onErrorOccurred(CommunicationCommand response) {
                            Log.d(LOG_TAG,"Error getting cameras: " + (response == null ? "?" : response.getError()));
                            showStatus( getString(R.string.error_getting_cameras));
                        }
                    }
                );
            }
        }, new ErrorCallback() {
            @Override
            public void onErrorOccurred(CommunicationCommand response) {
                Log.d(LOG_TAG, "Error Login: " + (response == null ? "?" : response.getError()));
                showStatus( getString(R.string.login_fail));
            }
        });
    }

    /**
     * Request video from the selected camera
     * @param camera_id The id of the camera
     */
    private void requestVideo( String camera_id ){
        HashMap<String, String> videoProps = new HashMap<String, String>();
        // Use a default width & height
        videoProps.put(CommunicationCommand.PARAM_WIDTH, "640");
        videoProps.put(CommunicationCommand.PARAM_HEIGHT, "480");

        HashMap<String, Object> allProperties = new HashMap<>();
        allProperties.put(LiveVideo.CAMERA_ID_PROPERTY, camera_id );
        allProperties.put(LiveVideo.VIDEO_PROPERTIES, videoProps );
        LiveVideo liveVideo = new LiveVideo(xpMobileSDK, this, allProperties );
        liveVideo.setIsPull(true);
        ErrorState rslt = liveVideo.requestVideo();
        if( rslt == null || rslt.getErrorCode() != 0 ){
            showStatus(getString(R.string.video_error));
        }
    }


    /**
     * Called on each received video frame
     * @param videoCommand the server response containing the frame (headers + image)
     */
    @Override
    public void receiveVideo(final VideoCommand videoCommand) {
        // The image data are contained in the payload
        if (videoCommand.getPayloadSize() > 0) {
            ByteArrayInputStream bais = new ByteArrayInputStream(videoCommand.getPayload(), 0, videoCommand.getPayloadSize());
            if( bais != null ) {
                final Bitmap bmp = BitmapFactory.decodeStream(bais);
                try {
                    if (bmp != null) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                videoview.setImageBitmap(bmp);
                            }
                        });
                    }
                    bais.close();
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d(LOG_TAG, "Error decoding image");
                }
            }
        }
    }

    /**
     * Disconnect from the server.
     */

    private void disconnect() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if( xpMobileSDK != null ) {
                    xpMobileSDK.closeCommunication();
                }
            }
        }).start();
        showStatus(getString(R.string.status_disconnected));
    }

    // Options menu handling ======================================================================

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu );
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId() ){
            case R.id.settings_menu_item:
                showSettings();
                break;

            case R.id.about_menu_item:
                showAboutDlg();
                break;

            default:
                return false;
        }
        return true;
    }

    // Settings ===================================================================================

    /**
     * Bring up the connections settings menu (let the user enter the server address, user name
     * and password)
     */

    private void showSettings() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.connection_settings);
        final View view = getLayoutInflater().inflate(R.layout.connection_form, null);
        final EditText url_field = (EditText)view.findViewById(R.id.txt_URL);
        final EditText port_field = (EditText)view.findViewById(R.id.txt_port);
        final EditText user_field = (EditText)view.findViewById(R.id.txt_user);
        final EditText pwd_field = (EditText)view.findViewById(R.id.txt_password);

        url_field.setText(address);
        port_field.setText(String.valueOf(port));
        user_field.setText(user);
        pwd_field.setText(pass);

        builder.setView(view);
        builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        address = url_field.getText().toString();
                        port = Integer.valueOf(port_field.getText().toString());
                        user = user_field.getText().toString();
                        pass = pwd_field.getText().toString();
                        saveSettings();
                        disconnect();
                        connect();
                    }
                }
        );
        builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) { }
        });

        AlertDialog dialog = builder.create();
        dialog.show();
    }

    /**
     * Display application name & version number
     */
    private void showAboutDlg() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("Live Video Sapmple of Milestone MobileSDK.")
                .setTitle(R.string.app_long_name);
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    /**
     * Status message overlay. Can be called from any thread.
     * @param msg Message text to display
     */
    private void showStatus( final String msg ) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ((TextView) findViewById(R.id.status_txt)).setText(msg);
                videoview.setImageResource(android.R.color.transparent);
            }
        });
    }

    /**
     * Load stored connection settings
     */
    private void loadSettings() {
        SharedPreferences preferences = getPreferences(MODE_PRIVATE);
        address = preferences.getString(address_key, address);
        port = preferences.getInt(port_key, port);
        user = preferences.getString(user_key,user);
        pass = preferences.getString(pass_key,pass);
    }

    /**
     * Store connection settings
     */
    private void saveSettings() {
        SharedPreferences preferences = getPreferences(MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString(address_key, address);
        editor.putInt(port_key, port);
        editor.putString(user_key, user);
        editor.putString(pass_key, pass);
        editor.commit();
    }
}
