/*
    Copyright (C) 2014 Sergii Pylypenko.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.cups.android;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
import android.view.MotionEvent;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.EditText;
import android.text.Editable;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.FrameLayout;
import android.graphics.drawable.Drawable;
import android.graphics.Color;
import android.content.res.Configuration;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.view.View.OnKeyListener;
import android.view.MenuItem;
import android.view.Menu;
import android.view.Gravity;
import android.text.method.TextKeyListener;
import java.util.LinkedList;
import java.io.SequenceInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Set;
import android.text.SpannedString;
import java.io.BufferedReader;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
import android.view.inputmethod.InputMethodManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import android.content.pm.ActivityInfo;
import android.view.Display;
import android.text.InputType;
import android.util.Log;
import android.view.Surface;
import android.app.ProgressDialog;
import android.text.util.Linkify;
import android.provider.Settings;
import android.app.AlertDialog;
import android.widget.ScrollView;
import android.content.DialogInterface;
import android.net.Uri;
import java.util.*;


public class PrintJobs
{
	private static boolean destroyed = false;
	private static HashMap<android.printservice.PrintJob, String[]> trackedJobs = new HashMap<android.printservice.PrintJob, String[]>();
	private static CupsPrintService context = null;
	private static Handler mainThread = null;


	synchronized public static void init(final CupsPrintService c)
	{
		if (context != null)
			Log.e(TAG, "Error: Already initialized with context " + context + ", new context " + c);
		destroyed = false;
		context = c;
		mainThread = new Handler(context.getMainLooper());
		Log.d(TAG, "Creating jobs tracking thread");
		new Thread(new Runnable()
		{
			public void run()
			{
				trackJobsThread();
			}
		}).start();
	}

	synchronized public static void destroy()
	{
		Log.d(TAG, "Destroying jobs tracking thread");
		destroyed = true;
	}

	synchronized public static void trackJob(final android.printservice.PrintJob job)
	{
		trackedJobs.put(job, new String[] {job.getTag(), job.getInfo().getPrinterId().getLocalId()});
		Log.d(TAG, "Started tracking job: " + job.getTag() + " printer " + job.getInfo().getPrinterId().getLocalId());
	}

	synchronized public static void stopTrackingJob(final android.printservice.PrintJob job)
	{
		trackedJobs.remove(job);
	}

	private static String getJobStatus(final String[] jobInfo)
	{
		for (String s: jobInfo)
		{
			if (s.startsWith("Status:") && s.length() > "Status:".length() + 1)
				return s.substring("Status:".length() + 1);
		}
		return "";
	}

	private static boolean isJobSucceeded(final String[] jobInfo)
	{
		for (String s: jobInfo)
		{
			if (s.equals("Alerts: job-completed-successfully") || s.equals("Alerts: processing-to-stop-point"))
				return true;
		}
		return false;
	}

	synchronized private static HashMap<android.printservice.PrintJob, String[]> copyTrackedJobsArray()
	{
		// I'm to lazy to find out how to synchronize class object from static context, so I'll just create another synchronized method
		return new HashMap<android.printservice.PrintJob, String[]>(trackedJobs);
	}

	private static void trackJobsThread()
	{
		while (!destroyed)
		{
			try
			{
				Thread.sleep(10000);
			}
			catch (Exception e)
			{
			}
			// Invoking commandline tools is slow, so we will create local copy of trackedJobs, and only lock it when changing it
			HashMap<android.printservice.PrintJob, String[]> trackedCopy = copyTrackedJobsArray();
			final HashMap<String, Map<String, String[]> > activeJobs = new HashMap<String, Map<String, String[]> >();
			final HashMap<String, Map<String, String[]> > completedJobs = new HashMap<String, Map<String, String[]> >();
			for (final android.printservice.PrintJob job: trackedCopy.keySet())
			{
				final String jobName = trackedJobs.get(job)[0];
				final String printer = trackedJobs.get(job)[1];
				if (!activeJobs.containsKey(printer))
					activeJobs.put(printer, Cups.getPrintJobs(context, printer, false));
				if (activeJobs.get(printer).containsKey(jobName))
				{
					mainThread.post(new Runnable()
					{
						public void run()
						{
							String status = getJobStatus(activeJobs.get(printer).get(jobName));
							Log.d(TAG, "Print job " + jobName + " status: " + status);
							if (status.length() > 0)
								job.block(status);
							else if (!job.isStarted())
								job.start();
						}
					});
				}
				else
				{
					if (!completedJobs.containsKey(printer))
						completedJobs.put(printer, Cups.getPrintJobs(context, printer, true));
					mainThread.post(new Runnable()
					{
						public void run()
						{
							if (!completedJobs.get(printer).containsKey(jobName))
								job.fail(context.getResources().getString(R.string.error_job_disappeared));
							else if (isJobSucceeded(completedJobs.get(printer).get(jobName)))
								job.complete();
							else
							{
								String status = getJobStatus(completedJobs.get(printer).get(jobName));
								Log.d(TAG, "Print job " + jobName + " failed with status: " + status);
								job.fail(status);
							}
						}
					});
					stopTrackingJob(job);
				}
			}
		}
	}

	static public final String TAG = "PrintJobs";
}
