package com.artifex.mupdf.android;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;

import com.artifex.mupdf.fitz.Document;
import com.artifex.mupdf.fitz.Link;
import com.artifex.mupdf.fitz.Outline;
import com.artifex.mupdf.fitz.PDFDocument;
import com.artifex.mupdf.fitz.PDFObject;
import com.artifex.mupdf.fitz.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DocActivityView extends FrameLayout implements TabHost.OnTabChangeListener, View.OnClickListener, DocView.SelectionChangeListener
{
	/*
	This class displays a DocView, and optionally, a DocListPagesView to its right.
	Both views show the document content, but the DocListPagesView is narrow,
	and always shows one column.

	There is logic here, and in Docview, DocViewBase and DocListPagesView,
	to keep them both in sync, as follows:

	When the DocListPagesView is showing, and the user taps on one of its pages,
	that page is highlighted, and the DocView on the left is auto-scrolled to bring that page into view.
	When the user scrolls the DocListPagesView, nothing more is done.

	When the user scrolls the DocView on the left, a "most visible" page is determined, and the
	DocListPagesView is auto-scrolled to bring that page into view, and that page is
	highlighted.
	*/

	private DocView mDocView;
	private DocListPagesView mDocPagesView;

	private DocReflowView mDocReflowView;

	private boolean mShowUI = false;

	//  tab tags
	private String mTagHidden;
	private String mTagFile;
	private String mTagAnnotate;
	private String mTagPages;

	private Button mReflowButton;
	private Button mFirstPageButton;
	private Button mLastPageButton;

	private ImageButton mSearchButton;
	private EditText mSearchText;
	private Button mSearchNextButton;
	private Button mSearchPreviousButton;
	private ImageButton mBackButton;

	private Button mSaveButton;
	private Button mSaveAsButton;
	private Button mPrintButton;
	private Button mShareButton;
	private Button mOpenInButton;

	private Button mToggleAnnotButton;
	private Button mHighlightButton;
	private Button mDeleteButton;

	private Button mNoteButton;
	private Button mDrawButton;
	private Button mLineColorButton;
	private Button mLineThicknessButton;

	private Button mProofButton;

	private String mEmbeddedProfile = null;

	private Context mContext;

	public DocActivityView(Context context)
	{
		super(context);
		mContext = context;
	}

	public DocActivityView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		mContext = context;
	}

	public DocActivityView(Context context, AttributeSet attrs, int defStyle)
	{
		super(context, attrs, defStyle);
		mContext = context;
	}

	protected boolean usePagesView()
	{
		return true;
	}

	TabHost tabHost;
	protected void setupTabs()
	{
		tabHost = (TabHost) findViewById(R.id.tabhost);
		tabHost.setup();

		//  get the tab tags.
		mTagHidden = getResources().getString(R.string.hidden_tab);
		mTagFile = getResources().getString(R.string.file_tab);
		mTagAnnotate = getResources().getString(R.string.annotate_tab);
		mTagPages = getResources().getString(R.string.pages_tab);

		//  first tab is and stays hidden.
		//  when the search tab is selected, we programmatically "select" this hidden tab
		//  which results in NO tabs appearing selected in this tab host.
		setupTab(tabHost, mTagHidden, R.id.hiddenTab, R.layout.tab);
		tabHost.getTabWidget().getChildTabViewAt(0).setVisibility(View.GONE);

		//  these tabs are shown.
		setupTab(tabHost, mTagFile, R.id.fileTab, R.layout.tab_left);
		setupTab(tabHost, mTagAnnotate, R.id.annotateTab, R.layout.tab);
		setupTab(tabHost, mTagPages, R.id.pagesTab, R.layout.tab_right);

		//  start by showing the edit tab
		tabHost.setCurrentTabByTag(mTagFile);

		tabHost.setOnTabChangedListener(this);
	}

	public void switchTabHost(){
		int visibility = tabHost.getVisibility() == VISIBLE ? GONE : VISIBLE;
		tabHost.setVisibility(visibility);
	}

	protected void setupTab(TabHost tabHost, String text, int viewId, int tabId)
	{
		View tabview = LayoutInflater.from(tabHost.getContext()).inflate(tabId, null);
		TextView tv = (TextView) tabview.findViewById(R.id.tabText);
		tv.setText(text);

		TabHost.TabSpec tab = tabHost.newTabSpec(text);
		tab.setIndicator(tabview);
		tab.setContent(viewId);
		tabHost.addTab(tab);
	}

	@Override
	public void onTabChanged(String tabId)
	{
		//  hide the search tab
		findViewById(R.id.searchTab).setVisibility(View.GONE);

		//  show search is not selected
		showSearchSelected(false);

		//  show/hide the pages view
		handlePagesTab(tabId);

		hideKeyboard();
	}

	private void showSearchSelected(boolean selected)
	{
		//  set the view
		mSearchButton.setSelected(selected);

		//  colorize
		if (selected)
			mSearchButton.setColorFilter(0xff000000, PorterDuff.Mode.SRC_IN);
		else
			mSearchButton.setColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN);
	}

	protected void handlePagesTab(String tabId)
	{
		if (tabId.equals(mTagPages))
			showPages();
		else
		{
			hideReflow();
			hidePages();
		}
	}

	protected void showPages()
	{
		LinearLayout pages = (LinearLayout) findViewById(R.id.pages_container);
		if (null == pages)
			return;

		if (pages.getVisibility() == View.VISIBLE)
			return;

		pages.setVisibility(View.VISIBLE);

		final ViewTreeObserver observer = mDocView.getViewTreeObserver();
		observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
		{
			@Override
			public void onGlobalLayout()
			{
				observer.removeOnGlobalLayoutListener(this);
				mDocView.onShowPages();
			}
		});

		final ViewTreeObserver observer2 = getViewTreeObserver();
		observer2.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
		{
			@Override
			public void onGlobalLayout()
			{
				observer2.removeOnGlobalLayoutListener(this);
				mDocPagesView.onOrientationChange();
				int page = mDocView.getMostVisiblePage();
				mDocPagesView.setCurrentPage(page);
				mDocPagesView.scrollToPage(page);
			}
		});
	}

	protected void hidePages()
	{
		LinearLayout pages = (LinearLayout) findViewById(R.id.pages_container);
		if (null == pages)
			return;

		if (pages.getVisibility() == View.GONE)
			return;

		pages.setVisibility(View.GONE);
		ViewTreeObserver observer = mDocView.getViewTreeObserver();
		observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
		{
			@Override
			public void onGlobalLayout()
			{
				mDocView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
				mDocView.onHidePages();
			}
		});
	}

	//  called from the main DocView whenever the pages list needs to be updated.
	public void setCurrentPage(int pageNumber)
	{
		if (usePagesView())
		{
			//  set the new current page and scroll there.
			mDocPagesView.setCurrentPage(pageNumber);
			mDocPagesView.scrollToPage(pageNumber);
		}
	}

	public boolean showKeyboard()
	{
		//  show keyboard
		InputMethodManager im = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
		im.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);

		return true;
	}

	public void hideKeyboard()
	{
		//  hide the keyboard
		InputMethodManager im = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
		im.hideSoftInputFromWindow(mDocView.getWindowToken(), 0);
	}

	private boolean started = false;
	public void start(final String path)
	{
		started = false;

		((Activity)getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
		((Activity)getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

		//  inflate the UI
		final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		final LinearLayout view = (LinearLayout) inflater.inflate(R.layout.doc_view, null);

		final ViewTreeObserver vto = getViewTreeObserver();
		vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
		{
			@Override
			public void onGlobalLayout()
			{
				if (!started)
				{
					findViewById(R.id.tabhost).setVisibility(mShowUI?View.VISIBLE:View.GONE);
					findViewById(R.id.footer).setVisibility(mShowUI?View.VISIBLE:View.GONE);
					afterFirstLayoutComplete(path);

					started = true;
				}
			}
		});

		addView(view);
	}

	public void afterFirstLayoutComplete(final String path)
	{
		//  main view
		mDocView = (DocView) findViewById(R.id.doc_view_inner);
		mDocView.setHost(this);
		mDocReflowView = (DocReflowView) findViewById(R.id.doc_reflow_view);

		//  page list
		if (usePagesView())
		{
			mDocPagesView = new DocListPagesView(getContext());
			mDocPagesView.setSelectionListener(new DocListPagesView.SelectionListener()
			{
				@Override
				public void onPageSelected(int pageNumber)
				{
					mDocView.scrollToPage(pageNumber);
				}
			});

			LinearLayout layout2 = (LinearLayout) findViewById(R.id.pages_container);
			layout2.addView(mDocPagesView);
		}

		//  tabs
		setupTabs();

		//  selection handles
		View v = findViewById(R.id.doc_wrapper);
		RelativeLayout layout = (RelativeLayout) v;
		mDocView.setupHandles(layout);

		//  watch for a possible orientation change
		ViewTreeObserver observer3 = getViewTreeObserver();
		observer3.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
		{
			@Override
			public void onGlobalLayout()
			{
				onPossibleOrientationChange();
			}
		});

		//  connect buttons to functions

		mBackButton = (ImageButton)findViewById(R.id.back_button);
		mBackButton.setOnClickListener(this);

		mReflowButton = (Button)findViewById(R.id.reflow_button);
		mReflowButton.setOnClickListener(this);

		mFirstPageButton = (Button)findViewById(R.id.first_page_button);
		mFirstPageButton.setOnClickListener(this);

		mLastPageButton = (Button)findViewById(R.id.last_page_button);
		mLastPageButton.setOnClickListener(this);

		mSearchButton = (ImageButton)findViewById(R.id.search_button);
		mSearchButton.setOnClickListener(this);
		showSearchSelected(false);
		mSearchText = (EditText) findViewById(R.id.search_text_input);
		mSearchText.setOnClickListener(this);

		mSaveButton = (Button)findViewById(R.id.save_button);
		mSaveButton.setOnClickListener(this);

		mSaveAsButton = (Button)findViewById(R.id.save_as_button);
		mSaveAsButton.setOnClickListener(this);

		mPrintButton = (Button)findViewById(R.id.print_button);
		mPrintButton.setOnClickListener(this);

		mShareButton = (Button)findViewById(R.id.share_button);
		mShareButton.setOnClickListener(this);

		mOpenInButton = (Button)findViewById(R.id.open_in_button);
		mOpenInButton.setOnClickListener(this);

		mProofButton = (Button)findViewById(R.id.proof_button);
		mProofButton.setOnClickListener(this);

		//  this listener will
		mSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener()
		{
			@Override
			public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
			{
				if (actionId == EditorInfo.IME_ACTION_NEXT)
				{
					onSearchNextButton();
					return true;
				}
				return false;
			}
		});

		mSearchNextButton = (Button)findViewById(R.id.search_next_button);
		mSearchNextButton.setOnClickListener(this);

		mSearchPreviousButton = (Button)findViewById(R.id.search_previous_button);
		mSearchPreviousButton.setOnClickListener(this);

		mToggleAnnotButton = (Button)findViewById(R.id.show_annot_button);
		mToggleAnnotButton.setOnClickListener(this);

		mHighlightButton = (Button)findViewById(R.id.highlight_button);
		mHighlightButton.setOnClickListener(this);

		mNoteButton = (Button)findViewById(R.id.note_button);
		mNoteButton.setOnClickListener(this);

		mDrawButton = (Button)findViewById(R.id.draw_button);
		mDrawButton.setOnClickListener(this);

		mLineColorButton = (Button)findViewById(R.id.line_color_button);
		mLineColorButton.setOnClickListener(this);

		mLineThicknessButton = (Button)findViewById(R.id.line_thickness_button);
		mLineThicknessButton.setOnClickListener(this);

		mDeleteButton = (Button)findViewById(R.id.delete_button);
		mDeleteButton.setOnClickListener(this);

		mDoc = new Document(path);

		if (mDoc.needsPassword())
		{
			askForPassword();
		}
		else
		{
			afterPassword();
		}
	}

	private static final int ORIENTATION_PORTAIT = 1;
	private static final int ORIENTATION_LANDSCAPE = 2;
	private int mLastOrientation = 0;
	private void onPossibleOrientationChange()
	{
		//  get current orientation
		Point p = Utilities.getRealScreenSize((Activity)mContext);
		int orientation = ORIENTATION_PORTAIT;
		if (p.x>p.y)
			orientation = ORIENTATION_LANDSCAPE;

		//  see if it's changed
		if (orientation != mLastOrientation)
			onOrientationChange();

		mLastOrientation = orientation;
	}

	private void onOrientationChange()
	{
		mDocView.onOrientationChange();

		if (usePagesView()) {
			mDocPagesView.onOrientationChange();
		}
	}

	private Document mDoc;

	private void askForPassword()
	{
		Utilities.passwordDialog((Activity) getContext(), new Utilities.passwordDialogListener()
		{
			@Override
			public void onOK(String password)
			{
				//  yes
				boolean ok = mDoc.authenticatePassword(password);
				if (ok)
				{
					afterPassword();
					mDocView.requestLayout();
				}
				else
				{
					askForPassword();
				}
			}

			@Override
			public void onCancel()
			{
				mDoc.destroy();
				if (mDoneListener != null)
					mDoneListener.done();
			}
		});
	}

	private void afterPassword()
	{
		//  start the views
		mDocView.start(mDoc);
		if (usePagesView())
		{
			mDocPagesView.clone(mDocView);
		}

		mHighlightButton.setEnabled(false);
		mDocView.setSelectionChangeListener(this);
		onSelectionChanged();
	}

	public void showUI(boolean show)
	{
		mShowUI = show;
	}

	public void stop()
	{
		mDocView.finish();
		if (usePagesView())
		{
			mDocPagesView.finish();
		}
	}

	private void onOutline(final Outline[] outline, int level)
	{
		if (outline == null)
			return;

		for (Outline entry : outline)
		{
			int numberOfSpaces = (level) * 4;
			String spaces = "";
			if (numberOfSpaces > 0)
				spaces = String.format("%" + numberOfSpaces + "s", " ");
			Log.i("example", String.format("%d %s %s %s", entry.page + 1, spaces, entry.title, entry.uri));
			if (entry.down != null)
			{
				//  branch
				onOutline(entry.down, level + 1);
			}
		}
	}

	private void onLinks()
	{
		int numPages = mDocView.getPageCount();
		for (int pageNum = 0; pageNum < numPages; pageNum++)
		{
			DocPageView cv = (DocPageView) mDocView.getOrCreateChild(pageNum);

			Link links[] = cv.getPage().getLinks();
			if (links != null)
			{

				for (int i = 0; i < links.length; i++)
				{
					Link link = links[i];

					Log.i("example", String.format("links for page %d:", pageNum));
					Log.i("example", String.format("     link %d:", i));
					Log.i("example", String.format("          page = %d", link.page));
					Log.i("example", String.format("          uri = %s", link.uri));
					Log.i("example", String.format("          bounds = %f %f %f %f ",
							link.bounds.x0, link.bounds.y0, link.bounds.x1, link.bounds.y1));
				}
			}
			else
			{
				Log.i("example", String.format("no links for page %d", pageNum));
			}

		}
	}

	@Override
	public void onClick(View v)
	{
		if (v == mReflowButton)
			onReflowButton();

		if (v == mFirstPageButton)
			onFirstPageButton();
		if (v == mLastPageButton)
			onLastPageButton();

		if (v == mSearchButton)
			onShowSearch();
		if (v == mSearchText)
			onEditSearchText();
		if (v == mSearchNextButton)
			onSearchNextButton();
		if (v == mSearchPreviousButton)
			onSearchPreviousButton();

		if (v == mBackButton)
			onBackButton();

		if (v == mSaveButton)
			onSaveButton();
		if (v == mSaveAsButton)
			onSaveAsButton();
		if (v == mPrintButton)
			onPrintButton();
		if (v == mShareButton)
			onShareButton();
		if (v == mOpenInButton)
			onOpenInButton();

		if (v == mToggleAnnotButton)
			onToggleAnnotButton();
		if (v == mHighlightButton)
			onHighlightButton();
		if (v == mDeleteButton)
			onDeleteButton();

		if (v == mNoteButton)
			onNoteButton();
		if (v == mDrawButton)
			onDrawButton();
		if (v == mLineColorButton)
			onLineColorButton();
		if (v == mLineThicknessButton)
			onLineThicknessButton();

		if (v == mProofButton)
			onProof();

	}

	public void onSearchNextButton()
	{
		hideKeyboard();
		mDocView.onSearchNext(mSearchText.getText().toString());
	}

	public void onSearchPreviousButton()
	{
		hideKeyboard();
		mDocView.onSearchPrevious(mSearchText.getText().toString());
	}

	public void onEditSearchText()
	{
		mSearchText.requestFocus();
		showKeyboard();
	}

	public void onShowSearch()
	{
		//  "deselect" all the visible tabs by selecting the hidden (first) one
		TabHost tabHost = (TabHost)findViewById(R.id.tabhost);
		tabHost.setCurrentTabByTag("HIDDEN");

		//  show search as selected
		showSearchSelected(true);

		//  hide all the other tabs
		hideAllTabs();

		//  show the search tab
		findViewById(R.id.searchTab).setVisibility(View.VISIBLE);
		mSearchText.getText().clear();
	}

	protected void hideAllTabs()
	{
		//  hide all the other tabs
		findViewById(R.id.fileTab).setVisibility(View.GONE);
		findViewById(R.id.annotateTab).setVisibility(View.GONE);
		findViewById(R.id.pagesTab).setVisibility(View.GONE);
	}

	private void onFirstPageButton()
	{
		goToPage(0);
	}

	private void onLastPageButton()
	{
		int npages = mDocView.getPageCount();
		goToPage(npages-1);
	}

	private void goToPage(int pageNumber)
	{
		mDocView.scrollToPage(pageNumber);

		if (mDocReflowView.getVisibility() == View.VISIBLE)
		{
			setReflowText(pageNumber);
			mDocPagesView.setCurrentPage(pageNumber);
		}
	}

	private void onReflowButton()
	{
		if (mDocView.getVisibility() == View.VISIBLE)
		{
			//  set initial text into reflow view
			setReflowText(mDocPagesView.getMostVisiblePage());

			//  show reflow
			showReflow();
		}
		else
		{
			//  hide reflow
			hideReflow();
		}
	}

	private void showReflow()
	{
		mDocView.setVisibility(View.GONE);
		mDocReflowView.setVisibility(View.VISIBLE);
	}

	private void hideReflow()
	{
		mDocReflowView.setVisibility(View.GONE);
		mDocView.setVisibility(View.VISIBLE);
	}

	private void setReflowText(int pageNumber)
	{
		DocPageView dpv = (DocPageView)mDocView.getAdapter().getView(pageNumber, null, null);
		byte bytes[] = dpv.getPage().textAsHtml();
		mDocReflowView.setHTML(bytes);
	}

	private void onBackButton()
	{
		if (mDoneListener != null)
			mDoneListener.done();
	}

	private void onSaveButton()
	{
		Toast.makeText(getContext(),"onSaveButton", Toast.LENGTH_SHORT).show();
	}

	private void onSaveAsButton()
	{
		Toast.makeText(getContext(),"onSaveAsButton", Toast.LENGTH_SHORT).show();
	}

	private void onPrintButton()
	{
		Toast.makeText(getContext(),"onPrintButton", Toast.LENGTH_SHORT).show();
	}

	private void onShareButton()
	{
		Toast.makeText(getContext(),"onShareButton", Toast.LENGTH_SHORT).show();
	}

	private void onOpenInButton()
	{
		Toast.makeText(getContext(),"onOpenInButton", Toast.LENGTH_SHORT).show();
	}

	private void onToggleAnnotButton()
	{
		mDocView.toggleAnnotations();
	}

	private void onHighlightButton()
	{
		mDocView.onHighlight();
	}

	private void onNoteButton()
	{
		mDocView.onNoteMode();
	}

	private void onDrawButton()
	{
		mDocView.onDrawMode();
	}

	private void onLineColorButton()
	{
		if (mDocView.getDrawMode() || mDocView.hasInkAnnotationSelected())
		{
			ColorDialog dlg = new ColorDialog(ColorDialog.BG_COLORS,
					getContext(), mLineColorButton, new ColorDialog.ColorChangedListener()
			{
				@Override
				public void onColorChanged(String color)
				{
					int icolor = Color.parseColor(color);
					mDocView.setInkLineColor(icolor);
					Drawable drawables[] = mLineColorButton.getCompoundDrawables();
					drawables[1].setColorFilter(icolor, PorterDuff.Mode.SRC_IN);
				}
			}, true);
			dlg.setShowTitle(false);
			dlg.show();
		}
	}

	private void onLineThicknessButton()
	{
		if (mDocView.getDrawMode() || mDocView.hasInkAnnotationSelected())
		{
			float val = mDocView.getInkLineThickness();
			LineWidthDialog.show(getContext(), mLineThicknessButton, val,
					new LineWidthDialog.WidthChangedListener()
					{
						@Override
						public void onWidthChanged(float value)
						{
							mDocView.setInkLineThickness(value);
						}
					});
		}
	}

	private String getEmbeddedProfileName()
	{
		PDFObject outputIntents = mDoc.toPDFDocument().getTrailer().get("Root").get("OutputIntents");
		if (outputIntents == null)
			return null;

		int length = outputIntents.size();
		int i;

		for (i = 0 ; i < length; i++) {
			PDFObject intent = outputIntents.get(i);

			String name = intent.get("S").asName();
			if (!name.equals("GTS_PDFX"))
				continue;

			/* We can't use the embedded profile if it's not CMYK based. */
			if (intent.get("DestOutputProfile").get("N").asInteger() != 4)
				continue;

			PDFObject id = intent.get("Info");
			if (id.isString())
				return id.asString();
			id = intent.get("OutputConditionIdentifier");
			if (id.isString())
				return id.asString();
			id = intent.get("OutputCondition");
			if (id.isString())
				return id.asString();
		}
		return null;
	}

	private void onProof()
	{
		proofSetup();
		if (!proofSupported())
		{
			Utilities.showMessage((Activity)getContext(), "gprf not supported", "gprf not supported");
			return;
		}

		//  show a dialog to collect the resolution and profiles
		final Activity activity = (Activity)getContext();

		final Dialog dialog = new Dialog(activity);
		dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
		dialog.setContentView(R.layout.proof_dialog);

		final Spinner sp1 = (Spinner)(dialog.findViewById(R.id.print_profile_spinner));
		final Spinner sp2 = (Spinner)(dialog.findViewById(R.id.display_profile_spinner));
		final Spinner sp3 = (Spinner)(dialog.findViewById(R.id.resolution_spinner));

		mEmbeddedProfile = getEmbeddedProfileName();
		if (mEmbeddedProfile!=null && !mEmbeddedProfile.isEmpty())
		{
			//  if the doc has an embedded profile, add it to the beginning of the list of print profiles.
			String[] baseList = getResources().getStringArray(R.array.proof_print_profiles);
			ArrayList<String> list = new ArrayList<String>(Arrays.asList(baseList));
			list.add(0, "Output Intent: " + mEmbeddedProfile);
			ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(activity, android.R.layout.simple_spinner_item, list);
			spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
			sp1.setAdapter(spinnerAdapter);
		}

		dialog.findViewById(R.id.cancel_button).setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				//  Cancel
				dialog.dismiss();

				//  remember the display profile selected
				SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
				SharedPreferences.Editor editor = sharedPref.edit();
				editor.putInt("displayProfileSelected", sp2.getSelectedItemPosition());
				editor.commit();
			}
		});

		dialog.findViewById(R.id.ok_button).setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				//  OK
				dialog.dismiss();

				//  remember the display profile selected
				SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
				SharedPreferences.Editor editor = sharedPref.edit();
				editor.putInt("displayProfileSelected", sp2.getSelectedItemPosition());
				editor.commit();

				doProof(sp1.getSelectedItemPosition(), sp2.getSelectedItemPosition(), sp3.getSelectedItemPosition());

			}
		});

		//  choose the last-selected display profile.
		SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
		int selected = sharedPref.getInt("displayProfileSelected", 0);
		sp2.setSelection(selected);

		dialog.show();
	}

	private static boolean proofSetupDone = false;
	private static boolean proofCodeSupported = false;
	private static boolean proofGsLibLoaded = false;
	private static void proofSetup()
	{
		if (proofSetupDone)
			return;

		proofCodeSupported = (com.artifex.mupdf.fitz.Context.gprfSupportedNative()==1);

		if (proofCodeSupported)
		{
			try
			{
				System.loadLibrary("gs");
				proofGsLibLoaded = true;
			}
			catch (UnsatisfiedLinkError e)
			{
			}
		}

		proofSetupDone = true;
	}

	private static boolean proofSupported()
	{
		return (proofCodeSupported && proofGsLibLoaded);
	}

	private void doProof(int printProfileIndex, int displayProfileIndex, int resolutionIndex)
	{
		//  get the resolution
		String[] resolutions = getResources().getStringArray(R.array.proof_resolutions);
		String resolutionString = resolutions[resolutionIndex];
		int resolution = Integer.parseInt(resolutionString);

		//  get the print profile
		String printProfilePath;
		String[] printProfiles = getResources().getStringArray(R.array.proof_print_profile_files);
		if (mEmbeddedProfile!=null && !mEmbeddedProfile.isEmpty())
		{
			if (printProfileIndex==0)
			{
				printProfilePath = "<EMBEDDED>";
			}
			else
			{
				printProfilePath   = extractProfileAsset("profiles/CMYK/" + printProfiles[printProfileIndex-1]);
			}
		}
		else
		{
			printProfilePath   = extractProfileAsset("profiles/CMYK/" + printProfiles[printProfileIndex]);
		}

		//  get the display profile
		String[] displayProfiles = getResources().getStringArray(R.array.proof_display_profile_files);
		String displayProfileFile = displayProfiles[displayProfileIndex];
		String displayProfilePath = extractProfileAsset("profiles/RGB/"  + displayProfileFile);

		//  what page are we doing?
		int thePage = mDocView.getMostVisiblePage();

		String proofFile = mDocView.getDoc().makeProof(mDocView.getDoc().getPath(), printProfilePath, displayProfilePath, resolution);

		Uri uri = Uri.parse("file://" + proofFile);
		Intent intent = new Intent(getContext(), ProofActivity.class);
		intent.setAction(Intent.ACTION_VIEW);
		intent.setData(uri);
		// add the current page so it can be found when the activity is running
		intent.putExtra("startingPage", thePage);
		(getContext()).startActivity(intent);
	}

	private String extractProfileAsset(String profile)
	{
		try
		{
			InputStream inStream = getContext().getAssets().open(profile);
			String tempfile = getContext().getExternalCacheDir() + "/shared/" + profile;
			new File(tempfile).mkdirs();
			Utilities.deleteFile(tempfile);

			FileOutputStream outStream = new FileOutputStream(tempfile);
			byte[] buffer = new byte[4096]; // To hold file contents
			int bytes_read; // How many bytes in buffer

			// Read a chunk of bytes into the buffer, then write them out,
			// looping until we reach the end of the file (when read() returns
			// -1). Note the combination of assignment and comparison in this
			// while loop. This is a common I/O programming idiom.
			while ((bytes_read = inStream.read(buffer)) != -1)
				// Read until EOF
				outStream.write(buffer, 0, bytes_read); // write

			return tempfile;
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}

		return "";
	}

	private void onDeleteButton()
	{
		mDocView.onDelete();
	}

	private DocPageView.NoteAnnotation lastNote = null;

	public void onSelectionChanged()
	{
		boolean hasSel = mDocView.hasSelection();
		boolean hasInkAnnotSel = mDocView.hasInkAnnotationSelected();
		boolean hasAnnotSel = mDocView.hasAnnotationSelected();
		boolean hasNoteAnnotSel = mDocView.hasNoteAnnotationSelected();

		mHighlightButton.setEnabled(hasSel);

		boolean noteMode = mDocView.getNoteMode();
		mNoteButton.setSelected(noteMode);
		findViewById(R.id.note_holder).setSelected(noteMode);

		boolean drawMode = mDocView.getDrawMode();
		mDrawButton.setSelected(drawMode);
		mLineColorButton.setEnabled(drawMode || hasInkAnnotSel);
		mLineThicknessButton.setEnabled(drawMode || hasInkAnnotSel);
		mDeleteButton.setEnabled(!drawMode && hasAnnotSel);

		findViewById(R.id.draw_tools_holder).setSelected(drawMode);

		View v = findViewById(R.id.doc_note_editor);
		mDocView.moveNoteEditor(v);

		DocPageView.NoteAnnotation note = mDocView.getSelectedNoteAnnotation();
		if (note != lastNote)
		{
			TextView tv = (TextView)findViewById(R.id.doc_note_editor_date);
			EditText te = (EditText)findViewById(R.id.doc_note_editor_text);

			//  save the old one
			if (lastNote != null)
			{
				SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm");
				String date = sdf.format(new Date());
				lastNote.setDate(date);
				lastNote.setText(te.getText().toString());
			}

			if (note != null)
			{
				tv.setText(note.getDate());
				te.setText(note.getText());
			}

			lastNote = note;
		}
	}

	private OnDoneListener mDoneListener = null;
	public void setOnDoneListener(OnDoneListener l) {mDoneListener = l;}
	public interface OnDoneListener
	{
		public void done();
	}
}