<?php
/**
 * This file contains the {@link EasyScriptUncompressedDecoder} class.
 * @copyright (c) 2015, Scriptel Corporation.
 */
namespace com\scriptel;

/**
 * This class creates the live signature and coordinates that are generated by an uncompressed
 * ScripTouch EasyScript device.
 */

class EasyScriptUncompressedDecoder implements EasyScriptDecoder {
    /**
     * The maximum number that it is possible to encode using the numbering
     * scheme used by the ScripTouch EasyScript devices.
     */
    const MAX_NUM = 505; //(21*23)+22;
    /**
     * The protocol by which to decode incoming signature strings.
     * @var SignatureProtocol
     */
    private $signatureProtocol;
    /**
     * This is the position of the character in the signature.
     * @var integer
     */
    private $position;
    /**
     * A list of Coordinate Receiver listeners.
     * @var EasyScriptEventListener[].
     */
    private $coordinateReceiverListeners;
    /**
     * This is s string that contains the characters of the incoming signature.
     * @var string
     */
    private $signatureStream;
    /**
     * A new stroke.
     * @var boolean.
     */
    private $newStroke;
    /**
     * Used to decode keyboard codes.
     * @var KeyboardDecoder
     */
    private $keyboardDecoder;

    /**
     * Constructor, creates a new instance of EasyScriptUncompressedDecoder class.
     * @param SignatureProtocol $signatureProtocol The signature protocol that is used (STNSignatureProtocol).
     * @param integer $position Position in the string.
     * @param EasyScriptEventListener[] $listOfCoordinateReceiverListener A list of the CoordinateReceiver listeners.
     */
    public function __construct(SignatureProtocol $signatureProtocol, $position, $listOfCoordinateReceiverListener) {
        $this->coordinateReceiverListeners = $listOfCoordinateReceiverListener;
        $this->signatureProtocol = $signatureProtocol;
        $this->newStroke = TRUE;
        $this->signatureStream = '';
        /* @var EasyScriptEventListener[] */
        if ($this->coordinateReceiverListeners === NULL) {
            $this->coordinateReceiverListeners = array();
        }
        $this->keyboardDecoder = new KeyboardDecoder($signatureProtocol->getXValues(), $signatureProtocol->getYValues());
    }

    /**
     * This method attempts to take a four character point and decode it using
     * the protocol's value tables.
     * @param string[] $ePoint Array of four characters from the signature array to decode.
     * @return Coordinate Representing the decoded point in percentage of the screen (0-&gt;1).
     * @throws SignatureInvalidException Thrown in the event there was a problem reading the point.
     */
    public function decodePoint($ePoint) {
        if (count($ePoint) !== 4) {
            throw new SignatureInvalidException("Didn't find exactly 4 characters. Position: ", $this->position);
        }
        try {
            $values = $this->keyboardDecoder->getValues($ePoint);
        } catch (SignatureInvalidException $ex) {
            throw new SignatureInvalidException($ex->getMessage(). " in position: " . $this->position);
        }

        $x = $values[0] / self::MAX_NUM * $this->signatureProtocol->getWidth();
        $y = $values[1] / self::MAX_NUM * $this->signatureProtocol->getHeight();

        return new Coordinate($x,$y);
    }

    /**
     * This method parses the incoming signature in the batch mode. It converts the signature to
     * single chars to handle it in the streaming mode.
     * @param string The incoming signature.
     */
    public function parse($str) {
       for ($i = 0, $stringLength = strlen($str); $i < $stringLength; $i++) {
           $chr = substr($str, $i, 1);
           parseSignature($chr);
       }
    }
    /**
     * This method parses the signature character by character to decode it.
     * @param string $chr A char read from the signature.
     * @throws SignatureInvalidException Thrown in the event of a problem while parsing the signature.
     */
    public function parseSignature($chr) {
        $this->position++;
        if ($chr === $this->getSignatureProtocol()->getPenUp()) {
            $this->newStroke = TRUE;
            if (strlen($this->signatureStream) != 0) {
                throw new SignatureInvalidException("Buffer wasn't empty when a new stroke was found in position: " . $this->position);
            }
        } else {
            $this->signatureStream .= $chr;
            if (strlen($this->signatureStream) == 4) {
                $myPoint = array(substr($this->signatureStream, 0, 1), substr($this->signatureStream,1 , 1),substr($this->signatureStream, 2, 1),substr($this->signatureStream, 3, 1));
                $coordinate = $this->decodePoint($myPoint);
                $this->signatureStream = '';
                if($this->newStroke) {
                    foreach($this->coordinateReceiverListeners as $listener) {
                        $listener->newStroke();
                    }
                    $this->newStroke = FALSE;
                }
                foreach($this->coordinateReceiverListeners as $listener) {
                    $listener->receiveCoordinate($coordinate);
                }
            }
        }
    }
    /**
     * Gets the current signature protocol.
     * @return SignatureProtocol The current used signature protocol.
     */
    function getSignatureProtocol() {
        return $this->signatureProtocol;
    }

}
