import com.sun.jna.*;

// libfacturista.java 2013-08-06, DLL para generar el XML, sellar y timbrar.
// Ejemplo de uso de libfacturista en Java 
// Se requiere JNA (Java Native Access), si no dispone de este en su entorno, puede usar https://github.com/twall/jna/
//* Ariel Medina - 2013 - facturista.com

// Ejemplo de uso en GNU/Linux
// javac -classpath .:jna-3.5.1.jar libfacturista.java 
// java -cp .:jna-3.5.1.jar -Djna.library.path=/usr/lib libfacturista cfdi_ejemplo.xml cfdi_timbrado.xml

// En windows debera renombrar libfacturista.dll a facturista.dll y ponerlo en la ruta de busqueda de Java

public class libfacturista {


// dll funcs

public static native
String libfacturista_version();

public static native
int  cfdi_comprobante(String  version, String  serie, String  folio, String  fecha,
String  formaDePago, String  subTotal, String  TipoCambio, String  Moneda, String  total,
String  metodoDePago, String  tipoDeComprobante, String  LugarExpedicion, String  NumCtaPago,
String  FolioFiscalOrig, String  SerieFolioFiscalOrig, String  FechaFolioFiscalOrig, String  MontoFolioFiscalOrig);

public static native
int  cfdi_emisor(int  pcfdi, String  rfc, String  nombre);

public static native
int  cfdi_emisor_regimen(int  pcfdi, String  Regimen);

public static native
int  cfdi_emisor_domicilio(int  pcfdi, String  calle, String  noExterior, String  noInterior,
String  colonia, String  localidad, String  municipio, String  estado, String  pais, String  codigoPostal, String  referencia);

public static native
int  cfdi_emisor_expedido_en(int  pcfdi, String  calle, String  noExterior, String  noInterior,
String  colonia, String  localidad, String  municipio, String  estado, String  pais, String  codigoPostal, String  referencia);

public static native
int  cfdi_receptor(int  pcfdi, String  rfc, String  nombre);

public static native
int  cfdi_receptor_domicilio(int  pcfdi, String  calle, String  noExterior, String  noInterior,
String  colonia, String  localidad, String  municipio, String  estado, String  pais, String  codigoPostal, String  referencia);

public static native
int  cfdi_impuestos(int  pcfdi, String  totalImpuestosRetenidos, String  totalImpuestosTrasladados);

public static native
int  cfdi_impuestos_retencion(int  pcfdi, String  impuesto, String  importe);

public static native
int  cfdi_impuestos_traslado(int  pcfdi, String  impuesto, String  importe, String  tasa);

public static native
int  cfdi_concepto(int  pcfdi, String  cantidad, String  unidad, String  noIdentificacion,
String  descripcion, String  valorUnitario, String  importe);

public static native
int  cfdi_concepto_informacionaduanera(int  hc, String  numero, String  fecha, String  aduana);

public static native
int  cfdi_concepto_cuentapredial(int  hc, String  numero);

public static native
int  cfdi_concepto_parte(int  hc, String  cantidad, String  unidad, String  noIdentificacion,
String  descripcion, String  valorUnitario, String  importe);

public static native
int  cfdi_concepto_ComplementoConcepto(int  hc);


public static native
int  cfdi_xml_cargar(int  h, String  rutaFuente);

public static native
int cfdi_xml_guardar(int  h, String  rutaDestino, int decorar);

public static native
int cfdi_xml_validar(int h, String archivoXML, String archivoXSD, String parametros);

public static native
int cfdi_sellar_pkcs8(int  h, String  rutaLlavePrivada, String  contrasena, String  rutaCertificado, String  rutaDestino, int decorar);

public static native
int cfdi_sellar_pem(int  h, String  rutaLlavePrivada, String  contrasena, String  rutaCertificado, String  rutaDestino, int decorar);

public static native
int cfdi_sellar(int  h, String  rutaLlavePrivada, String  rutaCertificado, String  rutaDestino, int decorar);

public static native
int cfdi_timbrar(int  h, String  rutaFuente, String  rutaDestino, int numeroPAC, String  parametros);

public static native
int cfdi_cancelar(int  h, int numeroPAC, String  parametros);

public static native
int cfdi_recuperar(int  h, String  rutaDestino, int numeroPAC, String  parametros);

public static native
String cfdi_estado_cuenta(int  h, int numeroPAC, String  parametros);

public static native
String cfdi_error(int  h);

public static native
String cfdi_resultado(int  h);


public static native
String cfdi_xml_atributo(int  h, String  ruta, String  nombre);

public static native
int cfdi_xml_atributo_agregar(int  h, String  ruta, String  nombre, String  valor);

public static native
int cfdi_xml_atributo_borrar(int  h, String  ruta, String  nombre);

public static native
String cfdi_cadena_original(int  h);

public static native
String cfdi_cadena_original_sat(int  h);

public static native
String cfdi_uuid(int  h);

public static native
String cfdi_fecha_timbrado(int  h);

public static native
String cfdi_numero_certificado_sat(int  h);

public static native
String cfdi_sello_cfd(int  h);

public static native
String cfdi_sello_sat(int  h);

public static native
String cfdi_sello(int  h);

public static native
String cfdi_fecha(int  h);

// si no damos rutaCertificado, lo toma del XML
public static native
String cfdi_numero_certificado(int  h, String  rutaCertificado);


public static native
int xml_caller_charset(int  h, String  charset);

public static native
int  xml_node(int  h, String  path);

public static native
int xml_node_children_number(int  h);

public static native
int  xml_node_by_index(int  h, int index);

public static native
String xml_node_name(int  h);

public static native
int  xml_node_by_name(int  h, String  name);

public static native
String xml_node_attribute(int  h, String  name);

public static native
String xml_serialize(int h, int decorate);

public static native
int xml_insert(int h, String path, String XMLstring, int indexFather, int indexChildren);

public static native
int set_rsmm(String rsmm);

public static native
int set_client_charset(int charsetNumber);

public static native
int cfdi_validar(int h, String archivoXML, String parametros);

public static native
int cfdi_timbrar_lote(int h, String rutaFuente, String rutaDestino, String parametrosLote, int numeroPAC, String parametrosPAC);



static { Native.register("facturista"); }


// Funcion para realizar el timbrado, parametros:
// xml - archivoFuente
// xmltim - archivoDestino
// pruebas - true si queremos servidor de pruebas
// solotimbrado - true si queremos servicio de Solo Timbrado
// h - manejador del XML, si es diferente de 0, lo consideramos como el XML fuente e ignoramos el primer parametro
static
Boolean timbrar(String xml, String xmltim, Boolean pruebas, Boolean solotimbrado, int h)
{
	String timbre, params, rfc, uuid;

	// Los nombres de archivo no deberian estar vacios
	if((h==0 && xml.length()<1) || xmltim.length()<1)
		{
		System.out.println("Nombre de archivo fuente o destino no es valido");
		return false;
		}

	if(h==0)
		{
		// Solo se carga si la estructura XML es correcta
		h=cfdi_xml_cargar(0,xml);
		}

	if(h!=0)
		{
		// Aprovechamos para verificar si tiene timbre
		timbre=cfdi_xml_atributo(h,"cfdi:Comprobante/cfdi:Complemento/tfd:TimbreFiscalDigital","UUID");
		if(timbre.length()>0)
			{
			System.out.println("El archivo ya tiene timbre: "+timbre);
			return false;
			}

		// Obtenemos el RFC del emisor
		rfc=cfdi_xml_atributo(h,"cfdi:Comprobante/cfdi:Emisor","rfc");
		if(rfc.length()<1)
			{
			System.out.println("No se pudo determinar el RFC");
			return false;
			}
		
		// Parametro TransaccionID para el PAC
		params="TransaccionID=1234567890";
		
		// Si se trata de pruebas, ponemos el servidor y puerto adecuado en los parametros
		if(pruebas)
			params=params+";RFC=AAA010101AAA;Servidor=pruebas.ecodex.com.mx;Puerto=2044";
		else
			params=params+";RFC="+rfc;


		// Si no quiere solo timbrado, entonces indicamos al PAC que (re)selle
		if(!solotimbrado)
			params=params+";Sellar=1";
		
		// Intentamos timbrar
		if(cfdi_timbrar(h,xml,xmltim,1,params)!=0)
			{
			h=cfdi_xml_cargar(h,xmltim);
			if (h!=0)
				{
				uuid=cfdi_xml_atributo(h,"cfdi:Comprobante/cfdi:Complemento/tfd:TimbreFiscalDigital","UUID");
				System.out.println("Folio fiscal : "+uuid+"; archivo: "+xmltim);
				return true;
				}
			else
				System.out.println("La factura fue timbrada pero no se pudo cargar; archivo "+xmltim);
			}
		else
				System.out.println("Ha fallado el timbrado, "+cfdi_error(h));
		}

	return false;

}



// libfacturista
public static void main(String[] args)
	{
	if(args.length>0)
		{
		System.out.println("Uso: libfacturista.class \nEj.:\n\tjava -cp .:jna-3.5.1.jar -Djna.library.path=/usr/lib libfacturista\n");
		return;
		}


    int h, he, hr, hc, hi;
	String rutaLlavePrivada, contrasena, rutaCertificado, rutaDestino, xmlTimbrado, version, estadoCuenta;
	
	rutaLlavePrivada="./aaa010101aaa__csd_01.key";
	contrasena="12345678a";
	rutaCertificado="./aaa010101aaa__csd_01.cer";
	rutaDestino="./cfdi_sellado.xml";
	xmlTimbrado="./cfdi_timbrado_java.xml";

	h=cfdi_comprobante("3.2", "A", "1234", "2013-06-27T18:43:11", "Pago en una sola exhibicion", "6172.50", "12.34", "USD", "7124.75", "Efectivo", "ingreso", "Campeche, Campeche", "3849", "", "", "", "");
	  

	// Los caracteres están en UTF8, pero si los vamos a mostrar al usuario en controles de Windows,
	// y nuestro Windows no soporta el UTF8
	// debemos decirle a libfacturista que convierta las cadenas a ACP ( ANSI Code Page)
	// antes de devolverlas
	xml_caller_charset(h,"acp");


	he=cfdi_emisor(h,"MEVA7809229CA","facturista.com");
	if (he==0)
		{
		System.out.println( cfdi_error(h)  );
		return; //-1
		}

	cfdi_emisor_domicilio(he,"31", "7", "Piso 3A", "Col. Mexico", "Campeche", "Municipio de Campeche", "Campeche", "Mexico", "24000", "Por la CFE");
	
	// En caso de ser una sucursal, agregamos su direccion
	cfdi_emisor_expedido_en(he,"39", "8", "Depto. 25", "Col. Jalisco", "San Nicolas de los Garza", "Monterrey", "Nuevo Leon", "Mexico", "74000", "Cerca de Telmex");


	cfdi_emisor_regimen(he,"Regimen 1");
	cfdi_emisor_regimen(he,"Regimen 2");

	// el RFC de los clientes no debe contener guiones o espacios
	hr=cfdi_receptor(h,"X&XX010101AAA","nombre_tu_cliente");
	// los clientes deben tener desglosada su direccion para poder llenar los campos del receptor
	cfdi_receptor_domicilio(hr,"Paseo de Montejo", "1234", "456", "Col. Europea", "Ciudad gotica", "Municipio de Merida", "Yucatan", "Mexico", "38294", "Por la cascada");


	// aqui ponemos el detalle de la factura, en vez de los conceptos del ejemplo
	// deberia poner información de su BD
	hc=cfdi_concepto(h,"10", "PZA", "1", "Memoria USB", "123.45", "1234.50");
	cfdi_concepto_informacionaduanera(hc,"12345", "2011-01-01","Aduana nogales");
	 
	hc=cfdi_concepto(h,"10", "PZA", "2", "Memoria USB", "123.45", "1234.50");
	cfdi_concepto_cuentapredial(hc,"3836463729385495");
	 
	hc=cfdi_concepto(h,"10", "PZA", "3", "Memoria USB", "123.45", "1234.50");
	cfdi_concepto_parte(h,"10", "PZA", "3", "Parte 1", "123.45", "1234.50");
	cfdi_concepto_parte(h,"10", "PZA", "3", "Parte 2", "123.45", "1234.50");
	 
	hc=cfdi_concepto(h,"10", "PZA", "4", "Memoria USB", "123.45", "1234.50");
	 
	hc=cfdi_concepto(h,"10", "PZA", "5", "Memoria USB", "123.45", "1234.50");


	hi=cfdi_impuestos(h,"35.35","987.60");
	  
	cfdi_impuestos_retencion(hi,"ISR","12.12");
	cfdi_impuestos_retencion(hi,"IVA","23.23");
	  
	cfdi_impuestos_traslado(hi,"IVA", "987.60", "16.00");
	cfdi_impuestos_traslado(hi,"IEPS", "0.00", "0.00");


	System.out.println("Cadena original:" + cfdi_cadena_original(h)  );
	
	// Si es DLL demo, no se guarda en un archivo el XML sellado, pero se queda en memoria
	// para poder timbrarlo; al final obtenemos el XML timbrado en un archivo
	if ( cfdi_sellar_pkcs8(h, rutaLlavePrivada, contrasena, rutaCertificado, rutaDestino,1) != 0 )
		{
        //if(timbrar("cfdi_ejemplo.xml",xmlTimbrado,true,false,0))
        // poniendo la cadena vacia y el manejador en el ultimo parametro, se timbra lo que hay en memoria
        if(timbrar("",xmlTimbrado,true,false,h))
        	{
            estadoCuenta = cfdi_estado_cuenta(h, 1, "RFC=AAA010101AAA;TransaccionID=456;Servidor=pruebas.ecodex.com.mx;Puerto=2044");
			System.out.println(estadoCuenta);
			}
		else
			System.out.println("Error al timbrar: " + cfdi_error(h) );
		}
	else
	    	System.out.println("Error al sellar: " + cfdi_error(h) );
    

	version = libfacturista_version();
    System.out.println(version);
    
	}

}