package com.stario;

import com.facebook.react.bridge.Promise;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import com.starmicronics.stario.StarIOPort;
import com.starmicronics.stario.StarIOPortException;
import com.starmicronics.stario.StarPrinterStatus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

public class RNStarioPrinterModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;

    public RNStarioPrinterModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    @Override
    public String getName() {
        return "RNStarioPrinter";
    }

    @ReactMethod
    public void check(String portName, Promise promise) {
        try {
            String portSettings = "mini";
            // String portName = "BT:";
            this.checkPrinter(portName, portSettings, promise);
        } catch (Exception e) {
			promise.reject(e.getMessage());
        }
    }

    @ReactMethod
    public void rawprint(String message, String portNameSearch, String portSettings, Promise promise) {
        String portName = "";
        portName = PrinterFunctions.getFirstPrinter(portNameSearch);
        StarIOPort port;

        System.err.println("PORTY: "+portName);
        System.err.println("PORTYSET: "+portSettings);

		ArrayList availableTags = new ArrayList(Arrays.asList(new String[] {"<BOLD>", "<UNDERLINE>","<FLIPPED>","<INVERT>","<CENTER>", "<LEFT>","<RIGHT>","</BOLD>","<HEIGHT>", "<WIDTH>", "</UNDERLINE>","</FLIPPED>","</INVERT>","</CENTER>", "</LEFT>","</RIGHT>","</HEIGHT>", "</WIDTH>", "</SIZE>","</MARGIN>"}));
        LinkedList snippets = parseText(message);
        LinkedList<PrinterFunctions.Alignment> alignments = new LinkedList<PrinterFunctions.Alignment>();;
        PrinterFunctions.Alignment alignment;
        boolean isTag = false;
        boolean emphasized = false;
        boolean underline = false;
        boolean upsideDown = false;
        boolean invertColor = false;
        byte heightExpansion = 0;
        byte widthExpansion = 0;
        int leftMargin = 0;
        ArrayList<byte[]> commands = new ArrayList<byte[]>();

        try{
	        for(Iterator<String> i = snippets.iterator(); i.hasNext(); ) {

			    String item = i.next();
			    System.err.println(item);

				isTag = false;

			    if(availableTags.contains(item)){

				    isTag = true;

				    if(item.equals("<BOLD>")){
					    emphasized = true;
					    System.err.println("bolding");
				    }else if(item.equals("</BOLD>")){
					    emphasized = false;
				    }
				    if(item.equals("<UNDERLINE>")){
					    underline = true;
					    System.err.println("underlining");
				    }else if(item.equals("</UNDERLINE>")){
					    underline = false;
				    }
				    if(item.equals("<FLIPPED>")){
				    	System.err.println("Flipping");
					    upsideDown = true;
				    }else if(item.equals("</FLIPPED>")){
					    upsideDown = false;
				    }
				    if(item.equals("<INVERT>")){
				    	System.err.println("Inverting");
					    invertColor = true;
				    }else if(item.equals("</INVERT>")){
					    invertColor = false;
				    }
				    if(item.equals("<CENTER>")){
				    	System.err.println("Centering");
					    alignments.add(PrinterFunctions.Alignment.Center);
				    }
				    if(item.equals("<LEFT>")){
					    alignments.add(PrinterFunctions.Alignment.Left);
				    }
				    if(item.equals("<RIGHT>")){
					    alignments.add(PrinterFunctions.Alignment.Right);
				    }else if(item.equals("</CENTER>") || item.equals("</LEFT>") || item.equals("</RIGHT>")){
					    try{
					    	alignments.removeLast();
					    }catch(NoSuchElementException ex){

					    }
				    }else if(item.equals("</HEIGHT>")){
					    heightExpansion = 0;
				    }else if(item.equals("</WIDTH>")){
					    widthExpansion = 0;
				    }else if(item.equals("</SIZE>")){
					    heightExpansion = 0;
					    widthExpansion = 0;
				    }else if(item.equals("</MARGIN>")){
					    leftMargin = 0;
				    }else if(item.equals("</MARGIN>")){
					    leftMargin = 0;
				    }


			    }else if(item.length() >= 4){

			    	if(item.substring(0, 4).equals("<HEI")){

					    int helper = Integer.parseInt(item.replaceAll("\\D+",""));
					    helper = (helper > 8)? 8 : helper;

					    heightExpansion = (byte)helper;
					    isTag = true;

				    }else if(item.substring(0, 4).equals("<WID")){

					    int helper = Integer.parseInt(item.replaceAll("\\D+",""));
					    helper = (helper > 8)? 8 : helper;

					    widthExpansion = (byte)helper;
					    isTag = true;

				    }else if(item.substring(0, 4).equals("<SIZ")){
					    int helper = Integer.parseInt(item.replaceAll("\\D+",""));
					    helper = (helper > 8)? 8 : helper;

					    widthExpansion = (byte)helper;
					    heightExpansion = widthExpansion;
					    isTag = true;

				    }else if(item.substring(0, 4).equals("<MAR")){
					    leftMargin = Integer.parseInt(item.replaceAll("\\D+",""));
					    leftMargin = (leftMargin > 256)? 256 : leftMargin;
					    isTag = true;
					}

			    }

			    if(item != "" && !isTag){

				    System.err.println("Printing: " + item);

				    byte[] textToPrint = item.getBytes();

					if(alignments.size() == 0){
						alignment = PrinterFunctions.Alignment.Left;
					}else{
						alignment = alignments.getLast();
					}

					ArrayList<byte[]> commandList = PrinterFunctions.FormatText(portName, portSettings, underline, emphasized, upsideDown, invertColor, heightExpansion, widthExpansion, leftMargin, alignment, textToPrint);

					commands.addAll(commandList);
			    }



			}
		} catch(Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

		PrinterFunctions.sendCommand(portName, portSettings, commands);
		promise.resolve("Success");
    }

	/**
     * This method check the status of the first paired device (we assume it's a printer) and returns "OK"
     */
    private void checkPrinter(String portNameSearch, String portSettings, final Promise promise) {
        String portName = "";
        portName = PrinterFunctions.getFirstPrinter(portNameSearch);

        try {
            StarPrinterStatus status = PrinterFunctions.GetStatus(portName, portSettings, true);

            if (status == null) {
				promise.reject("Cannot get the printer status.");
            } else if (status.offline) {
				promise.reject("The printer is offline.");
            } else {
				promise.resolve("The Printer is online");
            }
        } catch (StarIOPortException e) {
        	e.printStackTrace();
			promise.reject(e.getMessage());
        }
    }

    private String padText(String t, int padding){
	    String text = t;

	    if(text.length() > padding){
		    text = text.substring(0, padding);
	    }else{
		    int spaces = padding - text.length();
		    spaces = (spaces > 500)? 500 : spaces;

		    while(spaces > 0){
			    text += " ";
			    spaces--;
		    }
	    }

	    System.err.println("Padded: " + text + " .");

	    return text;
    }

    private LinkedList parseText(String xml){
	    boolean inTag = false;
	    String curToken = "";
	    int padding = 0;

	    LinkedList<String> result = new LinkedList<String>();

	    for (int i = 0; i < xml.length(); i++){
		    char c = xml.charAt(i);

			if(c == '<' && !inTag){

				inTag = true;

				if(curToken != ""){
					if(padding > 0){
						curToken = padText(curToken, padding);
						padding = 0;
					}
					result.add(curToken);
				}


				curToken = c+"";

			}else if(c == '>' && inTag){

				inTag = false;
				curToken += c;
				curToken = curToken.replaceAll("\\s+","");
				curToken = curToken.toUpperCase();

				if(curToken.length() >= 2){
					if(curToken.substring(0,2).equals("</")){ // this removes numbers in closing tag to help with parsing later on...
						curToken = curToken.replaceAll("\\d","");
					}
				}

				if(curToken.length() >= 4){
					if(curToken.substring(0, 4).equals("<PAD")){

						padding = Integer.parseInt(curToken.replaceAll("\\D+",""));
						System.err.println("PadInt: "+ padding);
						curToken = "";

					}else if(curToken.substring(0, 4).equals("</PA")){

						curToken = result.removeLast();
						curToken = result.removeLast() + curToken; // pop the last two things off the top because they are connected
						padding = 0;
						continue; // we want to immediately go to the next token without pushing onto the stack to prevent new lines

					}
					else{
						result.add(curToken.toUpperCase());
						curToken = "";
					}
				}
				else{
					result.add(curToken.toUpperCase());
					curToken = "";
				}

			}else{
				curToken += c;
			}

		}

		if(curToken != ""){
			if(padding > 0){
				curToken = padText(curToken, padding);
				padding = 0;
			}
			result.add(curToken);
		}

	    return result;
    }
}
