package com.github.andlyticsproject;


import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.support.v4.app.LoaderManager;
import android.content.Context;
import android.support.v4.content.Loader;
import android.os.Bundle;
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.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewSwitcher;

import com.github.andlyticsproject.Preferences.Timeframe;
import com.github.andlyticsproject.adsense.AdSenseClient;
import com.github.andlyticsproject.chart.Chart.ChartSet;
import com.github.andlyticsproject.db.AndlyticsDb;
import com.github.andlyticsproject.model.AdmobStats;
import com.github.andlyticsproject.model.AdmobStatsSummary;
import com.github.andlyticsproject.model.StatsSummary;
import com.github.andlyticsproject.util.DetachableAsyncTask;
import com.github.andlyticsproject.util.LoaderBase;
import com.github.andlyticsproject.util.LoaderResult;
import com.github.andlyticsproject.util.Utils;
import com.github.andlyticsproject.view.ViewSwitcher3D;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class AdmobFragment extends ChartFragment<AdmobStats> implements
		LoaderManager.LoaderCallbacks<LoaderResult<AdmobStatsSummary>> {

	static final String ARG_PACKAGE_NAME = "packageName";
	static final String ARG_TIMEFRAME = "timeframe";
	static final String ARG_LOAD_REMOTE = "loadRemote";

	private static final String TAG = AdmobFragment.class.getSimpleName();

	private AdmobListAdapter admobListAdapter;
	public Integer heighestRatingChange;
	public Integer lowestRatingChange;
	private ViewSwitcher3D mainViewSwitcher;
	private ViewGroup accountList;

	private ViewSwitcher configSwitcher;

	private ViewGroup siteList;

	private LoadAdUnitsTask loadAdUnitsTask;

	private String selectedAdmobAccount;

	static class AdmobDbLoader extends LoaderBase<AdmobStatsSummary> {

		static final String ARG_PACKAGE_NAME = "packageName";
		static final String ARG_TIMEFRAME = "timeframe";
		static final String ARG_LOAD_REMOTE = "loadRemote";

		private ContentAdapter db;
		private String packageName;
		private Timeframe timeframe;
		private boolean loadRemote;

		public AdmobDbLoader(Context context, String packageName, Timeframe timeframe,
				boolean loadRemote) {
			super(context);
			db = ContentAdapter.getInstance(AndlyticsApp.getInstance());
			this.packageName = packageName;
			this.timeframe = timeframe;
			this.loadRemote = loadRemote;
		}

		@Override
		protected AdmobStatsSummary load() throws Exception {
			if (packageName == null || timeframe == null) {
				return null;
			}

			String[] admobDetails = AndlyticsDb.getInstance(getContext()).getAdmobDetails(
					packageName);
			if (admobDetails == null) {
				Log.w(TAG, "Admob account and site ID not founf for " + packageName);
				return null;
			}
			//
			String currentAdmobAccount = admobDetails[0];
			String currentSiteId = admobDetails[1];
			String adUnitId = admobDetails[2];

			Log.d(TAG, "Loading Admob stats...");
			if (loadRemote) {
				AdSenseClient.foregroundSyncStats(getContext(), currentAdmobAccount,
						Arrays.asList(adUnitId));
			}

			Log.d(TAG, "Loading Admob stats from DB...");
			return db.getAdmobStats(currentSiteId, adUnitId, timeframe);
		}

		@Override
		protected void releaseResult(LoaderResult<AdmobStatsSummary> result) {
			// just a string, nothing to do
		}

		@Override
		protected boolean isActive(LoaderResult<AdmobStatsSummary> result) {
			return false;
		}
	}

	public AdmobFragment() {
		setHasOptionsMenu(true);
		setRetainInstance(true);
	}

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

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

		// init loader
		loadCurrentData();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = super.onCreateView(inflater, container, savedInstanceState);

		mainViewSwitcher = new ViewSwitcher3D(
				(ViewGroup) view.findViewById(R.id.base_chart_main_frame));
		mainViewSwitcher.setListener(this);

		admobListAdapter = new AdmobListAdapter(getActivity());

		setAdapter(admobListAdapter);

		String[] admobDetails = AndlyticsDb.getInstance(getActivity()).getAdmobDetails(
				statsActivity.getPackage());
		if (admobDetails == null) {
			mainViewSwitcher.swap();
			if (configSwitcher.getCurrentView().getId() != R.id.base_chart_config) {
				configSwitcher.showPrevious();
			}

			showAccountList();
		}

		return view;
	}

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

		Log.d(TAG, "onResume()");

		if (statsActivity.shouldRemoteUpdateStats()) {
			loadRemoteData();
		} else {
			loadCurrentData();
		}
	}


	private void loadRemoteData() {
		loadData(getCurrentTimeFrame(), true);
	}

	private void loadData(Timeframe timeframe, boolean loadRemote) {
		Bundle args = new Bundle();
		args.putString(ARG_PACKAGE_NAME, statsActivity.getPackage());
		args.putSerializable(ARG_TIMEFRAME, timeframe);
		args.putBoolean(ARG_LOAD_REMOTE, loadRemote);
		statsActivity.refreshStarted();

		Log.d(TAG, "Restarting loader");
		getLoaderManager().restartLoader(0, args, this);
	}

	@Override
	protected void loadCurrentData() {
		Bundle args = new Bundle();
		args.putString(ARG_PACKAGE_NAME, statsActivity.getPackage());
		args.putSerializable(ARG_TIMEFRAME, getCurrentTimeFrame());
		args.putBoolean(ARG_LOAD_REMOTE, false);
		statsActivity.refreshStarted();

		getLoaderManager().initLoader(0, args, this);
	}

	@Override
	public void updateView(StatsSummary<AdmobStats> statsSummary) {
		admobListAdapter.setOverallStats(statsSummary.getOverallStats());

		List<AdmobStats> admobStats = statsSummary.getStats();
		loadChartData(admobStats);
		// make shallow copy
		List<AdmobStats> reversedAdmobStats = new ArrayList<AdmobStats>();
		reversedAdmobStats.addAll(admobStats);
		Collections.reverse(reversedAdmobStats);

		admobListAdapter.setStats(reversedAdmobStats);
		// admobListAdapter.setCurrentChart(currentChart);
		admobListAdapter.notifyDataSetChanged();

		restoreChartSelection();
	}

	@Override
	public void setupListAdapter(ChartListAdapter<AdmobStats> listAdapter,
			StatsSummary<AdmobStats> statsSummary) {
		// nothing to do
	}

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

		// The CursorLoader example doesn't do this, but if we get an update
		// while the UI is
		// destroyed, it will crash. Why is this necessary?
		getLoaderManager().destroyLoader(0);
	}

	@Override
	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
		menu.clear();
		inflater.inflate(R.menu.admob_fragment_menu, menu);
		//
		MenuItem activeTimeFrame = null;
		switch (currentTimeFrame) {
		case LAST_SEVEN_DAYS:
			activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframe7);
			break;
		case LAST_THIRTY_DAYS:
			activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframe30);
			break;
		case LAST_NINETY_DAYS:
			activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframe90);
			break;
		case UNLIMITED:
			activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframeUnlimited);
			break;
		case MONTH_TO_DATE:
			activeTimeFrame = menu.findItem(R.id.itemAdmobsmenuTimeframeMonthToDate);
			break;
		}
		activeTimeFrame.setChecked(true);

		String[] admobDetails = AndlyticsDb.getInstance(getActivity()).getAdmobDetails(
				statsActivity.getPackage());

		if (statsActivity.isRefreshing()) {
			menu.findItem(R.id.itemAdmobsmenuRefresh).setActionView(
					R.layout.action_bar_indeterminate_progress);
		}
		if (admobDetails == null) {
			menu.findItem(R.id.itemAdmobsmenuRemove).setVisible(false);
			menu.findItem(R.id.itemAdmobsmenuTimeframe).setVisible(false);
			menu.findItem(R.id.itemAdmobsmenuRefresh).setVisible(statsActivity.isRefreshing());
		}
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Context ctx = getActivity();
		if (ctx == null) {
			return false;
		}

		switch (item.getItemId()) {
		case R.id.itemAdmobsmenuRefresh:
			setChartIgnoreCallLayouts(true);
			loadRemoteEntries();
			return true;
		case R.id.itemAdmobsmenuRemove:
			AndlyticsDb.getInstance(getActivity()).saveAdmobDetails(statsActivity.getPackage(),
					null, null);
			showAccountList();
			if (configSwitcher.getCurrentView().getId() != R.id.base_chart_config) {
				configSwitcher.showPrevious();
			}
			mainViewSwitcher.swap();
			getActivity().invalidateOptionsMenu();
			return true;
		case R.id.itemAdmobsmenuTimeframe7:
			currentTimeFrame = Timeframe.LAST_SEVEN_DAYS;
			executeLoadData(currentTimeFrame);
			Preferences.saveChartTimeframe(Timeframe.LAST_SEVEN_DAYS, ctx);
			item.setChecked(true);
			return true;
		case R.id.itemAdmobsmenuTimeframe30:
			currentTimeFrame = Timeframe.LAST_THIRTY_DAYS;
			executeLoadData(currentTimeFrame);
			Preferences.saveChartTimeframe(Timeframe.LAST_THIRTY_DAYS, ctx);
			item.setChecked(true);
			return true;
		case R.id.itemAdmobsmenuTimeframe90:
			currentTimeFrame = Timeframe.LAST_NINETY_DAYS;
			executeLoadData(currentTimeFrame);
			Preferences.saveChartTimeframe(Timeframe.LAST_NINETY_DAYS, ctx);
			item.setChecked(true);
			return true;
		case R.id.itemAdmobsmenuTimeframeUnlimited:
			currentTimeFrame = Timeframe.UNLIMITED;
			executeLoadData(currentTimeFrame);
			Preferences.saveChartTimeframe(Timeframe.UNLIMITED, ctx);
			item.setChecked(true);
			return true;
		case R.id.itemAdmobsmenuTimeframeMonthToDate:
			currentTimeFrame = Timeframe.MONTH_TO_DATE;
			executeLoadData(currentTimeFrame);
			Preferences.saveChartTimeframe(Timeframe.MONTH_TO_DATE, ctx);
			item.setChecked(true);
			return true;
		default:
			return (super.onOptionsItemSelected(item));
		}
	}

	private void loadRemoteEntries() {
		loadData(currentTimeFrame, true);
	}

	@Override
	protected void executeLoadData(Timeframe currentTimeFrame) {
		loadData(currentTimeFrame, false);
	}

	@Override
	protected String getChartHint() {
		return "8 " + this.getString(R.string.admob__charts_available) + " ->";
	}

	private void showAccountList() {
		final AccountManager manager = AccountManager.get(getActivity());
		final Account[] accounts = manager.getAccountsByType("com.google");
		final int size = accounts.length;
		String[] names = new String[size];
		accountList.removeAllViews();
		for (int i = 0; i < size; i++) {
			names[i] = accounts[i].name;

			View inflate = getActivity().getLayoutInflater().inflate(
					R.layout.admob_account_list_item, null);
			TextView accountName = (TextView) inflate
					.findViewById(R.id.admob_account_list_item_text);
			accountName.setText(accounts[i].name);
			inflate.setTag(accounts[i].name);
			inflate.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View view) {
					String currentAdmobAccount = (String) view.getTag();
					selectedAdmobAccount = currentAdmobAccount;
					configSwitcher.showNext();
					loadAdUnits();

				}
			});
			accountList.addView(inflate);
		}
	}

	void loadAdUnits() {
		if (getActivity() == null) {
			return;
		}

		// can't have two loaders with different interface, use 
		// AsyncTask and retained fragment
		if (loadAdUnitsTask != null) {
			loadAdUnitsTask.cancel(true);
			loadAdUnitsTask = null;
		}

		loadAdUnitsTask = new LoadAdUnitsTask(getActivity(), this, selectedAdmobAccount);
		Utils.execute(loadAdUnitsTask);
	}

	@Override
	public ChartSet getChartSet() {
		return ChartSet.ADMOB;
	}

	@Override
	public String getTitle() {
		// this can be called before the fragment is attached
		Context ctx = AndlyticsApp.getInstance();
		return ctx.getString(R.string.admob);
	}

	private void loadChartData(List<AdmobStats> statsForApp) {
		if (statsForApp != null && statsForApp.size() > 0) {
			updateCharts(statsForApp);

			DateFormat dateFormat = Preferences.getDateFormatLong(getActivity());
			if (statsForApp.size() > 0) {

				timetext = dateFormat.format(statsForApp.get(0).getDate()) + " - "
						+ dateFormat.format(statsForApp.get(statsForApp.size() - 1).getDate());
				updateChartHeadline();
			}

			// chartFrame.showNext();

		}
	}

	@Override
	protected void notifyChangedDataformat() {
		loadData(getCurrentTimeFrame(), false);
	}

	@Override
	protected List<View> getExtraFullViews(View view) {
		configSwitcher = (ViewSwitcher) view.findViewById(R.id.base_chart_viewswitcher_config);
		configSwitcher.setInAnimation(AnimationUtils.loadAnimation(getActivity(),
				R.anim.slide_in_right));
		configSwitcher.setOutAnimation(AnimationUtils.loadAnimation(getActivity(),
				R.anim.slide_out_left));
		List<View> ret = new ArrayList<View>();

		RelativeLayout container = (RelativeLayout) getActivity().getLayoutInflater().inflate(
				R.layout.admob_config_selectapp, null);
		siteList = (ViewGroup) container.findViewById(R.id.admob_sitelist);
		ret.add(container);

		container = (RelativeLayout) getActivity().getLayoutInflater().inflate(
				R.layout.admob_config_addaccount, null);
		accountList = (ViewGroup) container.findViewById(R.id.admob_accountlist);
		ret.add(container);

		return ret;

	}

	@Override
	public Loader<LoaderResult<AdmobStatsSummary>> onCreateLoader(int id, Bundle args) {
		String packageName = null;
		Timeframe timeframe = null;
		boolean loadRemote = false;
		if (args != null) {
			packageName = args.getString(ARG_PACKAGE_NAME);
			timeframe = (Timeframe) args.getSerializable(ARG_TIMEFRAME);
			loadRemote = args.getBoolean(ARG_LOAD_REMOTE);
		}

		return new AdmobDbLoader(getActivity(), packageName, timeframe, loadRemote);
	}

	@Override
	public void onLoadFinished(Loader<LoaderResult<AdmobStatsSummary>> loader,
			LoaderResult<AdmobStatsSummary> result) {
		statsActivity.refreshFinished();

		if (result.isFailed()) {
			statsActivity.handleUserVisibleException(result.getError());

			return;
		}

		if (result.getData() == null) {
			return;
		}

		updateView(result.getData());
	}

	@Override
	public void onLoaderReset(Loader<LoaderResult<AdmobStatsSummary>> arg0) {
	}

	@Override
	public void initLoader(Bundle args) {
		// NOOP, to fulfill ChartFragment interface
	}

	@Override
	public void restartLoader(Bundle args) {
		// NOOP, to fulfill ChartFragment interface
	}

	private static class LoadAdUnitsTask extends
			DetachableAsyncTask<Void, Void, Exception, Activity> {

		// ad unit -> name
		private Map<String, String> adUnits;

		private AdmobFragment admobFragment;
		private DetailedStatsActivity statsActivity;
		private String admobAccount;

		public LoadAdUnitsTask(Activity activity, AdmobFragment admobFragment, String admobAccount) {
			super(activity);
			this.statsActivity = (DetailedStatsActivity) activity;
			this.admobFragment = admobFragment;
			this.admobAccount = admobAccount;
		}

		@Override
		protected void onPreExecute() {
			if (activity == null) {
				return;
			}

			statsActivity.refreshStarted();
		}

		@Override
		protected Exception doInBackground(Void... params) {
			if (activity == null) {
				return null;
			}

			try {
				adUnits = AdSenseClient.getAdUnits(activity, admobAccount);
			} catch (Exception e) {
				return e;
			}

			return null;
		}

		@Override
		protected void onPostExecute(Exception error) {
			if (activity == null) {
				return;
			}

			statsActivity.refreshFinished();

			if (error != null) {
				if (error instanceof UserRecoverableAuthIOException) {
					activity.startActivityForResult(
							((UserRecoverableAuthIOException) error).getIntent(),
							BaseActivity.REQUEST_AUTHORIZATION);

					return;
				} else if (error instanceof GoogleJsonResponseException) {
					GoogleJsonError details = ((GoogleJsonResponseException) error).getDetails();
					String message = error.getMessage();
					if (details != null) {
						message = details.getMessage();
					}
					Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
					admobFragment.configSwitcher.showPrevious();

					return;
				} else if (error.getCause() instanceof GoogleAuthException) {
					String message = ((GoogleAuthException) error.getCause()).getMessage();
					Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
					admobFragment.configSwitcher.showPrevious();

					return;
				}


				statsActivity.handleUserVisibleException(error);
				return;
			}

			if (adUnits != null && adUnits.size() > 0) {
				admobFragment.siteList.removeAllViews();

				Set<String> keySet = adUnits.keySet();
				for (String siteId : keySet) {

					String siteName = adUnits.get(siteId);

					// pull the id from the data
					View inflate = activity.getLayoutInflater().inflate(
							R.layout.admob_account_list_item, null);
					TextView accountName = (TextView) inflate
							.findViewById(R.id.admob_account_list_item_text);
					accountName.setText(siteName);
					inflate.setTag(siteId);
					inflate.setOnClickListener(new OnClickListener() {

						@Override
						public void onClick(View view) {
							String admobAdUnitId = (String) view.getTag();
							AndlyticsDb.getInstance(activity).saveAdmobAdUnitId(
									statsActivity.getPackage(), admobAccount, admobAdUnitId);

							admobFragment.mainViewSwitcher.swap();
							admobFragment.loadRemoteData();
							activity.invalidateOptionsMenu();
						}
					});
					admobFragment.siteList.addView(inflate);

				}
			}
		}
	}

	@Override
	public ChartListAdapter<AdmobStats> createChartAdapter() {
		return new AdmobListAdapter(getActivity());
	}

}
