import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.BlockingQueue;

import org.openqa.selenium.UnhandledAlertException;
import org.seleniumhq.jetty7.util.BlockingArrayQueue;

import os.path.Pathutil;

public class AppsRunner
{
	private static XcodeExceptionHandler myXcodeErrorHandler;
	
	 //Track the state of the current Xcode automator thread:
	static boolean buildFailed;
	
	// Whether or not failure messages should be emailed to developers
	static final boolean SEND_FAILURE_MESSAGES = false;

	public static void main( String[] args ) throws Exception
	{
		String datapath = "~/Desktop/incoming_android";
		ArrayList<File> filesInOrder = new ArrayList<File>();
		DirectoryStream<Path> directory = Files.newDirectoryStream( Pathutil.expanduser( datapath ).toPath() );
		
		if (new File(Pathutil.expanduser( datapath ), "Sharefaith ChurchApp.ipa").exists())
		{
			System.err.println("IPA for previous build already on desktop.");
			System.exit(1);
		}
		
		for( Path entry : directory )
		{
			//only number, in desktop
			if( entry.getFileName().toString().matches( "^\\d+$" ) )
			{
				filesInOrder.add( entry.toFile() );
			}
		}
		Collections.sort( filesInOrder, new Comparator<File>()
		{
			@Override
			public int compare( File o1, File o2 )
			{
				return Integer.valueOf( o1.getName() ) - Integer.valueOf( o2.getName() );
			}
			
		});
		
		//AppsRunner.itunesNewAppRunner( filesInOrder );
		//AppsRunner.itunesUpdateRunner( filesInOrder );

		
		//AppsRunner.googleUpdateRunner( filesInOrder );
		AppsRunner.googleNewAppRunner( filesInOrder );
	}
	
	public static void googleNewAppRunner( ArrayList<File> filesInOrder )
	{
		try
		{
			GoogleConnectInterface google = new GoogleConnectInterface();
			
			for( File item : filesInOrder )
			{
				AppData appData = new AppData( item );
				System.out.println( "********************************** Starting addition of " + item.getName().toString() );
				System.out.println( appData.toString() );
				
				google.addApp( appData );
			}
			
		}
		catch( Exception e )
		{
			e.printStackTrace();
		}
	}
	
	public static void googleUpdateRunner( ArrayList<File> filesInOrder )
	{
		try
		{
			GoogleConnectInterface google = new GoogleConnectInterface();
			
			for( File item : filesInOrder )
			{
				// uncomment to continue on exceptions (not recommended)
				//try
				//{
					AppData app = new AppData( item );
					google.binaryOnly = app.isBinaryOnly();
					System.out.println( "********************************** Starting update for " + item.getName().toString() );
					System.out.println( app.toString() );

					google.findApp( app.getAndroID() );
					// google.makeAdsSelection( app ); 	// TODO: Remove this after version 1.8.0
					google.replacePublishedApk( app );
					
					// only update store listing if any of it has changed
					if( !google.binaryOnly )
					{
						google.replaceData( app );
					}
					
					google.sleep( 1000 );
					google.finalPublish( app );
					
					System.out.println( "yay" );
				/*
				}
				catch (Exception e)
				{ //continue on next app
					throw new Exception();
				}
				*/
			}
		}
		catch( Exception e )
		{
			e.printStackTrace();
		}
		
		System.out.println("Finished.....");
	}
	
	public static void itunesUpdateRunner( ArrayList<File> filesInOrder )
	{
		iTunesConnectInterface itunes = new iTunesConnectInterface();

		BlockingArrayQueue<AppData> readyQueue = new BlockingArrayQueue<AppData>();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		//Handle concurrently:
		new Thread( new AppFinalizer( new iTunesConnectInterface(), readyQueue)).start();
		//iTunesConnectInterface finalSubmitter = new iTunesConnectInterface();
		
		for( File item : filesInOrder )
		{
			//if( Integer.valueOf( item.getName().toString() ) > 65 )
			//{
				try
				{
					SmartTimer timer = new SmartTimer( new File("UpdateTimings.log") );
					AppData app = new AppData( item );
					System.out.println( "Starting "+item.getName().toString() );
					System.out.println( app.toString() );
					
					timer.start("Find-app");
					itunes.findApp( app.getId() );
					timer.end("Find-app");
					
					buildFailed = false;
					Thread buildParallel = new Thread( new Xcoder( app ) );
					buildParallel.setUncaughtExceptionHandler( myXcodeErrorHandler );
					timer.start("Xcode");
					buildParallel.start();
					timer.start("Info-fill-in");
					itunes.startNewVersion( app, true );
					timer.end("Info-fill-in");
					//wait for xcode if not finished:
					buildParallel.join();
					timer.end("Xcode");
					
					if ( !buildFailed )//prevent stalling all apps when one failed in sikuli automator
					{
						readyQueue.put( app );
					}
					else
					{
						SendMailTLS.mail( "The app didn't fully comple/submit! : " + app.toString() );
					}
					//timer.start("Build-submit");
					//Thread.sleep( 30000 );
					//Maybe it needs to be minutes...?
					//itunes.publishBuild( app.getShortVersion() );
					
					//System.out.println("yay");
					//timer.end("Build-submit");
				}
				catch (UnhandledAlertException e)
				{
					SendMailTLS.mail("iTunes-Update UnhandledAlertException Error:"+ e.getMessage() +"\n" + e.getStackTrace().toString() );
					e.printStackTrace();
					System.out.println("Whatever happened, a human should look at this so it doesn't blindly bring up a ton of Sikulis and Xcodes");
					System.exit(1);
				}
				catch (Exception e)
				{ //continue on next app
					SendMailTLS.mail("General iTunes-Update Error, continuing:"+ e.getMessage() +"\n" + e.getStackTrace().toString() );
					e.printStackTrace();
					e.printStackTrace();
				}
			//}

		}
		System.out.println("Finished.....");
	}

	public static void itunesNewAppRunner( ArrayList<File> filesInOrder )
	{
		AppData appData = null;
		//TODO verify the Certificates.p12, Certsigningrequest, etc aren't on desktop, otherwise might possibly grab those when save to desktop fails
		try
		{
			
			iTunesConnectInterface itunes = new iTunesConnectInterface();

			BlockingArrayQueue<AppData> readyQueue = new BlockingArrayQueue<AppData>();
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			//Handle concurrently:
			new Thread( new AppFinalizer( new iTunesConnectInterface(), readyQueue)).start();
			
			for (File item : filesInOrder )
			{
				appData = new AppData( item );
				appData.noBinOnly();//it's new!
				if( true)//itunes.isAvailableName( appData.getTitle() ) )
				{
					iTunesDeveloperInterface devinterface = new iTunesDeveloperInterface(appData);
	
					System.out.println(appData.toString());
					devinterface.createAppID();
					devinterface.createPushCert();
				
					PushServeInterface pushServ = new PushServeInterface( devinterface.browser );
					pushServ.pushserveLogin();
					pushServ.addIOSApp( appData );
					
					devinterface.createProvisioningProfiles();
					devinterface.close();
				
					Thread buildParallel = new Thread( new Xcoder( appData ) );
					buildParallel.start();
					itunes.createNewApp( appData );
					buildParallel.join();
					readyQueue.put( appData );
					// called by  ^ :  itunes.publishBuild( appData.getShortVersion() );
					System.out.println("Finished "+item.getAbsolutePath());
				} 
				else
				{
					SendMailTLS.mail("The app name already exists for:\n" + appData.toString());
					System.out.println("The app name already exists for:\n" + appData.toString());
				}
			}
		}
		catch( Exception e )
		{
			if (appData != null)
			{
				SendMailTLS.mail("New-App exception for:\n"+appData.toString() +" exception:\n\n" + e.getLocalizedMessage());
			} else {
				SendMailTLS.mail("New-App exception:\n"+e.getLocalizedMessage());
			}
			e.printStackTrace();
		}
	}
	
	private class XcodeExceptionHandler implements UncaughtExceptionHandler
	{
		@Override
		public void uncaughtException(Thread t, Throwable e)
		{
			buildFailed = true;
		}
	}
}

