package rfidreader;

import java.util.List;

import javax.smartcardio.Card;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardTerminals;
import javax.smartcardio.TerminalFactory;


import anywheresoftware.b4a.BA;
import jGPIO.mycodes;

@BA.ShortName("ACR122Device")
@BA.Events(values= {
		"log(msg as string)",
		"cardfound(card as RFIDCardInfo)",
		"cardmissing(card as RFIDCardInfo)"
})

public class ACR122Device  {
	
	public ACR122Device() {
		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				System.out.println("ACR122Device ShutdownHook executed");
				close();
			}
		});
	}
	private BA ba;
	private String event;
	private Object Me;
	private boolean need_log_event = false;
	private boolean need_cardfound_event = false;
	private boolean need_cardmissing_event = false;
	
	private boolean inited = false;
	
	private CardTerminal terminal;
	 
	 
	 public void Initialize(BA ba, String eventname) {
		 this.ba = ba;
		 this.event = eventname;
		 Me = this;
		 check_events();
		 init_acr();
		 
	 }
	 
	 /**
	  * Check if already initialized
	  * @return true if initialized
	  */
	 public boolean IsInitialized() {
		 return inited;
	 }
	 
	 /**
	  * Initialize ACR122
	  * 
	  */
	 @BA.Hide
	 public void init_acr() {
		 terminal = null;
		 CardTerminals cts = TerminalFactory.getDefault().terminals();
		 if (cts!=null) {
			 try {
				List<CardTerminal> ll = cts.list();
				ll.forEach((xx)-> {
					raise_log("Found "+xx.getName());
					if (xx.getName().contains("ACR122")) {
						terminal = xx;
					}
				});
				
			} catch (CardException e) {
				raise_log("CardException on CardTerminals list = "+e.getMessage());
			}
			 
		 }
		 
		 inited = (terminal!=null);
		 if (inited) {
			 raise_log("Card Terminal is found and using "+terminal.getName()); 
			 
			 Thread tt = new Thread(new CardConnect(terminal));
			 tt.start();
			 
		 } else {
			 raise_log("Unable to find Card Terminal");
		 }
		 
	 }
	 
	 /**
	  * Close ACR122Device
	  */
	 public void close() {
		 // akan closing CardConnect Runnable
		 inited = false;
	 }
	 
	 
	 private class CardConnect implements Runnable{

		private final CardTerminal terminal;
		private String connectprocotol = "*";
		private RFIDCardInfo last_card = null;
		public CardConnect(CardTerminal terminal) {
			this.terminal = terminal;
		}
		
		@SuppressWarnings("unused")
		public CardConnect(CardTerminal terminal, String protocol) {
			this.terminal = terminal;
			if (mycodes.ValidString(protocol)) {
				connectprocotol = protocol;
			}
		}
		@Override
		public void run() {
			if (terminal==null) return;
			raise_log("CardConnect started for "+terminal.getName()+", protocol = "+connectprocotol);
			while(inited) {
				try {
					if (terminal.isCardPresent()) {
						
						Card card = terminal.connect(connectprocotol);
						RFIDCardInfo card_info = new RFIDCardInfo(card);
						if (card_info.IsValid()) {
							if (last_card==null) {
								if (mycodes.ValidString(card_info.getSerialNumber())) {
									last_card = card_info;
									raise_cardfound(card_info);
								}
							} else {
								if (mycodes.ValidString(card_info.getSerialNumber())) {
									if (card_info.getSerialNumber().contentEquals(last_card.getSerialNumber())==false) {
										last_card = card_info;
										raise_cardfound(card_info);
									}
								}
							}
							
						}
						
					} else {
						if (last_card!=null) {
							raise_cardmissing(last_card);
						}
						last_card = null;
						
					}
					Thread.sleep(500);
				} catch (InterruptedException e) {
					raise_log("CardConnect InterruptedException exception = "+e.getMessage());
					close();
					break;
				} catch (CardException e) {
					raise_log("CardConnect CardException exception = "+e.getMessage());
				}
			}
			raise_log("CardConnect stopped for "+terminal.getName());
		}
				
	 }
	 
	 
	 
	 private void check_events() {
		 if (ba!=null) {
			 if (mycodes.ValidString(event)) {
				 need_log_event = ba.subExists(event+"_log");
				 need_cardfound_event = ba.subExists(event+"_cardfound");
				 need_cardmissing_event = ba.subExists(event+"_cardmissing");
			 }
		 }
	 }
	 
	 
	 
	 
	 private void raise_log(String msg) {
		 if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg});
	 }
	 
	 private void raise_cardfound(RFIDCardInfo card) {
		 if (need_cardfound_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_cardfound", false, new Object[] {card});
	 }
	 
	 private void raise_cardmissing(RFIDCardInfo card) {
		 if (need_cardmissing_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_cardmissing", false, new Object[] {card});
	 }
}
