

import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
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 javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingWorker;

import net.miginfocom.swing.MigLayout;

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

import os.path.Pathutil;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;

public class MainUI {

	private JFrame frmSharefaithUpdateriffic;
	private JLabel lblStatus;
	private JLabel lblStatusDetail;

	private static XcodeExceptionHandler myXcodeErrorHandler;
	
	private boolean stop = false;
	private JCheckBox chckbxCreateAppId;
	private JCheckBox chckbxCreatePushCertificate;
	private JCheckBox chckbxCreateProvisioningProfiles;
	private JCheckBox chckbxAddToPushserve;
	private JCheckBox chckbxCompileUploadApp;
	private JCheckBox chckbxCreateOnItunesconnect;
	private JCheckBox chckbxIncrementBuildNumber;
	private JRadioButton rdbtnEven;
	private JRadioButton rdbtnOdd;
	
	//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 = true;
	/**
	 * @wbp.nonvisual location=53,71
	 */
	private final ButtonGroup buttonGroup = new ButtonGroup();
	private JCheckBox chckbxUpdateCompileUpload;
	private JCheckBox chckbxUpdateOnItunesconnect;
	private JPanel NewAppOptionPanel;
	private JPanel UpdateAppOptionPanel;

	/**
	 * Launch the Swing GUI.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainUI window = new MainUI();
					window.frmSharefaithUpdateriffic.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public MainUI() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frmSharefaithUpdateriffic = new JFrame();
		frmSharefaithUpdateriffic.setTitle("Sharefaith Updateriffic!");
		frmSharefaithUpdateriffic.setBounds(100, 100, 600, 400);
		frmSharefaithUpdateriffic.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmSharefaithUpdateriffic.getContentPane().setLayout(new MigLayout("", "[438px,grow]", "[25px,grow][grow][][grow][grow][][][grow][25px][]"));
		
		JPanel panel_4 = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(panel_4, "cell 0 0,grow");
		panel_4.setLayout(new GridLayout(0, 2, 0, 0));
		
		ButtonGroup group = new ButtonGroup();
		final JRadioButton rdbtnCreate = new JRadioButton("Create");
		rdbtnCreate.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if( NewAppOptionPanel == null) return;
				
				NewAppOptionPanel.setVisible( true );
				UpdateAppOptionPanel.setVisible( false );
			}
		});
		group.add(rdbtnCreate);
		rdbtnCreate.setSelected(true);
		panel_4.add(rdbtnCreate);
		
		JRadioButton rdbtnUpdate = new JRadioButton("Update");
		rdbtnUpdate.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if( NewAppOptionPanel == null) return;
				
				NewAppOptionPanel.setVisible( false );
				UpdateAppOptionPanel.setVisible( true );
			}
		});
		panel_4.add(rdbtnUpdate);
		group.add(rdbtnUpdate);
		
		JPanel panel_6 = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(panel_6, "cell 0 1,grow");
		
		JRadioButton rdbtnAll = new JRadioButton("ALL");
		buttonGroup.add(rdbtnAll);
		rdbtnAll.setSelected(true);
		panel_6.add(rdbtnAll);
		
		rdbtnEven = new JRadioButton("EVEN");
		buttonGroup.add(rdbtnEven);
		panel_6.add(rdbtnEven);
		
		rdbtnOdd = new JRadioButton("ODD");
		buttonGroup.add(rdbtnOdd);
		panel_6.add(rdbtnOdd);
		
		JPanel panel_5 = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(panel_5, "cell 0 2,grow");
		
		chckbxIncrementBuildNumber = new JCheckBox("Increment build number x.x.(x+1) for upload problem");
		panel_5.add(chckbxIncrementBuildNumber);
		
		NewAppOptionPanel = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(NewAppOptionPanel, "cell 0 3,grow");
		NewAppOptionPanel.setLayout(new GridLayout(0, 1, 0, 0));
		
		JLabel lblNewappProcess = new JLabel("New-App Process");
		NewAppOptionPanel.add(lblNewappProcess);
		
		chckbxCreateAppId = new JCheckBox("Create App ID");
		chckbxCreateAppId.setSelected(true);
		NewAppOptionPanel.add(chckbxCreateAppId);
		
		chckbxCreatePushCertificate = new JCheckBox("Create Push Certificate");
		chckbxCreatePushCertificate.setSelected(true);
		NewAppOptionPanel.add(chckbxCreatePushCertificate);
		
		chckbxAddToPushserve = new JCheckBox("Add to PushServe");
		chckbxAddToPushserve.setSelected(true);
		NewAppOptionPanel.add(chckbxAddToPushserve);
		
		chckbxCreateProvisioningProfiles = new JCheckBox("Create Provisioning Profiles");
		chckbxCreateProvisioningProfiles.setSelected(true);
		NewAppOptionPanel.add(chckbxCreateProvisioningProfiles);
		
		JPanel panel_2 = new JPanel();
		NewAppOptionPanel.add(panel_2);
		panel_2.setLayout(new GridLayout(0, 2, 0, 0));
		
		chckbxCompileUploadApp = new JCheckBox("Compile, upload app");
		chckbxCompileUploadApp.setSelected(true);
		panel_2.add(chckbxCompileUploadApp);
		
		chckbxCreateOnItunesconnect = new JCheckBox("Create on iTunes-Connect");
		chckbxCreateOnItunesconnect.setSelected(true);
		panel_2.add(chckbxCreateOnItunesconnect);
		
		UpdateAppOptionPanel = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(UpdateAppOptionPanel, "cell 0 4,grow");
		UpdateAppOptionPanel.setLayout(new GridLayout(0, 1, 0, 0));
		//Not default
		UpdateAppOptionPanel.setVisible(true);
		
		JLabel lblUpdateappProcess = new JLabel("Update-App Process");
		UpdateAppOptionPanel.add(lblUpdateappProcess);
		
		chckbxUpdateCompileUpload = new JCheckBox("Compile, upload");
		UpdateAppOptionPanel.add(chckbxUpdateCompileUpload);
		chckbxUpdateCompileUpload.setSelected(true);
		
		chckbxUpdateOnItunesconnect = new JCheckBox("Update on iTunes-Connect");
		chckbxUpdateOnItunesconnect.setSelected(true);
		UpdateAppOptionPanel.add(chckbxUpdateOnItunesconnect);
		
		JPanel panel = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(panel, "flowy,cell 0 8,grow");
		
		final JButton btnProcessOne = new JButton("Process One");
		panel.add(btnProcessOne);
		final JButton btnProcessAll = new JButton("Process All");
		panel.add(btnProcessAll);

		final JButton btnStopAfterCurrent = new JButton("STOP after current");
		btnStopAfterCurrent.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0)
			{
				stop = true;
			}
		});
		panel.add(btnStopAfterCurrent);
		
		btnProcessAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0)
			{
				btnProcessOne.setVisible(false);
				btnProcessAll.setVisible(false);
				SwingWorker sw = new SwingWorker() {//Long-running process:
					public Object doInBackground()
					{
						try
						{
							if (rdbtnCreate.isSelected())
							{
								createApp();
							} else {
								updateApp();
							}
							frmSharefaithUpdateriffic.toFront();
							frmSharefaithUpdateriffic.repaint();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						return null;
				    }
				}; 
				sw.execute();
			}
		});
		
		btnProcessOne.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0)
			{
				btnProcessOne.setVisible(false);
				btnProcessAll.setVisible(false);
				btnStopAfterCurrent.setVisible(false);//already "stopping"
				stop = true;//Stop after first one.
				SwingWorker sw = new SwingWorker() {//Long-running process:
					public Object doInBackground()
					{
						try
						{
							if (rdbtnCreate.isSelected())
							{
								createApp();
							} else {
								updateApp();
							}
							frmSharefaithUpdateriffic.toFront();
							frmSharefaithUpdateriffic.repaint();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						return null;
				    }
				}; 
				sw.execute();
			}
		});
		
		
		JPanel panel_3 = new JPanel();
		frmSharefaithUpdateriffic.getContentPane().add(panel_3, "cell 0 9,grow");
		panel_3.setLayout(new GridLayout(0, 1, 0, 0));
		
		lblStatus = new JLabel("Not Started");
		lblStatus.setFont(new Font("Dialog", Font.BOLD, 20));
		panel_3.add(lblStatus);
		
		lblStatusDetail = new JLabel("");
		panel_3.add(lblStatusDetail);
	}
	
	private ArrayList<File> getAppDirs() throws IOException
	{
		String datapath = "~/Desktop/";
		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.");
			lblStatus.setText( "IPA for previous build already on desktop.");
			stop = true;
			return new ArrayList<File>();
			//System.exit(1);
		}
		
		for( Path entry : directory )
		{
			//only number, in desktop
			if( entry.getFileName().toString().matches( "^\\d+$" ) )
			{
				int appNum = Integer.valueOf( entry.getFileName().toString() );
				//todo even
				if( rdbtnEven.isSelected() )
				{
					if( appNum % 2 == 0 )
					{
						filesInOrder.add( entry.toFile() );
					}
				}
				else if( rdbtnOdd.isSelected() )
				{
					if( appNum % 2 == 1 )
					{
						filesInOrder.add( entry.toFile() );
					}
				} else {
					//all
					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() );
			}
			
		});
		return filesInOrder;
	}
	
	private void updateApp() throws IOException
	{
		ArrayList<File> filesInOrder = getAppDirs();
		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 )
		{
			try
			{
				SmartTimer timer = new SmartTimer( new File("UpdateTimings.log") );
				AppData app = new AppData( item );
				if( this.chckbxIncrementBuildNumber.isSelected())
				{
					Xcoder.incrementBuildNumber( app );
					app = new AppData( item ); //Reload version-info into object
				}
				lblStatus.setText("Running..."+item.toString());
				lblStatusDetail.setText( app.toString() );
				System.out.println( "Starting "+item.getName().toString() );
				System.out.println( app.toString() );
				/*
				timer.start("Find-app");
				try
				{
					itunes.findApp( app.getId() );
				} catch( UnhandledAlertException uae)
				{
					itunes.findApp( app.getId() );
				}
				timer.end("Find-app");
				*/
				buildFailed = false;
				Thread buildParallel = new Thread( new Xcoder( app ) );
				buildParallel.setUncaughtExceptionHandler( myXcodeErrorHandler );
				if( chckbxUpdateCompileUpload.isSelected() )
				{
					timer.start("Xcode");
					buildParallel.start();
				}
				if( chckbxUpdateOnItunesconnect.isSelected() )
				{
					timer.start("Info-fill-in");
					itunes.startNewVersion( app, true );
					timer.end("Info-fill-in");
				}
				if( chckbxUpdateCompileUpload.isSelected() )
				{
					//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 );
					AppserveRecorder.recordReadyUploaded( app.getNumericId() );
				}
				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)
			{
				lblStatus.setText("iTunes-Update UnhandledAlertException Error:"+ e.getMessage() +"\n");
				SendMailTLS.mail("iTunes-Update UnhandledAlertException Error:"+ e.getMessage() +"\n" + e.getStackTrace() );
				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");
				stop = true;
				return;
				//System.exit(1);
			}
			catch (Exception e)
			{ //continue on next app
				SendMailTLS.mail("General iTunes-Update Error, continuing:"+ e.getMessage() +"\n" + e.getStackTrace() );
				e.printStackTrace();
				e.printStackTrace();
			}
			if (this.stop)
			{
				break;
			}
		}
		System.out.println("Finished... Now run MainFinalizer.");
		if (this.stop)
		{
			lblStatus.setText("STOPPED... Now run MainFinalizer.");
		} else {
			lblStatus.setText("Finished! Now run MainFinalizer.");
		}
	}

	private void createApp()
	{
		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
		{
			ArrayList<File> filesInOrder = getAppDirs();
			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 );
				lblStatusDetail.setText( appData.toString() );
				if( this.chckbxIncrementBuildNumber.isSelected())
				{
					Xcoder.incrementBuildNumber( appData );
					appData = new AppData( item ); //Reload version-info into object so deliver works
				}
				appData.noBinOnly();//it's new!
				if( true)//itunes.isAvailableName( appData.getTitle() ) )
				{
					iTunesDeveloperInterface devinterface = new iTunesDeveloperInterface(appData);
	
					System.out.println(appData.toString());
					if (chckbxCreateAppId.isSelected())
					{
						lblStatus.setText("CreateAppID "+item.toString());
						devinterface.createAppID();
					}
					if (chckbxCreatePushCertificate.isSelected())
					{
						lblStatus.setText("CreatePushCert "+item.toString());
						devinterface.createPushCert();
					}
					if (chckbxAddToPushserve.isSelected())
					{
						lblStatus.setText("Add-pushserve "+item.toString());
						PushServeInterface pushServ = new PushServeInterface( devinterface.browser );
						pushServ.pushserveLogin();
						pushServ.addIOSApp( appData );
					}
					if (chckbxCreateProvisioningProfiles.isSelected())
					{
						lblStatus.setText("Provisioning-profiles "+item.toString());
						devinterface.createProvisioningProfiles();
						devinterface.close();
					}
					lblStatus.setText("Compile/submit "+item.toString());
					Thread buildParallel = new Thread( new Xcoder( appData ) );
					if (chckbxCompileUploadApp.isSelected())
					{
						buildParallel.start();
					}
					if (chckbxCreateOnItunesconnect.isSelected())
					{
						itunes.createNewApp( appData );
					}
					if (chckbxCompileUploadApp.isSelected())
					{
						buildParallel.join();
						readyQueue.put( appData );//Set built and submit - AppFinalizer Firefox window.
					}
					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());
					lblStatus.setText("App name already exists");
				}
				if (stop)
				{
					break;
				}
			}
			if (stop)
			{
				lblStatus.setText("STOPPED");
			} else {
				lblStatus.setText("Finished!");
			}
		}
		catch( Exception e )
		{
			if (appData != null)
			{
				SendMailTLS.mail("New-App exception for:\n"+appData.toString() +"\n 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;
		}
	}
}
