/*
 * Copyright (C) 2018-2019 Tobias Brunner
 * HSR Hochschule fuer Technik Rapperswil
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package org.strongswan.android.ui;

import android.annotation.TargetApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.IBinder;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;

import org.strongswan.android.R;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
import org.strongswan.android.data.VpnType;
import org.strongswan.android.logic.VpnStateService;
import org.strongswan.android.utils.Constants;

import androidx.preference.PreferenceManager;

@TargetApi(Build.VERSION_CODES.N)
public class VpnTileService extends TileService implements VpnStateService.VpnStateListener
{
	private boolean mListening;
	private VpnProfileDataSource mDataSource;
	private VpnStateService mService;
	private final ServiceConnection mServiceConnection = new ServiceConnection()
	{
		@Override
		public void onServiceDisconnected(ComponentName name)
		{
			mService = null;
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service)
		{
			mService = ((VpnStateService.LocalBinder)service).getService();
			if (mListening && mDataSource != null)
			{
				mService.registerListener(VpnTileService.this);
				updateTile();
			}
		}
	};

	@Override
	public void onCreate()
	{
		super.onCreate();

		Context context = getApplicationContext();
		context.bindService(new Intent(context, VpnStateService.class),
							mServiceConnection, Service.BIND_AUTO_CREATE);

		mDataSource = new VpnProfileDataSource(this);
		mDataSource.open();
	}

	@Override
	public void onDestroy()
	{
		super.onDestroy();

		if (mService != null)
		{
			getApplicationContext().unbindService(mServiceConnection);
		}
		mDataSource.close();
		mDataSource = null;
	}

	@Override
	public void onStartListening()
	{
		super.onStartListening();
		mListening = true;

		if (mService != null)
		{
			mService.registerListener(this);
			updateTile();
		}
	}

	@Override
	public void onStopListening()
	{
		super.onStopListening();
		mListening = false;

		if (mService != null)
		{
			mService.unregisterListener(this);
		}
	}

	private VpnProfile getProfile()
	{
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
		String uuid = pref.getString(Constants.PREF_DEFAULT_VPN_PROFILE, null);
		if (uuid == null || uuid.equals(Constants.PREF_DEFAULT_VPN_PROFILE_MRU))
		{
			uuid = pref.getString(Constants.PREF_MRU_VPN_PROFILE, null);
		}
		return mDataSource != null ? mDataSource.getVpnProfile(uuid) : null;
	}

	@Override
	public void onClick()
	{
		if (mService != null)
		{
			/* we operate on the current/most recently used profile, but fall back to configuration */
			VpnProfile profile = mService.getProfile();
			if (profile == null)
			{
				profile = getProfile();
			}
			else if (mDataSource != null)
			{   /* always get the plain profile without cached password */
				profile = mDataSource.getVpnProfile(profile.getId());
			}
			/* reconnect the profile in case of an error */
			if (mService.getErrorState() == VpnStateService.ErrorState.NO_ERROR)
			{
				switch (mService.getState())
				{
					case CONNECTING:
					case CONNECTED:
						Runnable disconnect = new Runnable()
						{
							@Override
							public void run()
							{
								mService.disconnect();
							}
						};
						if (isLocked())
						{
							unlockAndRun(disconnect);
						}
						else
						{
							disconnect.run();
						}
						return;
				}
			}
			if (profile != null)
			{
				Intent intent = new Intent(this, VpnProfileControlActivity.class);
				intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				intent.setAction(VpnProfileControlActivity.START_PROFILE);
				intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
				if (profile.getVpnType().has(VpnType.VpnTypeFeature.USER_PASS) &&
					profile.getPassword() == null)
				{	/* the user will have to enter the password, so collapse the drawer */
					startActivityAndCollapse(intent);
				}
				else
				{
					startActivity(intent);
				}
				return;
			}
		}
		Intent intent = new Intent(this, MainActivity.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		startActivityAndCollapse(intent);
	}

	@Override
	public void stateChanged()
	{
		updateTile();
	}

	private void updateTile()
	{
		VpnProfile profile = mService.getProfile();
		VpnStateService.State state = mService.getState();
		VpnStateService.ErrorState error = mService.getErrorState();

		/* same as above, only use the configured profile if we have no active profile */
		if (profile == null)
		{
			profile = getProfile();
		}

		Tile tile = getQsTile();
		if (tile == null)
		{
			return;
		}

		if (error != VpnStateService.ErrorState.NO_ERROR)
		{
			tile.setState(Tile.STATE_INACTIVE);
			tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_warning));
			tile.setLabel(getString(R.string.tile_connect));
		}
		else
		{
			switch (state)
			{
				case DISCONNECTING:
				case DISABLED:
					tile.setState(Tile.STATE_INACTIVE);
					tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_disconnected));
					tile.setLabel(getString(R.string.tile_connect));
					break;
				case CONNECTING:
					tile.setState(Tile.STATE_ACTIVE);
					tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_connecting));
					tile.setLabel(getString(R.string.tile_disconnect));
					break;
				case CONNECTED:
					tile.setState(Tile.STATE_ACTIVE);
					tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification));
					tile.setLabel(getString(R.string.tile_disconnect));
					break;
			}
		}
		if (profile != null && !isSecure())
		{
			tile.setLabel(profile.getName());
		}
		tile.updateTile();
	}
}
