/*
 * Copyright 2015 - Scriptel Corporation
 */
package com.scriptel.easyscript.example;

import com.scriptel.easyscript.CardSwipe;
import com.scriptel.easyscript.Coordinate;
import com.scriptel.easyscript.EasyScriptEventListener;
import com.scriptel.easyscript.EasyScript;
import com.scriptel.easyscript.SignatureInvalidException;
import com.scriptel.easyscript.SignatureMetaData;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.geom.Line2D;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * This application demonstrates how to write a simple JFrame based application
 * that will capture EasyScript keyboard events and turn them into signatures.
 * The example also shows how to handle magnetic card swipes.
 */
public class Example implements EasyScriptEventListener {
    /**
     * The JFrame containing swing elements we'll be manipulating. This is
     * separate to keep this example concise.
     */
    private final ExampleForm form;
    /**
     * An EasyScript object we're instantiating in order to parse incoming
     * keyboard events.
     */
    private final EasyScript easyScript;
    /**
     * The scale we want to apply to the incoming coordinates.
     */
    private static final int SCALE = 2;
    /**
     * The width of the screen multiplied by the scale.
     */
    private static final int WIDTH = 240 * SCALE;
    /**
     * The height of the screen multiplied by the scale.
     */
    private static final int HEIGHT = 64 * SCALE;
    /**
     * The thickness of the drawn stroke.
     */
    private static final int LINE_THICKNESS = 2;
    /**
     * The background color that will be applied to the signature image.
     */
    private static final Color BACK_COLOR = Color.WHITE;
    /**
     * The foreground color that will be applied to the signature image.
     */
    private static final Color FORE_COLOR = Color.BLACK;
    /**
     * The graphics context we'll be using to draw the signature.
     */
    private final Graphics2D g;
    /**
     * This coordinate object keeps tracking for the previous coordinate.
     */
    private Coordinate previousCoordinate;
    /**
     * The current stroke count;
     */
    private int strokeCount;

    /**
     * The constructor of the class.
     */
    public Example(){
        // Initialize our form
        form = new ExampleForm();
        // Show our form
        form.setVisible(true);
        // Initialize drawing components
        g = (Graphics2D) form.getCanvas().getGraphics();
        // Antialias lines for smooth lines.
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // Allow for subpixel rendering.
        g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        // Set stroke width.
        g.setStroke(new BasicStroke(LINE_THICKNESS));
        
        // Create EasyScript object
        easyScript = new EasyScript();
        // Register ourselves for coordinate event callbacks
        easyScript.addListener(this);
        
        // Create an event listener looking for key presses on our form.
        KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        mgr.addKeyEventDispatcher(new ScriptelKeyboardDispatcher());
    }
    
    /**
     * Main, starts this program.
     * @param args Command line arguments.
     */
    public static void main(String[] args){
        Example demo = new Example();
        demo.clear();
    }
    
    /**
     * This method clears both the signature area and text box.
     */
    private void clear() {        
        previousCoordinate = null;
        strokeCount = 1;
        form.getTextArea().setText("Instructions:\n-Make sure this window has focus.\n-Sign or Swipe, the gathered information will be shown here.");
        
        //Fill with background color
        g.setColor(BACK_COLOR);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(FORE_COLOR);
    }
    
    /**
     * This method receives coordinates from EasyScript as events are parsed from
     * the incoming EasyScript stream.
     * @param coordinate The most recent coordinate parsed from the incoming
     * character stream.
     */
    @Override
    public void receiveCoordinate(Coordinate coordinate) {
        double x2 = coordinate.getX() * SCALE;
        double y2 = coordinate.getY() * SCALE;
        
        form.getTextArea().append("      {x:" + x2 + ", y:" + y2 + "}\n");
        
        if (previousCoordinate != null){
            double x1 = previousCoordinate.getX() * SCALE;
            double y1 = previousCoordinate.getY() * SCALE;
            
            
            g.draw(new Line2D.Double(x1, y1, x2, y2));
        }
        
        previousCoordinate = coordinate;
    }
    
    /**
     * This method is called when a new stroke is detected.
     */
    @Override
    public void newStroke() {
        form.getTextArea().append("\n Stroke " + (strokeCount++) + ":\n");
        previousCoordinate = null;
    }
    
    /**
     * This method is called when the user presses the cancel button on the
     * ScriptTouch EasyScript device.
     */
    @Override
    public void cancel() {
        clear();
    }
    
    /**
     * This method is called when the user presses the OK button on the ScriptTouch EasyScrpt 
     * device, or when the end of the signature is detected.
     */
    @Override
    public void endOfSignature() {
        //No need to do anything.
    }
    
    /**
     * This method prints the signature metadata to the textArea of the demo form.
     * @param header the array that contains the signature metadata
     */
    @Override
    public void signatureMetaData(SignatureMetaData header) {
        clear();
        StringBuilder sb = new StringBuilder();
        sb.append("Scriptel EasyScript Signature:\n");
        sb.append("  Protocol Version: ").append(header.getProtocolVersion()).append("\n");
        sb.append("      Device Model: ").append(header.getModel()).append("\n");
        sb.append("  Firmware Version: ").append(header.getVersion()).append("\n\n");
        form.getTextArea().append(sb.toString());
    }

    /**
     * This method receives a card swipe from the EasyScript parser.
     * @param swipe Card swipe.
     */
    @Override
    public void cardSwipe(CardSwipe swipe) {
        form.getTextArea().setText(swipe.toString());
    }
    
    /**
     * This class listens for keyboard events and handles them as they occur.
     */
    private class ScriptelKeyboardDispatcher implements KeyEventDispatcher {
        @Override
        public boolean dispatchKeyEvent(KeyEvent ke) {
            try {
                easyScript.parse(ke);
            } catch (SignatureInvalidException e) {
                handleException(e);
            }
            return false;
        }
    }
    
    
    /**
     * If something bad happens this prints the exception to the text box so
     * that we can see it.
     * @param e Exception to handle.
     */
    private void handleException(Exception e) {
        clear();
        StringWriter writer = new StringWriter();
        PrintWriter wrapper = new PrintWriter(writer);
        
        e.printStackTrace(wrapper);
        form.getTextArea().setText(writer.toString());
        
        wrapper.close();
        try {
            writer.close();
        } catch(IOException ex) {
            //This is ok.
        }
    }
    
}
