/*
 * Created on 27-sep-2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.fenalco.utilities.fileloader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Date;
import java.util.Properties;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.fenalco.entities.DataAccess;
import com.fenalco.entities.ProductCategory;
import com.fenalco.entities.SupplierProduct;
import com.fenalco.utilities.database.Transaction;

/**
 * @author andres
 *
 * Eesta clase se encarga de cargar un archivo XML y tomar los productos que
 * vienen en él para insertarlos o actualizarlos en la base de datos 
 */
public class FileLoader extends Thread
{
    
	/**
	 * ruta en donde se van a buscar los archivos xml
	 */
	private String path = "";
	
	/**
	 * Ruta en donde voy a buscar las imagenes
	 */
	private String imagesSearchPath = "";
	
	/**
	 * ruta en donde voy a dejar las imagenes
	 */
	private String imagesPath = "";
	
	/**
	 * Representa al directorio en donde estoy buscando los archivos
	 */
	private File file;
	
	/**
	 * Representa el directorio en donde estoy buscando las imagenes
	 */
	private File imagesFile;

	private Vector listeners;
	
	public FileLoader( ) throws Exception
	{
		// cargo los directorios del archivo de configuración
        Properties props = new Properties();
        InputStream entrada = getClass().getResourceAsStream( "../config/files.properties" );
        if( entrada == null )
        {
            throw new Exception( "File nof found: files.properties" );
        }

        props.load( entrada );
        entrada.close();

        path = props.getProperty( "xml" );
        imagesSearchPath = props.getProperty( "images" );
        imagesPath = props.getProperty( "imagesNew" );
        
		
		
		file = new File( path );
		listeners = new Vector( );
		
		imagesFile = new File( imagesSearchPath );
	}
	
	
	/**
	 * este método procesa el archivo XML
	 * @param fileName
	 */
	public void loadFile( String fileName )
	{
//		System.out.println( "procesando archivo " + fileName );
		
		// creo el evento de procesamiento del archivo
		FileProcessedEvent fpe = new FileProcessedEvent( fileName );
		
		
		////////////////////////////////////////////////////////////////////////////////
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//		factory.setNamespaceAware( true );
//		factory.setValidating( true );
		try
		{
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document document = builder.parse( fileName );
			
			// Aqui debo tener el eanucc:envelope
			Node rootNode = document.getDocumentElement();
//			System.out.println( rootNode.getNodeName() );

			rootNode = searchNode( rootNode, "body", false );
//			System.out.println( rootNode.getNodeName() );
			
			// ya estoy en el body, ahora toca buscar el eanucc:transaction
			rootNode = searchNode( rootNode, "eanucc:transaction", false );
//			System.out.println( rootNode.getNodeName() );

			
			// ya estoy en el transaction, ahora toca buscar el command
			// para todos los command hago lo siguiente
			Node commandNode = searchNode( rootNode, "command", true );
			while( commandNode != null )
			{
//				System.out.println( commandNode.getNodeName() );
	
				// ya estoy en el command, ahora toca buscar el eanucc:documentCommand
				commandNode = searchNode( commandNode, "eanucc:documentCommand", false );
//				System.out.println( commandNode.getNodeName() );
				
				// este es el nodo que necesito y a partir de aqui ahora si viene la 
				// información del producto, por eso se lo paso al método loadProduct
				if( loadProduct( commandNode, fileName ) )
				{
					fpe.addProduct();
				}
				else
				{
					fpe.addError();
				}
				commandNode = searchNode( rootNode, "command", true );
			}
			
			
		}
		catch (ParserConfigurationException e)
		{
			e.printStackTrace();
		}
		catch (SAXException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
		/////////////////////////////////////////////////////////////////////////////////
		
		
		// paso el evento a los listeners para que registren el evento
		for( int i = 0; i < listeners.size(); i++ )
		{
			( ( EventListener )listeners.elementAt( i ) ).processEvent( fpe );
		}
	}
	
	/**
	 * Para cada uno de los productos este método construye el objeto
	 * correspondiente y lo almacena en la base de datos
	 * @param node nodo command del archivo xml
	 * @return booleano que indica si se pudo cargar el archivo o no
	 */
	public boolean loadProduct( Node node, String fileName )
	{
		boolean res = false;
		
		// el nodo en el que estoy es eanucc:documentCommand
		NodeList childs;
		try
		{
			// el primer hijo debe ser el documentCommandHeader
			Node dch = searchNode( node, "documentCommandHeader", false );
			// en este nodo busco el atributo type
			NamedNodeMap attributes = dch.getAttributes();
			String commandType = attributes.getNamedItem( "type" ).getNodeValue();
//			System.out.println( "El tipo de comando es: " + commandType );
			
			
			// el siguiente nodo es documentCommandOperand
			Node dco = searchNode( node, "documentCommandOperand", false );
			
			// dentro de este debo buscar el eanucc:catalogueItemNotification
			node = searchNode( dco, "eanucc:catalogueItemNotification", false );
			
			// ya estoy en eanucc:catalogueItemNotification
			// ahora tengo que buscar el catalogueItem
			node = searchNode( node, "catalogueItem", false );
			
			// ya estoy en el catalogueItem ahora toca buscar el tradeItem
			node = searchNode( node, "tradeItem", false );
			
			
			// ya estoy en el tradeItem, ahora si vienen los datos del producto
			
//			System.out.println( node.getNodeName() );

			
			// reviso que tipo de comando es
			// si es ADD o UPDATE cargo todos los datos para actualizarlos
			if( commandType.equals( "ADD" ) || commandType.equals( "UPDATE" ) )
			{
//				System.out.println( "Comando ADD....." );
				SupplierProduct product = new SupplierProduct( );
				
				// aqui empiezo a buscar a partir de tradeItem
				childs = node.getChildNodes();
				for( int i = 0; i < childs.getLength(); i++ )
				{
					Node n1 = childs.item( i );
					if( n1.getNodeName().equals( "tradeItemUnitDescriptor" ) )
					{
						// de aqui no se saca nada
					}
					else if( n1.getNodeName().equals( "tradeItemIdentification" ) )
					{
						// de aqui se saca el código de barras del producto
						Node n2 = searchNode( n1, "gtin", false );
						// aqui asigno el código de barras al producto
						product.setCode( n2.getFirstChild().getNodeValue() );						
					}
					else if( n1.getNodeName().equals( "tradeItemInformation" ) )
					{
//						System.out.println( "Estoy en el TradeItemInformation............." );
						// Aqui viene el gordo de los datos del producto
						NodeList ch1 = n1.getChildNodes();
						for( int j = 0; j < ch1.getLength(); j++ )
						{
							Node n2 = ch1.item( j );
							if( n2.getNodeName().equals( "informationProviderOfTradeItem" ) )
							{
								// aqui viene la información del proveedor
								
								// asigno el gln del proveedor OBLIGATORIO
								Node n3 = searchNode( searchNode( n2, "informationProvider", false ), "gln", false );
								String gln = n3.getFirstChild().getNodeValue();
								product.setSupplierGLN( gln );
								
								// nombre del proveedor  OBLIGATORIO
								n3 = searchNode( n2, "nameOfInformationProvider", false );
								product.setSupplierName( n3.getFirstChild().getNodeValue() );
								
								// asigno el id del proveedor
								product.setSupplierId( DataAccess.geSuppliertIdFromGln( gln ));
							}
							else if( n2.getNodeName().equals( "paymentTerms" ) )
							{
								// información de pagos que no nos interesa
							}
							else if( n2.getNodeName().equals( "targetMarketInformation" ) )
							{
								// información del mercado objetivo que no nos interesa
							}
							else if( n2.getNodeName().equals( "classificationCategoryCode" ) )
							{
//								System.out.println( "Estoy en el classificationCategoryCode............." );
								// información de la categoría a la cual pertenece el producto
								
								// voy a coger el additionalClassification
								Node n3 = searchNode( n2, "additionalClassification", false );
								
								// creo un objeto de tipo Categoria 
								ProductCategory cat = new ProductCategory( );
								
								// busco quien es la compañía que asigna la categoria
								Node n4 = searchNode( n3, "additionalClassificationAgencyName", false );
								if( n4.getFirstChild().getNodeValue().equals( "CABASNET" ) )
								{	// es cabasnet
									cat.setCabasnet( true );
								}
								else
								{	// no es cabasnet 
									cat.setCabasnet( false );
									cat.setCompany( n4.getFirstChild().getNodeValue() );
								}
								
								// busco el código de la categoria	OBLIGATORIO
								n4 = searchNode( n3, "additionalClassificationCategoryCode", false );
								cat.setCode( Integer.parseInt( n4.getFirstChild().getNodeValue() ) );
								
								// busco la descripción de la categoria	OBLIGATORIO
								n4 = searchNode( n3, "additionalClassificationCategoryDescription", false );
								cat.setName( n4.getFirstChild().getNodeValue() );
								
								// ahora almaceno la información de la categoría en la BD y se la asigno al producto
								
								try
								{
									Transaction t = new Transaction();
									cat.saveData( t );
									t.execute();
								}
								catch( Exception e )
								{
									System.out.println( "No se pudo guardar la categoria" );
								}
								
//								System.out.println( "despues de categoria" );
								
								product.setCategory( cat.getCode() );
							}
							else if( n2.getNodeName().equals( "tradeItemDescriptionInformation" ) )
							{
//								System.out.println( "Estoy en el TradeItemDescriptionInformation............." );
								// aqui viene los siguientes datos del producto
								// * brandName: marca
								// * descriptionShort
								// * functionalName
								// * tradeItemDescription: descripción larga
								// * variant
								
								// marca	OBLIGATORIO
								Node n3 = searchNode( n2, "brandName", false );
								product.setBrand( n3.getFirstChild().getNodeValue() );
								
								// decripción corta	OBLIGATORIO
								n3 = searchNode( searchNode( searchNode( n2, "descriptionShort", false ), 
												"description", false ), "text", false );
								product.setShortDescription( n3.getFirstChild().getNodeValue() );
								
								//nombre funcional	OBLIGATORIO
								n3 = searchNode( searchNode( searchNode( n2, "functionalName", false ), 
												"description", false ), "text", false );
								product.setFunctionalName( n3.getFirstChild().getNodeValue() );
								
								// decripción larga	OBLIGATORIO
								n3 = searchNode( searchNode( searchNode( n2, "tradeItemDescription", false ), 
												"longDescription", false ), "longText", false );
								product.setLongDescription( n3.getFirstChild().getNodeValue() );

								//Variante	NO OBLIGATORIO
								try
								{
									n3 = searchNode( searchNode( searchNode( n2, "variant", false ), 
													"description", false ), "text", false );
									product.setVariant( n3.getFirstChild().getNodeValue() );
								}
								catch( Exception e )
								{
									
								}
							}
							else if( n2.getNodeName().equals( "tradingPartnerNeutralTradeItemInformation" ) )
							{
//								System.out.println( "Estoy en el tradingPartnerNeutral............." );
								// Aquí vienen los siguientes datos:
								// * tradeItemUnitIndicator
								// * manufacturarOfTradeItem: información del fabricante
								// * packaginMarking: información de empaque (no nos interesa)
								// * tradeItemCountryOfOrigin: no nos interesa
								// * tradeItemDateInformation: no nos interesa
								// * tradeItemHandlingInformation: 
								// * tradeItemHierarchy. ??
								// * tradeItemMarking: no nos interesa
								// * tradeItemMeasurements: información de las medidas del producto
								// * tradeItemOrderInformation: ??
								// * cataloguePrice: no nos interesa
								// * suggestedRetailPrice: no nos interesa
								// * tradeItemTaxInformation: informacion del IVA ¿?
								
								
								// tomando la información de tradeItemUnitIndicator
								Node n3 = searchNode( n2, "tradeItemUnitIndicator", false );
								
								
								// consumerUnit	NO OBLIGATORIO
								Node n4 = searchNode( n3, "isTradeItemAConsumerUnit", false );
								try
								{
									product.setConsumerUnit( ( Boolean.valueOf( n4.getFirstChild().getNodeValue() ) ).booleanValue() );
//									System.out.println( "Unidad de consumo = " + product.isConsumerUnit() );
								}
								catch( Exception e )
								{
									
								}

								
								//despatchUnit NO OBLIGATORIO
								try
								{
									n4 = searchNode( n3, "isTradeItemADespatchUnit", false );
									product.setConsumerUnit( ( Boolean.valueOf( n4.getFirstChild().getNodeValue() ) ).booleanValue() );
								}
								catch( Exception e )
								{
									
								}
								
								//invoiceUnit	NO OBLIGATORIO
								try
								{
									n4 = searchNode( n3, "isTradeItemAnInvoiceUnit", false );
									product.setConsumerUnit( ( Boolean.valueOf( n4.getFirstChild().getNodeValue() ) ).booleanValue() );
								}
								catch( Exception e )
								{
									
								}

								// tomando la información del fabricante	NO OBLIGATORIO
								try
								{
									n3 = searchNode( n2, "manufacturerOfTradeItem", false );
									
									// GLN
	//								n4 = searchNode( searchNode( n3, "manufacturer" ), "gln" );
									
									// Nombre del fabricante
									n4 = searchNode( n3, "nameOfManufacturer", false );
									product.setManufacturerName( n4.getFirstChild().getNodeValue() );
									
//									System.out.println( product.getManufacturerName() );
								}
								catch( Exception e )
								{
									
								}
								
								// tomando información del color
								try
								{
									n3 = searchNode( n2, "tradeItemColorDescription", false );
									n4 = searchNode( searchNode( searchNode( n3, "colorDescription", false ), 
											"description", false ), "text", false );
									product.setColor( n4.getFirstChild().getNodeValue() );
								}
								catch( Exception e )
								{
									
								}
								
								// tomando información de las medidas
								n3 = searchNode( n2, "tradeItemMeasurements", false );
								
								NamedNodeMap attr;
								// depth	NO OBLIGATORIO
								try
								{
									n4 = searchNode( searchNode( n3, "depth", false ), "measurementValue", false );
									attr = n4.getAttributes();
									product.setDepthUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									product.setDepth( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}

								// grossWeight	NO OBLIGATORIO
								try
								{
									n4 = searchNode( searchNode( n3, "grossWeight", false ), "measurementValue", false );
									attr = n4.getAttributes();
									product.setGrossWeightUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									product.setGrossWeight( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}
									
								// height	NO OBLIGATORIO
								try
								{
									n4 = searchNode( searchNode( n3, "height", false ), "measurementValue", false );
									attr = n4.getAttributes();
									product.setHeightUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									product.setHeight( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}
									
								// width	NO OBLIGATORIO
								try
								{
									n4 = searchNode( searchNode( n3, "width", false ), "measurementValue", false );
									attr = n4.getAttributes();
									product.setWidthUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									product.setWidth( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}
									
								// netContent	NO OBLIGATORIO
								try
								{
									n4 = searchNode( searchNode( n3, "netContent", false ), "measurementValue", false );
									attr = n4.getAttributes();
									product.setNetContentUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									product.setNetContent( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}
									
								// netWeight	NO OBLIGATORIO
								try
								{
									n4 = searchNode( searchNode( n3, "netWeight", false ), "measurementValue", false );
									attr = n4.getAttributes();
									product.setNetWeightUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									product.setNetWeight( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}
								
								// tomando la información de la talla NO OBLIGATORIO
								try
								{
									n3 = searchNode( n2, "tradeItemSizeDescription", false );
									n4 = searchNode( searchNode( n3, "descriptiveSize", false ), 
											"text", false );
									product.setSize( n4.getFirstChild().getNodeValue() );
								}
								catch( Exception e )
								{
									
								}
								
								// tomando información de las temperaturas NO OBLIGATORIO
								try
								{
									n3 = searchNode( n2, "tradeItemTemperatureInformation", false );
									
									// temperatura máxima
									n4 = searchNode( searchNode( n3, "storageHandlingTemperatureMaximum", false ), 
											"measurementValue", false );
									// unidades
									attr = n4.getAttributes();
									product.setTemperatureUnit( attr.getNamedItem( "unitOfMeasure" ).getNodeValue() );
									
									product.setMaximumTemperature( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );

									// temperatura mínima
									n4 = searchNode( searchNode( n3, "storageHandlingTemperatureMinimum", false ), 
											"measurementValue", false );
									product.setMinimumTemperature( Float.parseFloat( n4.getFirstChild().getNodeValue() ) );
								}
								catch( Exception e )
								{
									
								}
								
								// tomando la información del IVA
								// ¿?¿?¿?
								
//								System.out.println( "Peso Neto = " + product.getNetWeight() );
								
							}
							else if( n2.getNodeName().equals( "tdsn:additionalTextualItemInformation" ) )
							{
//								System.out.println( "Estoy en el tdsn:............." );
								
								NamedNodeMap attr = n2.getAttributes();
								String qualifier = attr.getNamedItem( "qualifier" ).getNodeValue();
								if( qualifier.equals( "CAB01" ) )	// registro sanitario
								{
									try
									{
										Node n3 = searchNode( n2, "tdsn:text", false );
										product.setSanitaryRegistry( n3.getFirstChild().getNodeValue() );
									}
									catch( Exception e )
									{
										
									}
								}
								else if( qualifier.equals( "CAB03" ) )	// fecha del registro sanitario
								{
									try
									{
										Node n3 = searchNode( n2, "tdsn:text", false );
										String theDate = n3.getFirstChild().getNodeValue();
										String date = theDate.substring( 0, 4) + "-" + theDate.substring( 4, 6 ) + "-" + theDate.substring( 6, 8 );
										product.setSanitaryRegistryDate( Date.valueOf( date ) );
									}
									catch( Exception e )
									{
										
									}
								}
							}
						}
					}
				}
				
				// ya tengo toda la información del producto, ahora lo guardo en la base de datos
				
				if( commandType.equals( "ADD" ) )
				{
//					System.out.println( "\nVOY A GUARDAR EL PRODUCTO EN LA BD\n");
					// guardo la información del producto
					Transaction t = new Transaction( );
					product.saveData( t );
					if( t.execute() )
					{
						res = true;		
					}
					else
					{
						res = false;
						ProductFailedEvent pfe = new ProductFailedEvent( fileName, product.getCode(), product.getSupplierGLN() );
						
						// paso el evento a los listeners para que registren el evento
						for( int i = 0; i < listeners.size(); i++ )
						{
							( ( EventListener )listeners.elementAt( i ) ).processEvent( pfe );
						}
					}
				}
			}
			else	// aqui deben venir los demás comandos
			// si es DELETE cargo solo lo básico para identificar al producto y eliminarlo
			{
				
			}
		}
		catch( Exception e )
		{
			e.printStackTrace();
			res = false;
		}
		
		return res;
	}
	
	public Node searchNode( Node theNode, String query, boolean remove )
	{
		Node res = null;
		NodeList childs = theNode.getChildNodes();
		for( int i = 0; i < childs.getLength(); i++ )
		{
			Node tmpNode = childs.item( i );
			if( tmpNode.getNodeName().equals( query ) )
			{
				res = tmpNode;
				if( remove )
				{
					theNode.removeChild( tmpNode );
				}
				break;
			}
		}
		return res;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run()
	{
		while( true )
		{
			try
			{
				// reviso si hay nuevos archivos cada hora
//				wait( 3600000 );
				sleep( 10000 );
			} 
			catch( InterruptedException e )
			{
//					e.printStackTrace();
			}
			
			// primero valido que la ruta especificada sea un directorio
			if( !file.isDirectory() )
			{
//				System.out.println( "La ruta especificada no es un directorio" );
				break;
			}
			
			// obtengo todos los archivos pertenecientes a este directorio
			File[] containedFiles = file.listFiles();
			Vector xmlFiles = new Vector( );
//			System.out.println( "Los archivos son: " );
			for( int i = 0; i < containedFiles.length; i++ )
			{
//				System.out.println( containedFiles[ i ] );
				// reviso que sea un archivo y que tenga extensión xml
				if( containedFiles[ i ].isFile() && containedFiles[ i ].toString().endsWith( ".xml" ) )
				{
					xmlFiles.addElement( containedFiles[ i ] );
//					System.out.println( containedFiles[ i ] );
				}
			}
			
			// ahora proceso todos los archivos encontrados
			for( int i = 0; i < xmlFiles.size(); i++ )
			{
				File f = ( File )xmlFiles.elementAt( i );
				
				// proceso el archivo
				loadFile( f.toString() );
				
				// y muevo el archivo a otro directorio
				String newPath = path + "/old/" + f.getName();
				
				// si el archivo ya existe en el directorio le cambio el nombre
				while( ( new File( newPath ) ).exists() )
				{
					newPath = newPath.substring( 0, newPath.length() - 3 ) + "-1.xml";
				}
				
				try
				{
					// ejecuto el comando para mover el archivo
					Runtime.getRuntime().exec( "mv " + f.toString() + " " + newPath );
				} 
				catch (IOException e1)
				{
					e1.printStackTrace();
				}
				
			}
			
			// ahora voy a buscar las imágenes
			
			// valido que sea un directorio
			if( !imagesFile.isDirectory() )
			{
//				System.out.println( "La ruta especificada de búsqueda de las imagenes no es un directorio" );
				break;
			}
			// obtengo todos los archivos pertenecientes a este directorio
			File[] images = imagesFile.listFiles();
//			System.out.println( "Los archivos son: " );
			for( int i = 0; i < images.length; i++ )
			{
//				System.out.println( containedFiles[ i ] );
				// reviso que sea una imagen jpg
				if( images[ i ].isFile() && ( images[ i ].toString().endsWith( ".jpg" ) ) )
				{
					// muevo el archivo a su nueva ubicación
					String newPath = imagesPath + "/" + images[ i ].getName();
//					System.out.println( containedFiles[ i ] );
					try
					{
						// ejecuto el comando para mover el archivo
						Runtime.getRuntime().exec( "mv " + images[ i ].toString() + " " + newPath );
					} 
					catch (IOException e1)
					{
						e1.printStackTrace();
					}
				}
			}
		}
	}
	
	public void addListener( EventListener el )
	{
		listeners.addElement( el );
	}
	
	public static void main( String[] args )
	{
		try
		{
			FileLoader t = new FileLoader( );
			t.addListener( new Statistics( ) );
			t.start();
		}
		catch( Exception e )
		{
			e.printStackTrace();
		}
	}
	
}