import com.dalsemi.system.BitPort;
import com.dalsemi.system.DataPort;
import com.dalsemi.system.IllegalAddressException;

import java.lang.*;
import java.io.*;

import HEXTOOLS;
import Event;
import EventQueue;

class KeyEventData extends Object {
    public int evtcode;
    public void KeyEvtData(int cod) {
	evtcode = cod;
    }
}

class HCS03IO implements Runnable {
    static final int PCE0_BASE_ADDRESS = 0x800000; // base address by TINI's PCE0 line

    static final int RELAY_PLANE_OFFSET = 1;
    static final int RELAY_LINE_OFFSET = 0;
    static final int KEY_DRIVE_OFFSET = 2;
    static final int KEY_RET_LOW_OFFSET = 2;
    static final int KEY_RET_HIGH_OFFSET = 3;

    static final int BASE_ADDRESS = 0x800000;
    static final int KEY_DRIVE_ADDRESS = BASE_ADDRESS + KEY_DRIVE_OFFSET;
    static final int KEY_RET_LOW_ADDRESS = BASE_ADDRESS + KEY_RET_LOW_OFFSET;
    static final int KEY_RET_HIGH_ADDRESS = BASE_ADDRESS + KEY_RET_HIGH_OFFSET;
    static final int RELAY_PLANE_ADDRESS = BASE_ADDRESS + RELAY_PLANE_OFFSET;
    static final int RELAY_LINE_ADDRESS = BASE_ADDRESS + RELAY_LINE_OFFSET;

    static final int TAG_KEY_EVENT = 1;
	static final int TAG_USR_EVENT = 2;

    static char cr = (char)13;
    static char esc = (char)27;

    int theBaseAddress;
    EventQueue keyEventQueue;
    Thread theThread;
    int keyMatrix[];
    int keyDebounceMatrix[];
    DataPort keyDrivePort;
    DataPort keyLowRetPort;
    DataPort keyHighRetPort;
    DataPort relPlanePort;
    DataPort relLinePort;
    boolean scannerRunning;

    static int keyret = 0;

    static DataPort createDP(int adr) {
	DataPort dp = new DataPort(adr);
	dp.setStretchCycles(DataPort.STRETCH7);
	dp.setFIFOMode(true);
	return dp;
    }

    public void startScanner() {
	keyMatrix = new int[8];
	keyDebounceMatrix = new int[8];
	theThread = new Thread(this);
	theThread.start();
    }

    public void stopScanner() {
	scannerRunning = false;
    }

    public void run() {
	try {
	keyDrivePort.write(0xFE);
	int i;
	for (i=0;i<8;i++) keyMatrix[i]=0;
	for (i=0;i<8;i++) keyDebounceMatrix[i]=0;
	int row = 0;
	int pat = 0;
	int debpat = 0;
	scannerRunning = true;
	//	System.out.print(esc+"[0H"+esc+"[0J");
	while (scannerRunning) {
	    try {
		Thread.sleep(2);
	    } catch (InterruptedException e) {}
	    // get key return lines
	    pat = keyLowRetPort.read()+256*keyHighRetPort.read();
	    pat ^= 0xFFFF;
	    //	    System.out.print(esc+"["+(row+1)+";0H Key Row "+HEXTOOLS.tohex2(row)+
	    //                             " is "+HEXTOOLS.tohex4(pat));
	    // process debounce and changes
	    debpat = pat&keyDebounceMatrix[row];
	    if (debpat!=keyMatrix[row]) { // this row has changed, process changed sensors
		// System.out.print(" CHANGED");
		int mask = 1;
		int col = 0;
		boolean newsta = false;
		boolean oldsta = false;
		for (col=0;col<16;col++) {
		    newsta = (mask&debpat)!=0;
		    oldsta = (mask&keyMatrix[row])!=0;
		    if (oldsta!=newsta) {        // this sensor has changed, send event
			int kc = row*16+col+(newsta?128:0);
			keyEventQueue.append(TAG_KEY_EVENT,kc,null);
			// System.out.println(esc+"[22;0H Generated Key Event "+HEXTOOLS.tohex2(kc));
                        // System.out.println("   now "+keyEventQueue.entnum+" entries in queue");
		    }
		    mask<<=1;
		}
		keyMatrix[row] = debpat;
	    }
	    else {
		// System.out.print("                ");
	    }
	    // memorize current state as debounce reference pattern
	    keyDebounceMatrix[row] = pat;
	    // move on to next row
	    row++;
	    row &= 0x07;
	    keyDrivePort.write((1<<row)^0xFF);
	}
	} catch (Exception e) { e.printStackTrace(); }
    }

    public void setBiRelay(int relnum, int onoff) {
	int pp = 1<<((relnum/4)&0x07);
	int lp = 1<<(((relnum&0x03)<<1)+((onoff!=0)?1:0));
	// System.out.println("setBiRelay "+relnum+" to "+onoff+", Plane Pattern = "+
        //                    HEXTOOLS.tohex2(pp)+", Line Pattern = "+HEXTOOLS.tohex2(lp));
        try {
	    relPlanePort.write(pp);
	    relLinePort.write(lp);
	}  catch (Exception e) { e.printStackTrace(); }
	try {
	    Thread.sleep(50);
	}
	catch (Exception e) { System.out.println("setBiRelay: sleep() Exception "+e.toString()); }
	try {
	    relPlanePort.write(0);
	    relLinePort.write(0);
	}  catch (Exception e) { e.printStackTrace(); }
	//	try {
	//	    Thread.sleep(100);
	//	}
	//	catch (Exception e) { System.out.println("setBiRelay: sleep() Exception "+e.toString()); }
    }

    public HCS03IO(int basadr, EventQueue keyque) {
	theBaseAddress = basadr;
	keyEventQueue = keyque;
	scannerRunning = false;
	keyDrivePort = createDP(theBaseAddress + KEY_DRIVE_OFFSET);
	keyLowRetPort = createDP(theBaseAddress + KEY_RET_LOW_OFFSET);
	keyHighRetPort = createDP(theBaseAddress + KEY_RET_HIGH_OFFSET);
	relPlanePort = createDP(theBaseAddress + RELAY_PLANE_OFFSET);
	relLinePort = createDP(theBaseAddress + RELAY_LINE_OFFSET);
	try {
	    relPlanePort.write(0x00);
	    relLinePort.write(0x00);
	} catch (Exception e) { e.printStackTrace(); }
    }


    public static void showKeyLines(DataPort dpkl, DataPort dpkh) {
	try {
	    int pat = dpkl.read()+256*dpkh.read();
	    if (pat!=keyret) {
		char cr = (char)0x0D;
		System.out.print(cr);
		System.out.print(HEXTOOLS.tohex4(pat));
		keyret = pat;
	    }
	}
	catch (Exception e) {
	    e.printStackTrace();
	}
    }

    public void testRM0501(int tim) {
	    int i = 0;
	    int j = 0;
	    int RM0501REL[] = {16,17,20,21,18,19,22,23,24,25,28,29};
	    for (i=0;i<RM0501REL.length;i++) {
		j = RM0501REL[i];
		setBiRelay(j,1);
		pause(tim);
		//		System.out.println(Thread.currentThread()+" - Bi-Relay "+j+" set to ON");
	    }
	    for (i=0;i<RM0501REL.length;i++) {
		j = RM0501REL[i];
		setBiRelay(j,0);
		pause(tim);
		//		System.out.println(Thread.currentThread()+" - Bi-Relay "+j+" set to OFF");
	    }
    }

    public void setPlanePort(int v) {
	try {
	    relPlanePort.write(v);
	} catch (Exception e) { e.printStackTrace(); }
    }

    public void setLinePort(int v) {
	try {
	    relLinePort.write(v);
	} catch (Exception e) { e.printStackTrace(); }
    }

    public void pause(int tim) {
	if (tim!=0) {
	  System.out.println("Pausing for "+tim+" milliseconds");
	  try {
	      Thread.sleep(tim);
	  }
	  catch (Exception e) { System.out.println("pause: sleep() Exception "+e.toString()); }
	}
    }

	public static void main(String[] args) {
	    System.out.println("HCS03IO PUCON V 0.2");
	    EventQueue kq = new EventQueue();
	    Event evt;
	    HCS03IO io = new HCS03IO(HCS03IO.PCE0_BASE_ADDRESS,kq);

	    String cmd = (args.length>0)?args[0]:"HELP";
	    String p1  = (args.length>1)?args[1]:"0";
	    int ip1 = Integer.parseInt(p1);
	    String p2  = (args.length>2)?args[2]:"0";
	    int ip2 = Integer.parseInt(p2);
	    String p3  = (args.length>3)?args[3]:"0";
	    int ip3 = Integer.parseInt(p3);

	    System.out.println("ARGS.LENGTH="+args.length);

	    System.out.println("-CMD: "+cmd);
	    System.out.println("--P1: "+p1);
	    System.out.println("--P2: "+p2);

	    if (cmd.startsWith("p")) {
		System.out.println("Setting Plane port to "+HEXTOOLS.tohex2(ip1));
		io.setPlanePort(ip1);
		if (ip2>0) { io.pause(ip2); }
	    }

	    if (cmd.startsWith("l")) {
		System.out.println("Setting Line port to "+HEXTOOLS.tohex2(ip1));
		io.setLinePort(ip1);
		if (ip2>0) { io.pause(ip2); }
	    }

	    if (cmd.startsWith("t")) {
		System.out.println("Testing RM0501, time="+ip2);
		io.testRM0501(ip2);
	    }

	    if (cmd.startsWith("s")) {
		System.out.println("Starting Scanner");
		io.startScanner();
		boolean srun = true;
		CommandReader crd = new CommandReader(kq,TAG_USR_EVENT);
		crd.start();
    		while (srun) {
		    // try {
		    // Thread.sleep(1000);
		    // }
		    // catch (Exception e) { System.out.println("S/sleep: Exception "+e); }
		    // System.out.print(cr+" KQ has "+kq.entnum+" entries");
		    // if (kq.entnum!=0) { kq.println(System.out); }
		    // if ((evt=kq.read())!=null) {
		    evt = kq.waitEvent();
 		    switch (evt.evtcode) {
 		    case TAG_KEY_EVENT:
 		    	  System.out.println(" -- Key Event: STA="+HEXTOOLS.tohex2(evt.subevent/128)+
                                           ".ROW="+HEXTOOLS.tohex2((evt.subevent/16)&0x07)+
                                           ".COL="+HEXTOOLS.tohex2(evt.subevent&0x0F));
			  break;
			case TAG_USR_EVENT:
				System.out.println(" -- USR event: "+evt.subevent);
				if (evt.subevent==('s'-'a')) {
				  srun = false;
				  System.out.println("S-loop will stop");
				  io.stopScanner();
				}
				break;
		    default:
			System.out.println(" -- Event: "+evt.evtcode+"/"+evt.subevent);
		    }
		}
	    }
		
		if (cmd.startsWith("u")) {
		System.out.println("User Interaction");
		CommandReader cr = new CommandReader(kq,TAG_USR_EVENT);
		cr.start();
		boolean running = true;
		while (running) {
			evt = kq.waitEvent();
			switch (evt.evtcode) {
			case TAG_USR_EVENT:
			  System.out.println(" -- U_USR event: "+evt.subevent);
			  if (evt.subevent==('s'-'a')) {
				  running = false;
				  System.out.println("USR loop will stop");
				}
			  break;
			default:
			System.out.println(" -- U_Event: "+evt.evtcode+"/"+evt.subevent);
			}
		}
		}
	}
}
