package org.manzerbredes.open_klm.drivers;

import java.io.IOException;
import java.util.HashMap;
import org.javatuples.Pair;
import com.codeminders.hidapi.*;


/**
 * 
 * Driver to communicate with the keyboard device 1770 ff00
 * using HIDAPI.
 * 
 * @author Manzerbredes
 *
 */
public class Driver_1770_ff00 implements Driver, DriverTypeA{
	
	/**
	 * Device entry
	 */
	HIDDevice device;
	
	/**
	 * Define Keyboard primary color state
	 */
	private HashMap<Region, Pair<Color,Intensity>> primaryColorsState=new HashMap<>();
	
	/**
	 * Define Keyboard secondary color state (for wave)
	 */
	private HashMap<Region, Pair<Color,Intensity>> secondaryColorsState=new HashMap<>();
	
	/**
	 * Define Keyboard mode state
	 */
	private Mode mode=Mode.NORMAL;


	@Override
	public boolean initDriver(){
		// Init HIDAPI Library
		com.codeminders.hidapi.ClassPathLibraryLoader.loadNativeHIDLibrary();
		
		// Try not bind the device
		try {
			HIDManager man=HIDManager.getInstance();
			this.device=man.openById(0x1770, 0xff00, null);
			if(this.device!=null){
				// Init primary color state
				this.primaryColorsState.put(Region.LEFT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(Color.OFF, Intensity.HIGH));
				this.primaryColorsState.put(Region.MIDDLE, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(Color.OFF, Intensity.HIGH));
				this.primaryColorsState.put(Region.RIGHT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(Color.OFF, Intensity.HIGH));
				// Init secondary color state
				this.secondaryColorsState.put(Region.LEFT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(Color.OFF, Intensity.LOW));
				this.secondaryColorsState.put(Region.MIDDLE, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(Color.OFF, Intensity.LOW));
				this.secondaryColorsState.put(Region.RIGHT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(Color.OFF, Intensity.LOW));
				// Init mode
				this.mode=Mode.NORMAL;
				// Return true (init successfully done)
				return true;
			}
		}
		catch(Exception e){
			System.err.println(e.getMessage());
		}
		return false;
	}
	
	
	/**
	 * Build a byte[] report for convenience.
	 * 
	 * @param a
	 * @param b
	 * @param c
	 * @param d
	 * @param e
	 * @param f
	 * @param g
	 * @param h
	 * @return
	 */
	private byte[] getReport(int a, int b, int c, int d, int e, int f, int g, int h){
		byte[] report={(byte) a,(byte) b,(byte) c,(byte) d,(byte) e,(byte) f,(byte) g,(byte) h};
		return report;
	}
	

	/**
	 * Commit (apply current mode to update the color)
	 */
	private void commit(){
		try {
			this.device.sendFeatureReport(this.getReport(1,2,65,this.mode.intValue(),0,0,0,236));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


	@Override
	public void setRegionColor(Region region, Color color, Intensity intensity) {
		try {
			this.device.sendFeatureReport(this.getReport(1,2,66,region.intValue(),color.intValue(),intensity.intValue(),0,236));
			this.commit();
			this.primaryColorsState.put(region, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}


	@Override
	public void setColor(Color color, Intensity intensity) {
		try {
			this.device.sendFeatureReport(this.getReport(1,2,66,Region.LEFT.intValue(),color.intValue(),intensity.intValue(),0,236));
			this.device.sendFeatureReport(this.getReport(1,2,66,Region.MIDDLE.intValue(),color.intValue(),intensity.intValue(),0,236));
			this.device.sendFeatureReport(this.getReport(1,2,66,Region.RIGHT.intValue(),color.intValue(),intensity.intValue(),0,236));
			this.commit();
			this.primaryColorsState.put(Region.LEFT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
			this.primaryColorsState.put(Region.MIDDLE, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
			this.primaryColorsState.put(Region.RIGHT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));

		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}


	@Override
	public void setMode(Mode mode) {
		this.mode=mode;
		
		// Apply wave mode
		if(this.mode==Mode.WAVE){
			for(int i=0;i<Region.values().length;i++){
				int entry=i*3; // 3 entry for each region (left:1,2,3 -- middle:4,5,6 -- right:7,8,9)
				try {
					// Set primary color (with 2 of intensity for speed problem)
					// TODO Check intensity (fixed for speed) :
					this.device.sendFeatureReport(this.getReport(1,2,67,entry+1 ,this.primaryColorsState.get(Region.values()[i]).getValue0().intValue(),2,0,236));
					// Set secondary color
					this.device.sendFeatureReport(this.getReport(1,2,67,entry+2 ,this.secondaryColorsState.get(Region.values()[i]).getValue0().intValue(),this.secondaryColorsState.get(Region.values()[i]).getValue1().intValue(),0,236));
					// Set period
					// TODO Check period :
					this.device.sendFeatureReport(this.getReport(1,2,67,entry+3 ,2,2,0,236));

				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}
		
		// Apply mode
		this.commit();
	}


	@Override
	public Class<?> getType() {
		return DriverTypeA.class;
	}


	@Override
	public void setSecondaryColor(Color color, Intensity intensity) {
		this.secondaryColorsState.put(Region.LEFT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
		this.secondaryColorsState.put(Region.MIDDLE, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
		this.secondaryColorsState.put(Region.RIGHT, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
	}


	@Override
	public void setSecondaryRegionColor(Region region, Color color, Intensity intensity) {
		this.secondaryColorsState.put(region, new Pair<DriverTypeA.Color, DriverTypeA.Intensity>(color, intensity));
	}






}