package org.apache.cordova.USBPrinter;

import org.apache.cordova.CallbackContext;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Vector;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.widget.Toast;
import android.util.Log;

@SuppressLint("DefaultLocale")
public class PrinterUtil {
	private static final String TAG = "PrinterUtil";
	private final UsbDevice mDevice;
	private final UsbDeviceConnection mConnection;
	private final UsbInterface mInterface;
	private final UsbEndpoint mEndpointout;
	private final UsbEndpoint mEndpointin;
	public byte[] sendDataCatch = new byte[]{27, 64};

	private static final int TRANSFER_TIMEOUT = 200;

	private final static int ARGB_MASK_RED = 0x00ff0000;
	private final static int ARGB_MASK_GREEN = 0x0000ff00;
	private final static int ARGB_MASK_BLUE = 0x000000ff;
	private final static int color = 128;

	public PrinterUtil(Context context, UsbDevice device, CallbackContext callbackContext) throws IOException {
		UsbInterface iface = null;
		UsbEndpoint epout = null;
		UsbEndpoint epin = null;
		Log.i("xuxl", "getInterfaceCount:" + device.getInterfaceCount());

		for (int i = 0; i < device.getInterfaceCount(); i++) {
			iface = device.getInterface(i);
			if (iface == null) {
			  callbackContext.success("lalalalala");
			  throw new IOException("failed to get interface " + i);
			}
			int epcount = iface.getEndpointCount();

			Log.i("xuxl", "getEndpointCount:" + iface.getEndpointCount());
			for (int j = 0; j < epcount; j++) {
				UsbEndpoint ep = iface.getEndpoint(j);
				if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
					epout = ep;
					// break;
				} else if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
					epin = ep;
					// break;
				}
			}

			if (epout != null)
				break;
		}

		if (epout == null) {
		  callbackContext.success(device.getDeviceName() + "1111111");
			throw new IOException("no output endpoint.");
		}

		mDevice = device;
		mInterface = iface;
		mEndpointout = epout;
		mEndpointin = epin;

		UsbManager usbman = (UsbManager) context.getSystemService(Context.USB_SERVICE);;
		mConnection = usbman.openDevice(device);

		if (mConnection == null) {
		  callbackContext.success(device.getDeviceName());
			throw new IOException("failed to open usb device.");
		}

		mConnection.claimInterface(mInterface, true);
	}

	/**
	 * yk提供的函数，获得缓存状态值
	 * 0x11	缓存区已满
	 * 0x13	缓存区为空
	 */
	private String mEncoding;

	public byte[] receive() {
		byte[] retData = new byte[64];
		int ret = this.mConnection.bulkTransfer(this.mEndpointin, retData, retData.length, 2000);
		Log.e("ret", "ret:" + ret);
		Log.e("retData", "retData:" + bytesToHexFun3(retData));
		if(ret <= 0) {
			return null;
		} else if(ret == 64) {
			return retData;
		} else {
			byte[] realData = new byte[ret];
			for(int i = 0; i < realData.length; ++i) {
				realData[i] = retData[i];
			}
			return realData;
		}
	}

	public int printText(String content) {
		if(content == null) {
			return -1;
		} else {
			try {
				byte[] var3 = content.getBytes(this.mEncoding != null?this.mEncoding:"GBK");
				return this.mConnection.bulkTransfer(this.mEndpointout, var3, var3.length, 3000);
			} catch (UnsupportedEncodingException var31) {
				var31.printStackTrace();
				return -1;
			}
		}
	}

	public void send() throws IOException {
    byte[] sendData = this.sendDataCatch;
    int count = 1;
    int len = sendData.length;
    if (sendData.length > 4096) {
      if (len % 4096 == 0) {
        count = len / 4096;
      } else {
        count = 1 + len / 4096;
      }

    }
    byte[] dataTemp = sendData;
    byte[] content;

    for (int i = 0; i < count; i++) {
      if (dataTemp.length >= 4096) {
        content = ByteArrayUtil.byteArrayGetHead(dataTemp, 4096);
        dataTemp = ByteArrayUtil.byteArrayTrimHead(dataTemp, 4096);
      } else {
        content = dataTemp;
      }
      if (mConnection.bulkTransfer(mEndpointout, content, content.length, TRANSFER_TIMEOUT) != content.length) {
        throw new IOException("failed to write usb endpoint.");
      }
    }
  }

  public void clear () {
    this.sendDataCatch = new byte[]{27, 64};
  }

  public void write(byte[] sendData) {
    byte[] sendDataCatchR = this.sendDataCatch;
    this.sendDataCatch = new byte[sendDataCatchR.length + sendData.length];

    System.arraycopy(sendDataCatchR, 0, this.sendDataCatch, 0, sendDataCatchR.length);
    System.arraycopy(sendData, 0, this.sendDataCatch, sendDataCatchR.length, sendData.length);
  }

	public static String bytesToHexFun3(byte[] bytes) {
		if(bytes==null){
			return null;
		}
		StringBuilder buf = new StringBuilder(bytes.length * 2);
		for(byte b : bytes) { // 使用String的format方法进行转换
			buf.append(String.format("%02x", new Integer(b & 0xff)));
		}
		return buf.toString();
	}
	public String  recevice(String str){
		byte[] recontent=new byte[1];
		mConnection.bulkTransfer(mEndpointin,recontent,recontent.length,1000);
		Log.i(TAG, "write: "+str+bytesToHexFun3(recontent)+ Arrays.toString(recontent));
		return bytesToHexFun3(recontent);
	}

	/**
	*  实时状态传输
	*  打印机状态
	 */
	public String realTimeData() {
		String command="DLE EOT 1";
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null){
			write(b);
			return recevice("realTimeData;");
		}
		return null;
	}

	/**
	 *  实时状态传输
	 *  传输脱机状态
	 */
	public String realTimeoffline() {
		String command="DLE EOT 2";
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null){
			write(b);
			return recevice("realTimeoffline:");
		}
		return null;
	}

	/**
  	 *  esc指令
  	 *  倒置打印
  	 */
  public void reversePrint() {
    String command="ESC { 1";
    byte[] b = EscposUtil.convertEscposToBinary(command);
    if (b != null){
      write(b);
    }
  }

	/**
	 *  实时状态传输
	 *  传输错误状态
	 */
	public String realTimeerr() {
		String command="DLE EOT 3";
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null){
			write(b);
			return recevice("realTimeerr");
		}
		return null;
	}
	/**
	 *  实时状态传输
	 *  传输纸传感器状态
	 */
	public String  realTimepaper() {
		String command="DLE EOT 4";
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null){
			write(b);
			return recevice("realTimepaper:");
		}
		return null;
	}

	public void close() {
		mConnection.releaseInterface(mInterface);
		mConnection.close();
	}

	public static enum ALIGNMENT {
		LEFT, CENTER, RIGHT,
	}

	public static enum FONT {
		FONT_A, FONT_B,
	}

	// public void getPrinterStatus() {
	//
	// }

	/**
	 * 复位重置初始化打印机
	 *
	 * @throws IOException
	 */
	public void resetInit() {
		String command = "";
		byte[] b;
		command = "ESC @";
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);
	}

	/**
	 * 换行
	 *
	 * @throws IOException
	 */
	public void changeLine() {
		byte[] b = new byte[] { 0x0A };
		if (b != null)
			write(b);

	}

	/**
   * 添加文本
   *
   * @throws IOException
   */


	/**
	 * 设置对其方式
	 *
	 * @param alignment
	 * @throws IOException
	 */
	public void selectAlignment(ALIGNMENT alignment) {
		int iAlignment = 0;

		switch (alignment) {
		case LEFT:
			iAlignment = 0;
			break;
		case CENTER:
			iAlignment = 1;
			break;
		case RIGHT:
			iAlignment = 2;
			break;
		default:
			iAlignment = 0;
		}

		String command = String.format("ESC a %d", iAlignment);
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);
	}

	/**
	 * 设置字体
	 *
	 * @param font
	 * @throws IOException
	 */
	public void selectFONT(FONT font) {
		String command = "";
		byte[] b;

		switch (font) {
		case FONT_A:
			command = "ESC M 0";
			break;
		case FONT_B:
			command = "ESC M 1";
			break;
		}

		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);

	}

	/**
	 * 是否加粗
	 *
	 * @param isBold
	 * @throws IOException
	 */
	public void selectBold(boolean isBold) {
		String command = "";
		byte[] b;

		if (isBold) {
			command = "ESC E 1";
		} else {
			command = "ESC E 0";
		}
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);

	}

	/**
	 * 是否有下划线
	 *
	 * @param underlined
	 * @throws IOException
	 */
	public void selectUnderlined(boolean underlined) {
		String command = "";
		byte[] b;

		if (underlined) {
			command = "ESC - 49";
		} else {
			command = "ESC - 48";
		}
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);

	}

	/**
	 * 是否双倍宽高
	 *
	 * @param doubleWidth
	 * @param doubleHeight
	 * @throws IOException
	 */
	public void doubleFontSize(boolean doubleWidth, boolean doubleHeight) {
		int options = 0;
		String command = "";
		byte[] b;
		if (doubleHeight) {
			options |= 1;
		}

		if (doubleWidth) {
			options |= 16;
		}

		command = String.format("GS ! %d", options);
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);
	}

	/**
   * 打印二维码
   */

  public synchronized int printQRCode(String text, int modeSize, boolean isCut) {
  		int len = text.length();
  		if(len <= 0) {
  			return -1;
  		} else {
  			byte[] qrcode = text.getBytes();
  			byte ms;
  			if(modeSize >= 1 && modeSize <= 16) {
  				ms = (byte)modeSize;
  			} else {
  				ms = 6;
  			}
        this.write(new byte[]{29, 40, 107, 3, 0, 49, 67, 6});
        this.write(new byte[]{29, 40, 107, 3, 0, 49, 69, 48});
        byte[] qrCodeHeader = new byte[]{29, 40, 107, (byte)(qrcode.length + 3 & 255), (byte)(qrcode.length + 3 >> 8 & 255), 49, 80, 48};

        int i;
        byte[] qrcodeByte = new byte[qrCodeHeader.length + qrcode.length];

        System.arraycopy(qrCodeHeader, 0, qrcodeByte, 0, qrCodeHeader.length);
        System.arraycopy(qrcode, 0, qrcodeByte, qrCodeHeader.length, qrcode.length);

        this.write(qrcodeByte);
        this.write(new byte[]{29, 40, 107, 3, 0, 49, 81, 48});
        if(isCut) {
          this.write(new byte[]{29, 86, 66, 0});
          return 1;
        } else {
          return 0;
        }
  		}
  	}

	/**
  	 * 设置字体放大倍数
  	 *
  	 * @param zoomWidth
  	 * @param zoomHeight
  	 * @throws IOException
  	 */
  public void zoomFontSize(int zoomWidth, int zoomHeight) {
    int options = 0;
    String command = "";
    byte[] b;
    options = options | zoomHeight | (zoomWidth * 16);
    command = String.format("GS ! %d", options);
    b = EscposUtil.convertEscposToBinary(command);
    if (b != null)
      write(b);
  }

  /**
    	 * 设置打印宽度
    	 *
    	 * @param nL
    	 * @param nH
    	 * @throws IOException
    	 */
    public void setPrintAreaWidth(int nL, int nH) {
      int options = 0;
      String command = "";
      byte[] b;
      command = String.format("GS W %d %d", nL, nH);
      b = EscposUtil.convertEscposToBinary(command);
      if (b != null)
        write(b);
    }

	/**
	 * 打印字符串
	 *
	 * @param content
	 * @throws IOException
	 */
	public void printString(String content) throws UnsupportedEncodingException {
		String command = "";
		byte[] b;

		command = String.format("%s \n", content);
		b = command.getBytes("GB18030");
		if (b != null)
			write(b);
	}

	public void printString(String string, FONT font, Boolean bold, Boolean underlined, Boolean doubleHeight, Boolean doubleWidth) {
		int options = 0;
		String command = "";
		byte[] b;

		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);

		command = String.format("'%s' LF", string);
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);
	}

	/**
	 * 切纸
	 *
	 * @throws IOException
	 */
	public void cutPaper() {
		String command = "GS V 65 20";
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);
	}



	/**
	 * 走纸
	 *
	 * @param n
	 *            行
	 * @throws IOException
	 */
	public void walkPaper(int n) {
		String command = "ESC d " + n;
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);

	}

	/**
	 * 设置行间距
	 *
	 * @param n
	 *            取值1到10,取0为默认行距
	 * @throws IOException
	 */
	public void setLineSpace(int n) {
		String command = "ESC 50";
		if (n != 0) {
			n = n * 20 + 55;
			command = String.format("ESC 51 %d", n);
		}
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null) {
			write(b);
		}

	}

	/**
	 * 打印条码，支持code128-codeB字符集
	 *
	 * @param code
	 * @param width
	 *            宽度 取值2-6，默认是3
	 * @param height
	 *            高度1-255 ，默认162
	 * @throws IOException
	 */
	public void printBarcode(String code, int width, int height) {
		// 设置高度
		String command = String.format("GS 104 %d", height);
		byte[] b = EscposUtil.convertEscposToBinary(command);
		if (b != null) {
			write(b);
		}

		// 设置宽度
		command = String.format("GS 77 %d", width);
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null) {
			write(b);
		}

		byte[] data = code.getBytes();
		byte[] com = new byte[1 + 3 + 1 + 2 + data.length];
		com[0] = 0x0A;
		com[1] = 0x1d;
		com[2] = 0x6b;
		com[3] = 73;
		com[4] = (byte) (data.length + 2);
		com[5] = 123;
		com[6] = 66;
		System.arraycopy(data, 0, com, 7, data.length);
		write(com);
	}

	/**
	 * 参数为 用打开bmp格式的文件二进制字节
	 *
	 * @param bmp
	 * @throws IOException
	 */
	public void printBitmapByBin(byte[] bmp) {
		// // 1D 76 30 m xL xH yL yH d1...dk
		byte[] escBmp = new byte[] { 0x1D, 0x76, 0x30, 48, 0x00, 0x00, 0x00, 0x00 };
		int len = (bmp[0x22] | ((bmp[0x23] & 0xff) << 8) | ((bmp[0x24] & 0xff) << 16) | ((bmp[0x25] & 0xff) << 24));
		int height = bmp[0x16] | ((bmp[0x17] & 0xff) << 8) | ((bmp[0x18] & 0xff) << 16) | ((bmp[0x19] & 0xff) << 24);
		int rowbytes = len / height;
		escBmp[4] = (byte) (rowbytes & 0xff);
		escBmp[5] = (byte) ((rowbytes & 0xff00) >> 8);
		escBmp[6] = (byte) (height & 0xff);
		escBmp[7] = (byte) ((height & 0xff00) >> 8);

		byte[] data = new byte[len];
		byte[] prtdata = new byte[len];
		System.arraycopy(bmp, bmp[0x0A], data, 0, len);
		for (int i = 0, k = 0; i < data.length; i += rowbytes, k++) {

			for (int j = 0; j < rowbytes; j++) {
				prtdata[i + j] = (byte) (data[len - rowbytes * k - rowbytes + j] ^ 0xff);
			}

		}

		write(escBmp);
		write(prtdata);
	}

	/**
	 * 逐行打印位图
	 *
	 * @param bmp
	 *            bitmap图片
	 * @param alignment
	 *            对齐方式
	 * @throws IOException
	 */
	public void printBitmapByLine(Bitmap bmp) {
		byte[] data = new byte[] { 0x1B, 0x33, 0x00 };
		write(data);

		data[0] = (byte) 0x00;
		data[1] = (byte) 0x00;
		data[2] = (byte) 0x00; // 重置参数

		int pixelColor;

		// ESC * m nL nH 点阵图
		byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x00, 0x00, 0x00 };

		escBmp[2] = (byte) 0x21;

		// nL, nH
		escBmp[3] = (byte) (bmp.getWidth() % 256);
		escBmp[4] = (byte) (bmp.getWidth() / 256);
		int len = (bmp.getHeight() / 24 + 1) * bmp.getWidth();

		// 每行进行打印
		for (int i = 0; i < bmp.getHeight() / 24 + 1; i++) {
			write(escBmp);

			for (int j = 0; j < bmp.getWidth(); j++) {
				for (int k = 0; k < 24; k++) {
					if (((i * 24) + k) < bmp.getHeight()) {
						pixelColor = bmp.getPixel(j, (i * 24) + k);
						if (pixelColor != -1) {
							data[k / 8] += (byte) (128 >> (k % 8));
						}
					}
				}

				write(data);

				// 重置参数
				data[0] = (byte) 0x00;
				data[1] = (byte) 0x00;
				data[2] = (byte) 0x00;
			}
			// // 换行
			byte[] byte_send1 = new byte[2];
			byte_send1[0] = 0x0d;
			byte_send1[1] = 0x0a;
			write(byte_send1);
		}

	}

	/**
	 * bitmap的X、Y进行互换
	 *
	 * @param bmp
	 * @return
	 */
	private Bitmap imageYtoX(Bitmap bmp) {
		int[] piexold = new int[bmp.getWidth() * bmp.getHeight()];
		int[] piexnew = new int[bmp.getWidth() * bmp.getHeight()];
		int ss = 0;
		for (int x = 0; x < bmp.getWidth(); x++) {
			for (int y = 0; y < bmp.getHeight(); y++) {
				piexold[ss] = bmp.getPixel(x, y);
				ss++;

			}

		}
		ss = 0;
		for (int y = 0; y < bmp.getHeight(); y++) {
			for (int x = 0; x < bmp.getWidth(); x++) {
				piexnew[y * bmp.getWidth() + x] = piexold[ss];
				ss++;
			}
		}
		Bitmap bitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
		// 通过像素数组生成bitmap,具体参考api
		bitmap.setPixels(piexnew, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
		return bitmap;
	}

	/**
	 * 打印下载的位图
	 *
	 *
	 * 宽高都是8的倍数 1 ≤ x/8 ≤ 255, 1 ≤ y/8 ≤ 48 ,x/8 × y/8 ≤ 912
	 *
	 * @param image
	 * @throws IOException
	 */
	public void printBitmapByCache(Bitmap image) {
		image = imageYtoX(image);

		// GS * x y d1...d(x × y × 8) 定义下载位图 1D 2A x y d1...d(x × y × 8)

		// 判断图片宽是否为8的倍数
		int widthLeft = image.getWidth() % 8;
		int heightLeft = image.getHeight() % 8;
		int printWidth = 0;
		int printHeight = 0;
		if (widthLeft != 0) {
			printWidth = image.getWidth() - widthLeft + 8;
		} else {
			printWidth = image.getWidth();
		}
		if (heightLeft != 0) {
			printHeight = image.getHeight() - heightLeft + 8;
		} else {
			printHeight = image.getHeight();
		}
		int widthOctet = 0;
		int temp;
		int ss = 0;
		int pix;
		int width = 0, column = 0;
		byte[] comm = new byte[4];
		comm[0] = 0x1D;
		comm[1] = 0x2A;
		comm[2] = (byte) (printWidth / 8);
		comm[3] = (byte) (printHeight / 8);

		byte[] Imagelogo = new byte[printWidth / 8 * printHeight];

		// 实现ARGB_8888 的转换
		widthOctet = image.getWidth() / 8;
		for (int row = 0; row < image.getHeight(); row++) {
			for (width = 0, column = 0; width < widthOctet; width++, column += 8) {
				temp = 0;
				for (int i = 0; i < 8; i++) {
					pix = image.getPixel(i + column, row);
					if ((((pix & ARGB_MASK_RED) >> 16) > color) && ((((pix & ARGB_MASK_GREEN) >> 8) > color)) && (((pix & ARGB_MASK_BLUE) > color))) {
						temp = temp << 1;
					} else {
						temp = (temp << 1) + 1;
					}
				}
				Imagelogo[ss] = (byte) temp;
				ss++;
			}

			if (widthLeft != 0) {
				temp = 0;
				for (int i = 0; i < widthLeft; i++) {
					pix = image.getPixel(i + column, row);
					if ((((pix & ARGB_MASK_RED) >> 16) > color) && ((((pix & ARGB_MASK_GREEN) >> 8) > color)) && (((pix & ARGB_MASK_BLUE) > color))) {
						temp = temp << 1;
					} else {
						temp = (temp << 1) + 1;
					}
				}

				temp <<= (8 - widthLeft);
				Imagelogo[ss] = (byte) temp;
				ss++;

			}
		}

		write(comm);

		write(Imagelogo);

		// GS / m 打印下载位图 1D 2F m
		byte[] prtcomm = new byte[3];
		prtcomm[0] = 0x1d;
		prtcomm[1] = 0x2f;
		prtcomm[2] = 48;
		write(prtcomm);

	}

	public void printBmp(String string, byte[] a) {
		String command = "";
		byte[] b;

		b = EscposUtil.convertEscposToBinary(command);
		if (b != null)
			write(b);

		command = String.format("'%s' LF", string);
		b = EscposUtil.convertEscposToBinary(command);
		if (b != null) {
			write(b);
			write(a);
		}

	}

	// 使用光栅位图的打印方式
	public void printBitmap(Bitmap bitmap) throws Exception {
		// GS v 0 m xL xH yL yH d1...dk
		changeLine();
		if (bitmap == null) {
			throw new Exception("bitmap is null");
		}

		byte[] b = new byte[] { 0x1D, 0x76, 0x30, 48 };

		// write(b);
		int width = bitmap.getWidth() / 8;
		int height = bitmap.getHeight();
		byte[] dataParam = new byte[4];
		dataParam[0] = (byte) (width % 256);
		dataParam[1] = (byte) (width / 256);
		dataParam[2] = (byte) (height % 256);
		dataParam[3] = (byte) (height / 256);
		byte[] data = new byte[width * height];
		int index = 0;
		int temp = 0;
		int pix = 0;

		for (int i = 0; i < height; i++) {
			for (int j = 0, column = 0; j < width; j++, column += 8) {
				temp = 0;
				for (int k = 0; k < 8; k++) {
					pix = bitmap.getPixel(k + column, i);
					if ((((pix & ARGB_MASK_RED) >> 16) > color) && ((((pix & ARGB_MASK_GREEN) >> 8) > color)) && (((pix & ARGB_MASK_BLUE) > color))) {
						temp = temp << 1;
					} else {
						temp = (temp << 1) + 1;
					}
				}
				data[index++] = (byte) temp;

			}
		}
		write(b);
		write(dataParam);
		write(data);

		// Log.i("xuxl", "command=" + DataProcessUtil.bytesToHexString(b));
		// Log.i("xuxl", "command=" +
		// DataProcessUtil.bytesToHexString(dataParam));
		// Log.i("xuxl", "command=" + DataProcessUtil.bytesToHexString(data));
	}
}
