/*
 * Copyright (c) 2012-2016 Arne Schwabe
 * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
 */

package de.blinkt.openvpn.fragments;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.routeone.dc.vpn.DcUtils;
import com.routeone.dc.vpn.Route1ConfigConverter;

import java.util.Collection;
import java.util.Comparator;
import java.util.TreeSet;

import de.blinkt.openvpn.LaunchVPN;
import de.blinkt.openvpn.R;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.activities.ConfigConverter;
import de.blinkt.openvpn.activities.DisconnectVPN;
import de.blinkt.openvpn.activities.FileSelect;
import de.blinkt.openvpn.activities.VPNPreferences;
import de.blinkt.openvpn.core.ICSOpenVPNApplication;
import de.blinkt.openvpn.core.ProfileManager;
import de.blinkt.openvpn.core.VpnStatus;


public class VPNProfileList extends ListFragment implements OnClickListener, VpnStatus.StateListener
{
    /*
     * This section is a derivative work from the original project.
     *
     * -----  Beginning of Section  -----
     *
     * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
     *
     * You may use, distribute and modify this code under the
     * terms of the GNU General Public License version 2
     * as published by the Free Software Foundation.

     * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
     */
    private static final String TAG = VPNProfileList.class.getSimpleName();
    /*
     * -----  End of Section  -----
     * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
     */
    public final static int RESULT_VPN_DELETED = Activity.RESULT_FIRST_USER;
    public final static int RESULT_VPN_DUPLICATE = Activity.RESULT_FIRST_USER + 1;

    private static final int MENU_ADD_PROFILE = Menu.FIRST;

    private static final int START_VPN_CONFIG = 92;
    private static final int SELECT_PROFILE = 43;
    private static final int IMPORT_PROFILE = 231;
    private static final int IMPORT_ROUTE1_PROFILE = 232;
    private static final int FILE_PICKER_RESULT_KITKAT = 392;
    private static final int MENU_IMPORT_PROFILE = Menu.FIRST + 1;
    private String mLastStatusMessage;
    /*
     * This section is a derivative work from the original project.
     *
     * -----  Beginning of Section  -----
     *
     * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
     *
     * You may use, distribute and modify this code under the
     * terms of the GNU General Public License version 2
     * as published by the Free Software Foundation.

     * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
     */
    private AlertDialog mDialog;
    private String mPin;
    /*
     * -----  End of Section  -----
     * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
     */
    @Override
    public void updateState(String state, String logmessage, final int localizedResId, VpnStatus.ConnectionStatus level) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mLastStatusMessage = VpnStatus.getLastCleanLogMessage(getActivity());
                mArrayadapter.notifyDataSetChanged();
            }
        });
    }


    class VPNArrayAdapter extends ArrayAdapter<VpnProfile> {

        public VPNArrayAdapter(Context context, int resource,
                               int textViewResourceId) {
            super(context, resource, textViewResourceId);
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            View v = super.getView(position, convertView, parent);

            final VpnProfile profile = (VpnProfile) getListAdapter().getItem(position);

            View titleview = v.findViewById(R.id.vpn_list_item_left);
            titleview.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    startOrStopVPN(profile);
                }
            });

            View settingsview = v.findViewById(R.id.quickedit_settings);
            settingsview.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    editVPN(profile);

                }
            });

            TextView subtitle = (TextView) v.findViewById(R.id.vpn_item_subtitle);
            if (ProfileManager.getLastConnectedVpn() == profile) {
                subtitle.setText(mLastStatusMessage);
                subtitle.setVisibility(View.VISIBLE);
            } else {
                subtitle.setText("");
                subtitle.setVisibility(View.GONE);
            }
            return v;
        }
    }

    private void startOrStopVPN(VpnProfile profile) {
        if (VpnStatus.isVPNActive() && ProfileManager.getLastConnectedVpn() == profile) {
            Intent disconnectVPN = new Intent(getActivity(), DisconnectVPN.class);
            startActivity(disconnectVPN);
        } else {
            startVPN(profile);
        }
    }


    private ArrayAdapter<VpnProfile> mArrayadapter;

    protected VpnProfile mEditProfile = null;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);

/*
 * This section is a derivative work from the original project.
 *
 * -----  Beginning of Section  -----
 *
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 *
 * You may use, distribute and modify this code under the
 * terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.

 * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
 */
        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }
        mDialog = null;
/*
 * -----  End of Section  -----
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 */
    }


    class MiniImageGetter implements ImageGetter {


        @Override
        public Drawable getDrawable(String source) {
            Drawable d = null;
            if ("ic_menu_add".equals(source))
                d = getActivity().getResources().getDrawable(R.drawable.ic_menu_add_grey);
            else if ("ic_menu_archive".equals(source))
                d = getActivity().getResources().getDrawable(R.drawable.ic_menu_import_grey);


            if (d != null) {
                d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
                return d;
            } else {
                return null;
            }
        }
    }


    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume()");
        setListAdapter();
        VpnStatus.addStateListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause()");
        VpnStatus.removeStateListener(this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.vpn_profile_list, container, false);
/*
 * This section is a derivative work from the original project.
 *
 * -----  Beginning of Section  -----
 *
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 *
 * You may use, distribute and modify this code under the
 * terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.

 * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
 */

        LinearLayout empty_default = (LinearLayout) v.findViewById(R.id.empty_default);
        if(ICSOpenVPNApplication.route1Configuration) {
            empty_default.setVisibility(View.GONE);
            empty_default.setVisibility(View.VISIBLE);
        } else {
            empty_default.setVisibility(View.VISIBLE);
            empty_default.setVisibility(View.GONE);
        /*
 * -----  End of Section  -----
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 */
            TextView newvpntext = (TextView) v.findViewById(R.id.add_new_vpn_hint);
            TextView importvpntext = (TextView) v.findViewById(R.id.import_vpn_hint);
            newvpntext.setText(Html.fromHtml(getString(R.string.add_new_vpn_hint), new MiniImageGetter(), null));
            importvpntext.setText(Html.fromHtml(getString(R.string.vpn_import_hint), new MiniImageGetter(), null));
            ImageButton fab_add = (ImageButton) v.findViewById(R.id.fab_add);
            ImageButton fab_import = (ImageButton) v.findViewById(R.id.fab_import);
            if (fab_add != null)
                fab_add.setOnClickListener(this);
            if (fab_import != null)
                fab_import.setOnClickListener(this);
        }
        return v;

    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setListAdapter();

        // We need to call importFromMDMIfRequired() here only if it's the very first launch of application
        // (it will popup the PIN dialog) and not to call it again since user is pushed either
        // to provide a PIN or cancel the PIN dialog. So, just check the flag we set on app start.
        if(!ICSOpenVPNApplication.getInstance().pinDialogDoneOnStartup) {
            importFromMDMIfRequired();
        }
    }
    
    public void importFromMDMIfRequired()
    {
        boolean isExists = false;
        for(int i=0; i<mArrayadapter.getCount(); i++)
        {
            VpnProfile vpnProfile = mArrayadapter.getItem(i);
            if(vpnProfile.getName().equals(Route1ConfigConverter.configName))
            {
                isExists = true;
                break;
            }                
        }
        if(!isExists)
            startImportConfigFromMDM();
    }

    static class VpnProfileNameComparator implements Comparator<VpnProfile> {

        @Override
        public int compare(VpnProfile lhs, VpnProfile rhs) {
            if (lhs == rhs)
                // Catches also both null
                return 0;

            if (lhs == null)
                return -1;
            if (rhs == null)
                return 1;

            if (lhs.mName == null)
                return -1;
            if (rhs.mName == null)
                return 1;

            return lhs.mName.compareTo(rhs.mName);
        }

    }

    private void setListAdapter() {
        if (mArrayadapter == null) {
            mArrayadapter = new VPNArrayAdapter(getActivity(), R.layout.vpn_list_item, R.id.vpn_item_title);

        }
        Collection<VpnProfile> allvpn = getPM().getProfiles();
        TreeSet<VpnProfile> sortedset = new TreeSet<VpnProfile>(new VpnProfileNameComparator());
        sortedset.addAll(allvpn);
        mArrayadapter.clear();
        mArrayadapter.addAll(sortedset);

        setListAdapter(mArrayadapter);
        mArrayadapter.notifyDataSetChanged();
    }


    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.add(0, MENU_ADD_PROFILE, 0, R.string.menu_add_profile)
                .setIcon(R.drawable.ic_menu_add)
                .setAlphabeticShortcut('a')
                .setTitleCondensed(getActivity().getString(R.string.add))
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

        menu.add(0, MENU_IMPORT_PROFILE, 0, R.string.menu_import)
                .setIcon(R.drawable.ic_menu_import)
                .setAlphabeticShortcut('i')
                .setTitleCondensed(getActivity().getString(R.string.menu_import_short))
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        final int itemId = item.getItemId();
        if (itemId == MENU_ADD_PROFILE) {
            onAddOrDuplicateProfile(null);
            return true;
        } else if (itemId == MENU_IMPORT_PROFILE) {
            return startImportConfigFilePicker();
        }
/*
 * This section is a derivative work from the original project.
 *
 * -----  Beginning of Section  -----
 *
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 *
 * You may use, distribute and modify this code under the
 * terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.

 * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
 */

        else if (item.getItemId()==R.id.help){
            // open help web page in a browser.
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(ICSOpenVPNApplication.helpURL)));
            return true;
        }
/*
 * -----  End of Section  -----
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 */
        else {
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.fab_import:
                startImportConfigFilePicker();
                break;
            case R.id.fab_add:
                onAddOrDuplicateProfile(null);
                break;
        }
    }

    private boolean startImportConfigFilePicker() {
        boolean startOldFileDialog = true;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            startOldFileDialog = !startFilePicker();

        if (startOldFileDialog)
            startImportConfig();

        return true;
    }
    private boolean startImportConfigFromMDM() {
        if(mPin == null)
        {
            DcUtils.showPinInput(this.getActivity(), mDialog, ICSOpenVPNApplication.getInstance().mDcReceiver, null, true);
        }

        return true;
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private boolean startFilePicker() {

        Intent i = Utils.getFilePickerIntent(getActivity(), Utils.FileType.OVPN_CONFIG);
        if (i != null) {
            startActivityForResult(i, FILE_PICKER_RESULT_KITKAT);
            return true;
        } else
            return false;
    }

    private void startImportConfig() {
        Intent intent = new Intent(getActivity(), FileSelect.class);
        intent.putExtra(FileSelect.NO_INLINE_SELECTION, true);
        intent.putExtra(FileSelect.WINDOW_TITLE, R.string.import_configuration_file);
        startActivityForResult(intent, SELECT_PROFILE);
    }
/*
 * This section is a derivative work from the original project.
 *
 * -----  Beginning of Section  -----
 *
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 *
 * You may use, distribute and modify this code under the
 * terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.

 * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
 */

    private void startImportRoute1Config() {
        //Intent startRoute1Import = new Intent(getActivity(), Route1ConfigConverter.class);
        //startRoute1Import.setAction(Route1ConfigConverter.IMPORT_PROFILE);
        Route1ConfigConverter converter = new Route1ConfigConverter();

        ProfileManager pm = getPM();


        converter.saveProfile(getPM(), getActivity(), "client\n" +
                "dev tun\n" +
                "proto udp\n" +
                "remote 192.168.206.214 443\n" +
                "resolv-retry infinite\n" +
                "nobind\n" +
                "persist-key\n" +
                "persist-tun\n" +
                "Route1DCType KeyManagement\n" +
                "Route1ProfileEditable 1\n" +
                "ca ca.crt\n" +
                "cipher AES-256-CBC\n" +
                "verb 3");
        //startActivityForResult(startRoute1Import, SELECT_PROFILE);


    }
/*
 * -----  End of Section  -----
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 */

    private void onAddOrDuplicateProfile(final VpnProfile mCopyProfile) {
        Context context = getActivity();
        if (context != null) {
            final EditText entry = new EditText(context);
            entry.setSingleLine();

            AlertDialog.Builder dialog = new AlertDialog.Builder(context);
            if (mCopyProfile == null)
                dialog.setTitle(R.string.menu_add_profile);
            else {
                dialog.setTitle(context.getString(R.string.duplicate_profile_title, mCopyProfile.mName));
                entry.setText(getString(R.string.copy_of_profile, mCopyProfile.mName));
            }

            dialog.setMessage(R.string.add_profile_name_prompt);
            dialog.setView(entry);

            dialog.setNeutralButton(R.string.menu_import_short,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            startImportConfigFilePicker();
                        }
                    });

            dialog.setNeutralButton(R.string.menu_route1_import,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            startImportConfigFromMDM();
                        }
                    });

            dialog.setPositiveButton(android.R.string.ok,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            String name = entry.getText().toString();
                            if (getPM().getProfileByName(name) == null) {
                                VpnProfile profile;
                                if (mCopyProfile != null)
                                    profile = mCopyProfile.copy(name);
                                else
                                    profile = new VpnProfile(name);

                                addProfile(profile);
                                editVPN(profile);
                            } else {
                                Toast.makeText(getActivity(), R.string.duplicate_profile_name, Toast.LENGTH_LONG).show();
                            }
                        }


                    });
            dialog.setNegativeButton(android.R.string.cancel, null);
            dialog.create().show();
        }

    }

    private void addProfile(VpnProfile profile) {
        getPM().addProfile(profile);
        getPM().saveProfileList(getActivity());
        getPM().saveProfile(getActivity(), profile);
        mArrayadapter.add(profile);
    }

    private ProfileManager getPM() {
        return ProfileManager.getInstance(getActivity());
    }


    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_VPN_DELETED) {
            if (mArrayadapter != null && mEditProfile != null)
                mArrayadapter.remove(mEditProfile);
        } else if (resultCode == RESULT_VPN_DUPLICATE && data != null) {
            String profileUUID = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID);
            VpnProfile profile = ProfileManager.get(getActivity(), profileUUID);
            if (profile != null)
                onAddOrDuplicateProfile(profile);
        }


        if (resultCode != Activity.RESULT_OK)
            return;


        if (requestCode == START_VPN_CONFIG) {
            String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID);

            VpnProfile profile = ProfileManager.get(getActivity(), configuredVPN);
            getPM().saveProfile(getActivity(), profile);
            // Name could be modified, reset List adapter
            setListAdapter();

        } else if (requestCode == SELECT_PROFILE) {
            String fileData = data.getStringExtra(FileSelect.RESULT_DATA);
            Uri uri = new Uri.Builder().path(fileData).scheme("file").build();

            startConfigImport(uri);
        } else if (requestCode == IMPORT_PROFILE) {
            String profileUUID = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID);
            mArrayadapter.add(ProfileManager.get(getActivity(), profileUUID));
        } else if (requestCode == IMPORT_ROUTE1_PROFILE) {
            String profileUUID = data.getStringExtra(VpnProfile.EXTRA_ROUTE1PROFILEUUID);
            mArrayadapter.add(ProfileManager.get(getActivity(), profileUUID));
        } else if (requestCode == FILE_PICKER_RESULT_KITKAT) {
            if (data != null) {
                Uri uri = data.getData();
                startConfigImport(uri);
            }
        }

    }

    private void startConfigImport(Uri uri) {
        Intent startImport = new Intent(getActivity(), ConfigConverter.class);
        startImport.setAction(ConfigConverter.IMPORT_PROFILE);
        startImport.setData(uri);
        startActivityForResult(startImport, IMPORT_PROFILE);
    }


    private void editVPN(VpnProfile profile) {
        mEditProfile = profile;
        Intent vprefintent = new Intent(getActivity(), VPNPreferences.class)
                .putExtra(getActivity().getPackageName() + ".profileUUID", profile.getUUID().toString());

        startActivityForResult(vprefintent, START_VPN_CONFIG);
    }

    private void startVPN(final VpnProfile profile) {

        getPM().saveProfile(getActivity(), profile);
/*
 * This section is a derivative work from the original project.
 *
 * -----  Beginning of Section  -----
 *
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 *
 * You may use, distribute and modify this code under the
 * terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.

 * Contributor:  A. M. Jehad Sarkar  <amjehad.sarkar@route1.com>
 */

        if(profile.mAuthenticationType == VpnProfile.TYPE_DERIVID_CERTIFICATES || (profile.mProfileCreator != null && profile.mProfileCreator.equalsIgnoreCase("route1"))) {
            ICSOpenVPNApplication.getInstance().mDcReceiver.setOnRetrieveCredential(
                new Runnable() {
                    @Override
                    public void run() {
                        profile.setDerivedCertificates(ICSOpenVPNApplication.getInstance().mDcReceiver.getCertificates());
                        profile.setPrivateKey(ICSOpenVPNApplication.getInstance().mDcReceiver.getPrivateKey());
                        startLaunchVPN(profile);
                        ICSOpenVPNApplication.getInstance().mDcReceiver.setOnRetrieveCredential(null);
                    }
                }
            );
            DcUtils.setDCType(getActivity(), mDialog, ICSOpenVPNApplication.getInstance().mDcReceiver, profile.mDCType, profile);
        }
        else
 /*
 * -----  End of Section  -----
 * Copyright (c) 2016 Route1 Inc.  All rights reserved.  <sales@route1.com>
 */
        {
            startLaunchVPN(profile);
        }
    }

    private void startLaunchVPN(VpnProfile profile) {
        Intent intent = new Intent(getActivity(), LaunchVPN.class);
        intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString());
        intent.setAction(Intent.ACTION_MAIN);
        startActivity(intent);
    }
}
