/*
 * PrinterPort.java
 *
 * Created on August 30 2007, 12:29
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
/**
 *
 * @author V.Kravtsov
 */
package com.shtrih.fiscalprinter.port;

import gnu.io.*;
import java.io.*;
import java.util.*;
import org.apache.log4j.*;
import com.shtrih.util.*;
import com.shtrih.fiscalprinter.*;

public class SerialPrinterPort2 implements PrinterPort {

    private int openTimeout = 0;
    private int timeout = 1000;
    private int baudRate = 9600;
    private String portName = "";
    private SerialPort port = null;
    static Logger logger = Logger.getLogger(SerialPrinterPort2.class);

    /**
     * Creates a new instance of PrinterPort
     */
    public SerialPrinterPort2() {
    }

    public Object getConnection()
            throws Exception {
        return port;
    }
    
    public void checkOpened()
            throws Exception {
        if (port == null) {
            throw new Exception("Port is not opened");
        }
    }

    public SerialPort getPort()
            throws Exception {
        checkOpened();
        return port;
    }

    public SerialPort getSerialPort() {
        return port;
    }

    public void setSerialPort(SerialPort port) {
        this.port = port;
    }

    public void setPortName(String portName)
            throws Exception {
        if (!this.portName.equalsIgnoreCase(portName)) {
            close();
            this.portName = portName;
        }
    }

    public void setBaudRate(int baudRate)
            throws Exception {
        if (this.baudRate != baudRate) {
            this.baudRate = baudRate;
            updateBaudRate();
        }
    }

    public int getOpenTimeout() {
        return openTimeout;
    }

    public void setOpenTimeout(int openTimeout) {
        this.openTimeout = openTimeout;
    }

    public void open()
            throws Exception {
        if (isClosed()) {
            port = SharedSerialPorts.getInstance().openPort(portName,
                    openTimeout, this);
            port.setInputBufferSize(1024);
            port.setOutputBufferSize(1024);
            port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
            port.enableReceiveTimeout(0);
            updateBaudRate();
        }
    }

    public void close() {
        if (isOpened()) {
            SharedSerialPorts.getInstance().closePort(port);
            port = null;
        }
    }

    public boolean isOpened() {
        return port != null;
    }

    public boolean isClosed() {
        return port == null;
    }

    public void updateBaudRate()
            throws Exception {
        if (isOpened()) {
            port.setSerialPortParams(baudRate,
                    SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE);
        }
    }

    private void updateTimeout()
            throws Exception {
        if (isOpened()) {
            if (timeout == 0) {
                timeout = 1;
            }
            port.enableReceiveTimeout(timeout);
        }
    }

    public void readAll()
            throws Exception {
        SerialPort serialPort = getPort();
        InputStream is = serialPort.getInputStream();
        int len = is.available();
        if (len > 0) {
            int offset = 0;
            byte[] data = new byte[len];
            is.read(data, offset, len);
        }
    }

    public void setTimeout(int value)
            throws Exception {
        if (value != timeout) {
            timeout = value;
            updateTimeout();
        }
    }

    public byte[] readBytes(int len)
            throws Exception {
        open();

        int offset = 0;
        int readLen = 0;
        byte[] data = new byte[len];

        SerialPort serialPort = getPort();
        InputStream is = serialPort.getInputStream();
        do {
            readLen = is.read(data, offset, len);
            if (readLen <= 0) {
                throw new IOException(
                        Localizer.getString(Localizer.NoConnection));
            }
            offset += readLen;
            len -= readLen;
        } while (len > 0);
        logData("<- ", data);
        return data;
    }

    private void noConnectionError()
            throws Exception {
        throw new IOException(
                Localizer.getString(Localizer.NoConnection));
    }

    private int byte2Int(byte value) {
        int B = value;
        if (B < 0) {
            B = (int) (256 + B);
        }
        return B;
    }

    public int readByte()
            throws Exception {
        byte[] data = readBytes(1);
        return byte2Int(data[0]);
    }

    private int min(int i1, int i2) {
        if (i1 < i2) {
            return i1;
        } else {
            return i2;
        }
    }

    public void logData(String prefix, byte[] data) {
        int linelen = 20;
        int count = (data.length + linelen - 1) / linelen;
        for (int i = 0; i < count; i++) {
            int len = min(linelen, data.length - linelen * i);
            byte b[] = new byte[len];
            System.arraycopy(data, i * linelen, b, 0, len);
            logger.debug(prefix + (Hex.toHex(b, b.length)).toUpperCase());
        }
    }

    public void write(byte[] b)
            throws Exception {
        logData("-> ", b);

        open();

        OutputStream stream = getPort().getOutputStream();
        stream.write(b);
        stream.flush();
    }

    public void write(int b)
            throws Exception {
        logger.debug("-> " + Hex.toHex((byte) b).toUpperCase());

        open();

        OutputStream stream = getPort().getOutputStream();
        stream.write(b);
        stream.flush();
    }
}
