/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.address.gui.addressmanagement;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import lu.tudor.santec.gecamed.address.ejb.entity.beans.Locality;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Municipality;
import lu.tudor.santec.gecamed.address.ejb.entity.beans.Zip;
import lu.tudor.santec.gecamed.address.utils.SGGAHandler;
import lu.tudor.santec.gecamed.core.gui.widgets.progress.ProgressEventDispatcher;
import lu.tudor.santec.gecamed.core.gui.widgets.progress.ProgressEventSource;
import lu.tudor.santec.gecamed.core.gui.widgets.progress.ProgressListener;
import lu.tudor.santec.gecamed.core.utils.StringUtilities;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

//***************************************************************************
//* Class Definition                                                        *
//***************************************************************************

public class AddressUpdater implements ProgressEventSource 
	{
	private static Logger m_Logger = Logger.getLogger(AddressUpdater.class.getName()); 

	private ProgressEventDispatcher				m_ProgressListeners;
	
	private Collection <Locality> 				m_Localities;
	private Collection <Zip> 	 				m_Zips;
	private Collection <Municipality> 			m_Municipalities;
	
	private Collection <Locality>				m_NewLocalities;
	private Collection <Zip>					m_NewZips;
	private Collection <Zip>					m_ObsoleteZips;
	
	private Hashtable <String,String>			m_LocalityAliases;
	
	private Hashtable <Integer,Municipality>	m_MunicipalityLookupById;
	private Hashtable <String,Municipality>		m_MunicipalityLookupByName;
	
	private Hashtable <Integer,Locality>		m_LocalityLookupById;
	private Hashtable <String,Locality>			m_LocalityLookupByName;
	
	private Hashtable <String,Zip>				m_ZipLookup;
	
	private Hashtable <String,Locality>			m_LocalityWithMunicipalityLookup;
	
	private Hashtable <String,File>				m_UpdateFiles;

	
	private static final int c_BufferSize = 2048;
	
	private static byte m_Buffer[] = new byte[c_BufferSize];

	private static Pattern
	
	c_FileNamePattern = Pattern.compile ("^[A-Z0-9]+.*$",Pattern.CASE_INSENSITIVE);
	
	private static Pattern
	
	c_NewLocalityPattern = Pattern.compile ("^\\s*(.+?)\\s*,\\s*(.+?)\\s*,\\s*(.+?)\\s*$",Pattern.CASE_INSENSITIVE);

	private static Pattern
	
	c_LocalityAliasPattern = Pattern.compile ("^\\s*?(.*?)\\s*=\\s*(.*?)\\s*$",Pattern.CASE_INSENSITIVE);
	
	private static Pattern 
    
    c_ZIPLinePattern = Pattern.compile ("^[T|P]\\d+#(.*?)#L-(\\d{4})#(.*?)#([I|P])?#(\\d*)#(\\d*)$",Pattern.CASE_INSENSITIVE);

	private static Pattern 
    
    c_MunicipalityPattern = Pattern.compile ("^([^\\s][\\w-\u00e9\u00e8\u00eb\u00fc\u00fb]*)\\s*(\\(([^\\)][\\w\\s-\u00e9\u00e8\u00eb\u00fc\u00fb]+)\\))?",Pattern.CASE_INSENSITIVE);


//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor		                                                        *
//***************************************************************************
//---------------------------------------------------------------------------

public 	AddressUpdater ()
	{
	m_ProgressListeners = new ProgressEventDispatcher ();
	}
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Class Primitives                                                        *
//***************************************************************************
//---------------------------------------------------------------------------

private Integer parseNumber (String p_Number)
	{
	Integer l_Number = 0;
	
	try	{
		l_Number = Integer.parseInt(p_Number);
		}
	catch (NumberFormatException p_Exception)
		{
		l_Number = 0;
		}
	return l_Number;
	}
	
	
//---------------------------------------------------------------------------

private long getNumberOfLines (File p_File)	
	{
	FileReader			l_FileReader;
	BufferedReader  	l_BufferedReader;                   
	String				l_Line;
	long				l_LineNumber = 0;
	
	if ((p_File != null) && p_File.exists())
		{
		try {
			l_FileReader     = new FileReader (p_File);
			l_BufferedReader = new BufferedReader (l_FileReader);
			do	{
				l_Line = l_BufferedReader.readLine();
				l_LineNumber++;
				}
				while (l_Line != null);
			}
		catch (Exception p_Exception)
			{
			m_Logger.log(Level.ERROR, "Failed to count number of lines for file " + p_File.getName(), p_Exception);
			}
		}
	return l_LineNumber;
	}	
	
//---------------------------------------------------------------------------

private String generateLocalityKey (Locality p_Locality, boolean p_Full)
	{
	StringBuffer l_Key;
	
	l_Key = new StringBuffer ();
	l_Key.append (p_Locality.getName().toLowerCase());
	
	if (p_Full)
		{
		l_Key.append ("_").append (p_Locality.getMunicipalityId().toString());
		}
	
	return l_Key.toString();
	}

//---------------------------------------------------------------------------

private void buildLookupTables ()
	{
	Iterator <Locality>					l_LocalityIterator;
	Locality							l_Locality;
	Locality							l_TwinLocality;
	
	Hashtable <String, Locality>		l_NonUniqueLocalities;
	
	Iterator <Municipality>				l_MunicipalityIterator;
	Municipality						l_Municipality;
	String								l_LocalityKey;
	
	Iterator <Zip>						l_ZipIterator;
	Zip									l_Zip;
	String								l_ZipKey;
	
	StringBuffer						l_Buffer;
	
	if ((m_Municipalities == null) || (m_Localities == null)) return;
	
	m_MunicipalityLookupById   = new Hashtable <Integer,Municipality> ();
	m_MunicipalityLookupByName = new Hashtable <String,Municipality> ();

	l_MunicipalityIterator = m_Municipalities.iterator();
	while (l_MunicipalityIterator.hasNext())
		{
		l_Municipality = l_MunicipalityIterator.next();
		m_MunicipalityLookupById.put(l_Municipality.getId(),   l_Municipality);
		m_MunicipalityLookupByName.put(l_Municipality.getName(), l_Municipality);
		}

	l_Buffer = new StringBuffer ();
	
	m_LocalityWithMunicipalityLookup = new Hashtable <String,Locality> ();
	l_LocalityIterator = m_Localities.iterator();
	while (l_LocalityIterator.hasNext())
		{
		l_Locality     = l_LocalityIterator.next();
		l_Municipality = m_MunicipalityLookupById.get(l_Locality.getMunicipalityId());
		if (l_Municipality != null)
			{
			l_Buffer.setLength (0);
			l_Buffer.append(l_Locality.getName()).append('(');
			l_Buffer.append(l_Municipality.getName()).append(')');
			l_LocalityKey = l_Buffer.toString();
			m_LocalityWithMunicipalityLookup.put(l_LocalityKey.toLowerCase(), l_Locality);
			}
		}
		
	m_LocalityLookupById      = new Hashtable <Integer,Locality> ();
	m_LocalityLookupByName 	  = new Hashtable <String,Locality> ();
	l_NonUniqueLocalities = new Hashtable <String,Locality> ();
	
	l_LocalityIterator = m_Localities.iterator();
	while (l_LocalityIterator.hasNext())
		{
		l_Locality     = l_LocalityIterator.next();
		l_LocalityKey  = this.generateLocalityKey(l_Locality,false);
		
		if (l_NonUniqueLocalities.containsKey(l_LocalityKey))
			{
			l_LocalityKey = this.generateLocalityKey(l_Locality,true);
			}
		
		if (m_LocalityLookupByName.containsKey(l_LocalityKey)) 
			{
			l_NonUniqueLocalities.put (l_LocalityKey,l_Locality);
			l_TwinLocality = m_LocalityLookupByName.get(l_LocalityKey);
			m_LocalityLookupByName.remove(l_LocalityKey);
			
			l_LocalityKey  = this.generateLocalityKey(l_TwinLocality,true);
			m_LocalityLookupByName.put(l_LocalityKey,l_TwinLocality);
			
			l_LocalityKey  = this.generateLocalityKey(l_Locality,true);
			}
			
		m_LocalityLookupByName.put(l_LocalityKey,l_Locality);
		m_LocalityLookupById.put(l_Locality.getId(), l_Locality);
		}	
	
	m_ZipLookup = new Hashtable <String,Zip> ();
	l_ZipIterator = m_Zips.iterator();
	while (l_ZipIterator.hasNext())
		{
		l_Zip = l_ZipIterator.next();
		l_ZipKey = l_Zip.generateZipKey();
		m_ZipLookup.put(l_ZipKey, l_Zip);
		}
	}
	
//---------------------------------------------------------------------------

private String scrubStreetName (String p_RawStreetName)
	{
	String l_StreetName;
	
	if (p_RawStreetName == null) return null;

	l_StreetName = StringUtilities.capitalizeWords(p_RawStreetName);
		
	l_StreetName = l_StreetName.replaceAll("\\b(L')", "l'");
	l_StreetName = l_StreetName.replaceAll("\\b(D')", "d'");
	l_StreetName = l_StreetName.replaceAll("\\sDe\\s",  " de ");
	l_StreetName = l_StreetName.replaceAll("\\sDu\\s",  " du ");
	l_StreetName = l_StreetName.replaceAll("\\sDes\\s", " des ");
	l_StreetName = l_StreetName.replaceAll("\\sDer\\s", " der ");
	
	l_StreetName = l_StreetName.replaceAll("\\sLe\\s", " le ");
	l_StreetName = l_StreetName.replaceAll("\\sLa\\s", " la ");
	l_StreetName = l_StreetName.replaceAll("\\sLes\\s", " les ");
	
	l_StreetName = l_StreetName.replaceAll("\\sOp\\s", " op ");
	
	return 	l_StreetName;
	}

//---------------------------------------------------------------------------	

private Locality processLocalityLine (String p_LocalityLine)
	{
	Matcher			l_LocalityParser;
	Locality		l_Locality = null;
	Municipality	l_Municipality;
	
	if (m_MunicipalityLookupByName == null) this.buildLookupTables();
	
	l_LocalityParser = c_NewLocalityPattern.matcher(p_LocalityLine);
	if (l_LocalityParser.matches())
		{
		l_Locality = new Locality ();
		l_Locality.setName(l_LocalityParser.group(1));
		l_Locality.setLuxembourgishName(l_LocalityParser.group(2));
		
		if (m_MunicipalityLookupByName.containsKey(l_LocalityParser.group(3)))
			{
			l_Municipality = m_MunicipalityLookupByName.get(l_LocalityParser.group(3));
			l_Locality.setMunicipalityId(l_Municipality.getId());
			}
		else l_Locality = null;
		}

	return l_Locality;
	}

//---------------------------------------------------------------------------
	
private Zip processZIPLine (String p_ZIPLine)
	{
	Matcher			l_ZIPParser;
	Matcher			l_MunicipalityMatcher;
	
	String			l_StreetName;
	String			l_ZipCode;
	
	String			l_LocalityName;
	String			l_MunicipalityName;
	String			l_LocalityKey;
	Locality		l_Locality;
	
	StringBuffer	l_Buffer;
	
	Character		l_Parity;
	Integer			l_First	= null;
	Integer			l_Last	= null;
	
	Zip				l_Zip = null;
	
	if (m_LocalityWithMunicipalityLookup == null) this.buildLookupTables();
		
	l_ZIPParser = c_ZIPLinePattern.matcher(p_ZIPLine);
	if (l_ZIPParser.matches())
		{
		l_StreetName = this.scrubStreetName(l_ZIPParser.group(1));
		l_ZipCode    = l_ZIPParser.group(2);
		
		l_LocalityName = l_ZIPParser.group(3).toLowerCase();
		if (m_LocalityAliases.containsKey(l_LocalityName))
			{
			l_LocalityName = m_LocalityAliases.get(l_LocalityName).toLowerCase();
			}

		l_Buffer = new StringBuffer ();
		
		l_MunicipalityMatcher = c_MunicipalityPattern.matcher(l_LocalityName);
		if (l_MunicipalityMatcher.matches() && (l_MunicipalityMatcher.group(3) != null))
			{
			l_LocalityName     = l_MunicipalityMatcher.group(1);
			l_MunicipalityName = l_MunicipalityMatcher.group(3);
			
			l_Buffer.append(l_LocalityName.toLowerCase()).append('(');
			l_Buffer.append(l_MunicipalityName.toLowerCase()).append(')');
			l_LocalityKey = l_Buffer.toString();
			
			l_Locality = m_LocalityWithMunicipalityLookup.get(l_LocalityKey);
			}
		else
			{
			l_Locality = m_LocalityLookupByName.get(l_LocalityName);
			
			if (l_Locality == null)
				{
				l_Buffer.append(l_LocalityName.toLowerCase()).append('(');
				l_Buffer.append(l_LocalityName.toLowerCase()).append(')');
				l_LocalityKey = l_Buffer.toString();
				
				l_Locality = m_LocalityWithMunicipalityLookup.get(l_LocalityKey);			
				}
			}		
		
		if (l_ZIPParser.group(4) != null)
			{
			l_Parity = ("I".equals(l_ZIPParser.group(4)))?Zip.ODD:Zip.EVEN;
			}
		else l_Parity = Zip.NONE;
		
		if ((l_ZIPParser.group(5) != null) && (l_ZIPParser.group(5).length() > 0))
			{
			l_First = this.parseNumber (l_ZIPParser.group(5));
			}
		if ((l_ZIPParser.group(6) != null) && (l_ZIPParser.group(6).length() > 0))
			{
			l_Last = this.parseNumber (l_ZIPParser.group(6));
			}
		
		if (l_Locality == null)
			{
			m_Logger.log(Level.INFO, "Failed to lookup locality " + l_LocalityName);
			}
		else
			{
			l_Zip = new Zip ();
			l_Zip.setLocalityId(l_Locality.getId());
			l_Zip.setZip (this.parseNumber (l_ZipCode));
			l_Zip.setStreet	(l_StreetName);
			l_Zip.setParity (l_Parity);
			l_Zip.setFirst	(l_First);
			l_Zip.setLast	(l_Last);
			}
		}
	return l_Zip;
	}

//---------------------------------------------------------------------------

private Zip processZIP (Zip p_ZIP)
	{
	Matcher			l_MunicipalityMatcher;
	
	String			l_StreetName;
	
	String			l_LocalityName;
	String			l_MunicipalityName;
	String			l_LocalityKey;
	Locality		l_Locality;
	
	StringBuffer	l_Buffer;
	
	if (m_LocalityWithMunicipalityLookup == null) this.buildLookupTables();
		
	l_StreetName   = this.scrubStreetName (p_ZIP.getStreet());
	l_LocalityName = (String) p_ZIP.getNonAssignable(SGGAHandler.c_Locality);
		
	if (m_LocalityAliases.containsKey(l_LocalityName))
		{
		l_LocalityName = m_LocalityAliases.get(l_LocalityName).toLowerCase();
		}

	l_Buffer = new StringBuffer ();
		
	l_MunicipalityMatcher = c_MunicipalityPattern.matcher(l_LocalityName);
	if (l_MunicipalityMatcher.matches() && (l_MunicipalityMatcher.group(3) != null))
		{
		l_LocalityName     = l_MunicipalityMatcher.group(1);
		l_MunicipalityName = l_MunicipalityMatcher.group(3);
			
		l_Buffer.append(l_LocalityName.toLowerCase()).append('(');
		l_Buffer.append(l_MunicipalityName.toLowerCase()).append(')');
		l_LocalityKey = l_Buffer.toString();
			
		l_Locality = m_LocalityWithMunicipalityLookup.get(l_LocalityKey);
		}
	else
		{
		l_Locality = m_LocalityLookupByName.get(l_LocalityName);
			
		if (l_Locality == null)
			{
			l_Buffer.append(l_LocalityName.toLowerCase()).append('(');
			l_Buffer.append(l_LocalityName.toLowerCase()).append(')');
			l_LocalityKey = l_Buffer.toString();
				
			l_Locality = m_LocalityWithMunicipalityLookup.get(l_LocalityKey);			
			}
		}		
		
		
	if (l_Locality == null)
		{
		m_Logger.log(Level.INFO, "Failed to lookup locality " + l_LocalityName);
		}
	else
		{
		p_ZIP.setLocalityId(l_Locality.getId());
		p_ZIP.setStreet	(l_StreetName);
		}
	
	return p_ZIP;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Main Processing Primitives                                              *
//***************************************************************************
//---------------------------------------------------------------------------

private boolean readAliasesFile (File p_AliasFile)
	{
	BufferedReader  l_AliasFileReader;                   
	String			l_AliasLine; 
	Matcher			l_AliasMatcher;
	
	boolean			l_Success = false;
	
	m_LocalityAliases = new Hashtable <String,String> ();
	
	try {
		l_AliasFileReader = new BufferedReader (new InputStreamReader(new FileInputStream(p_AliasFile), "UTF-8"));
			
		do	{
			l_AliasLine = l_AliasFileReader.readLine();
			if (l_AliasLine != null) 
				{
				l_AliasMatcher = c_LocalityAliasPattern.matcher(l_AliasLine);
				if (l_AliasMatcher.matches())
					{
					m_LocalityAliases.put(l_AliasMatcher.group(1), l_AliasMatcher.group(2));
					}
				}
			}
			while (l_AliasLine != null);
		
		l_Success = true;
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.ERROR, "Failed to read Locality Alias File " + p_AliasFile.getName(), p_Exception);
		}
	
	m_Logger.log(Level.INFO, m_LocalityAliases.size() + " Locality Aliases read!");
	
	return l_Success;
	}
	
//---------------------------------------------------------------------------	

private boolean readLocalitiesFile (File p_LocalityFile)
	{
	FileInputStream		l_LocalityFile;
	InputStreamReader	l_LocalityStream;
	BufferedReader  	l_LocalityFileReader;                   
	String				l_LocalityLine; 
	
	Locality			l_NewLocality;
	Locality			l_MatchedLocality;
	String				l_LocalityKey;
	
	boolean				l_Success = false;
	long				l_LineNumber = 0;
	ImportProgress		l_Progress;
	
	m_NewLocalities = new ArrayList <Locality> ();
	l_Progress		= new ImportProgress (this,0,"");
	
	try {
		l_LocalityFile 		 = new FileInputStream(p_LocalityFile);
		l_LocalityStream 	 = new InputStreamReader(l_LocalityFile, "UTF-8");
		l_LocalityFileReader = new BufferedReader (l_LocalityStream);
				
		do	{
			l_LocalityLine = l_LocalityFileReader.readLine();
			if (l_LocalityLine != null) 
				{
				l_LineNumber++;
				l_Progress.setLineNumber(l_LineNumber);			
				m_ProgressListeners.notifyProgressListeners(l_Progress);
				
				l_NewLocality = this.processLocalityLine(l_LocalityLine);
				if (l_NewLocality != null)
					{
					//========================================================
					//= First we're going to try to lookup locality as being
					//= unique, i.e. its name only appears once. If such a
					//= locality can be found and its municipality matches the
					//= new one, then we can say for sure that the new locality
					//= already exists and that we may skip it
					//========================================================
					
					l_LocalityKey = this.generateLocalityKey(l_NewLocality, false);
					if (m_LocalityLookupByName.containsKey(l_LocalityKey)) 
						{
						l_MatchedLocality = m_LocalityLookupByName.get (l_NewLocality.getName().toLowerCase());
						if (l_MatchedLocality.getMunicipalityId().equals(l_NewLocality.getMunicipalityId())) continue;			
						}
					
					//========================================================
					//= Next we're going to try to lookup locality as being
					//= non unique, i.e. there's more than one locality sharing
					//= the same name but being in different municipalities.
					//= By specifying true in the generateLocalityKey method, the
					//= ID of the municipality will be appended to the generated
					//= key, thus if lookup is successful, the new locality already
					//= exists and may be skipped.
					//========================================================

					l_LocalityKey = this.generateLocalityKey(l_NewLocality, true);
					if (m_LocalityLookupByName.containsKey(l_LocalityKey)) continue;

					//========================================================
					//= If no match could be established, then we have to assume
					//= that the new locality didn't yet exist in the database
					//========================================================
					
					m_NewLocalities.add(l_NewLocality);
					}				
				}
			} while (l_LocalityLine != null);
		
		l_Success = true;	
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.ERROR, "Failed to read Locality File " + p_LocalityFile.getName(), p_Exception);
		}
	
	if (l_Success)
		{
		m_Logger.log(Level.INFO, "Found " + m_NewLocalities.size() + " new Localities to import!");
		}
	
	return l_Success;
	}

//---------------------------------------------------------------------------

private boolean readZIPCodeFile (File p_ZIPCodeFile)
	{
	FileReader			l_ZIPFile;
	BufferedReader  	l_ZIPFileReader;                   
	String				l_ZIPLine; 
	
	Zip					l_Zip;
	String				l_ZipKey;
	
	boolean				l_Success 	 = false;
	long				l_LineNumber = 0;
	ImportProgress		l_Progress;
	
	m_NewZips 	    = new ArrayList <Zip> ();
	m_ObsoleteZips  = new ArrayList <Zip> ();
	m_ObsoleteZips.addAll(m_Zips);
	l_Progress		= new ImportProgress (this,0,"");
	
	try {
		l_ZIPFile       = new FileReader (p_ZIPCodeFile);
		l_ZIPFileReader = new BufferedReader (l_ZIPFile);
		do	{
			l_ZIPLine = l_ZIPFileReader.readLine();
			if (l_ZIPLine != null) 
				{
				l_Zip = this.processZIPLine (l_ZIPLine);
				if (l_Zip != null)
					{					
					l_ZipKey = l_Zip.generateZipKey();
					if (m_ZipLookup.containsKey(l_ZipKey))
						 m_ObsoleteZips.remove(l_Zip);
					else m_NewZips.add(l_Zip);
					}				
				l_LineNumber++;
				l_Progress.setLineNumber(l_LineNumber);			
				m_ProgressListeners.notifyProgressListeners(l_Progress);
				}
			} while (l_ZIPLine != null);
		
		l_Success = true;
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.ERROR, "Failed to read ZIP File " + p_ZIPCodeFile.getName(), p_Exception);
		}
	
	if (l_Success)
		{
		m_Logger.log(Level.INFO, "Found " + m_NewZips.size() + " new ZIPs to import!");
		m_Logger.log(Level.INFO, "Found " + m_ObsoleteZips.size() + " ZIPs to delete!");
		}
	
	return l_Success;
	}

//---------------------------------------------------------------------------

//private boolean readZIPCodeFile (File p_ZIPCodeFile)
//	{
//	SGGAParser			l_ZIPParser;                   
//	Collection <Zip>	l_ZIPs;
//		
//	Zip					l_Zip;
//	String				l_ZipKey;
//	
//	boolean				l_Success 	 = false;
//	long				l_LineNumber = 0;
//	ImportProgress		l_Progress;
//	
//	m_NewZips 	    = new ArrayList <Zip> ();
//	m_ObsoleteZips  = new ArrayList <Zip> ();
//	m_ObsoleteZips.addAll(m_Zips);
//	l_Progress		= new ImportProgress (this,0,"");
//	
//	try {
//		l_ZIPParser = new SGGAParser (p_ZIPCodeFile);
//		l_ZIPParser.parse();		
//		
//		l_ZIPs = l_ZIPParser.getZips();
//		}
//	catch (Exception p_Exception)
//		{
//		m_Logger.log(Level.ERROR, "Failed to read ZIP File " + p_ZIPCodeFile.getName(), p_Exception);
//		}
//	
//	if (l_Success)
//		{
//		m_Logger.log(Level.INFO, "Found " + m_NewZips.size() + " new ZIPs to import!");
//		m_Logger.log(Level.INFO, "Found " + m_ObsoleteZips.size() + " ZIPs to delete!");
//		}
//	
//	return l_Success;
//	}




//---------------------------------------------------------------------------	
//***************************************************************************
//* Class Body                                                        		*
//***************************************************************************
//---------------------------------------------------------------------------

public void addProgressListener (ProgressListener p_Listener) 
	{
	m_ProgressListeners.addProgressListener(p_Listener);
	}

//---------------------------------------------------------------------------


public void removeProgressListener (ProgressListener p_Listener) 
	{
	m_ProgressListeners.removeProgressListener(p_Listener);
	}

//---------------------------------------------------------------------------

public void setMunicipalities (Collection <Municipality> p_Municipalities)
	{
	m_Municipalities = p_Municipalities;
	}

//---------------------------------------------------------------------------

public void setLocalities (Collection <Locality> p_Localities)
	{
	m_Localities = p_Localities;
	}

//---------------------------------------------------------------------------

public void setZips (Collection <Zip> p_Zips)
	{
	m_Zips = p_Zips;
	}

//---------------------------------------------------------------------------

public void rebuildLookupTables ()
	{
	this.buildLookupTables();
	}

//---------------------------------------------------------------------------

public Collection <Zip> getNewZips ()
	{
	return m_NewZips;
	}

//---------------------------------------------------------------------------

public Collection <Zip> getObsoleteZips ()
	{
	return m_ObsoleteZips;
	}

//---------------------------------------------------------------------------

public Collection <Locality> getNewLocalities ()
	{
	return m_NewLocalities;
	}

//---------------------------------------------------------------------------

public Municipality getMuncipalityForLocality (Locality p_Locality)
	{
	if (p_Locality == null) return null;
	
	return m_MunicipalityLookupById.get(p_Locality.getMunicipalityId());
	}

//---------------------------------------------------------------------------

public Locality getLocalityForZIP (Zip p_Zip)
	{
	if (p_Zip == null) return null;
	
	return m_LocalityLookupById.get(p_Zip.getLocalityId());
	}

//---------------------------------------------------------------------------

public boolean extractUpdateFiles (File p_UpdateFile)
	{
	FileInputStream			l_ArchiveFile;
	BufferedInputStream		l_ArchiveStream;
	ZipInputStream			l_Archive;
	
	ZipEntry				l_ArchivedFile;
	Matcher					l_FileNameScrubber;

	File					l_File;
	FileOutputStream		l_FileStream;
	BufferedOutputStream 	l_FileWriter;
	int						l_ByteCount;
	
	boolean					l_Success = false;
	
	if ((p_UpdateFile == null) || (p_UpdateFile.exists() == false)) return false;
	
	m_UpdateFiles = new Hashtable <String,File> ();
	
	try	{
		l_ArchiveFile   = new FileInputStream (p_UpdateFile);
		l_ArchiveStream = new BufferedInputStream (l_ArchiveFile);
		l_Archive		= new ZipInputStream(l_ArchiveStream);
			
		do	{
			l_ArchivedFile = l_Archive.getNextEntry();
			if (l_ArchivedFile != null)
				{
				l_FileNameScrubber = c_FileNamePattern.matcher(l_ArchivedFile.getName());
				if (l_FileNameScrubber.matches() == false) continue;
				
				l_File = File.createTempFile (l_ArchivedFile.getName(), null);
				
				l_FileStream = new FileOutputStream (l_File.getPath());
				l_FileWriter = new BufferedOutputStream (l_FileStream,c_BufferSize);
				
				while ((l_ByteCount = l_Archive.read(m_Buffer, 0, c_BufferSize)) != -1) 
					{
					l_FileWriter.write(m_Buffer, 0, l_ByteCount);
					}
				l_FileWriter.flush();
				l_FileWriter.close();
				
				m_UpdateFiles.put(l_ArchivedFile.getName(), l_File);
				}
			} while (l_ArchivedFile != null);
		
		l_Archive.close();	
		l_Success = true;
		}
	catch (IOException p_Exception)
		{
		m_Logger.log(Level.ERROR, "Failed to read address update " + p_UpdateFile.getAbsolutePath(), p_Exception);
		}
	
	return l_Success;
	}

//---------------------------------------------------------------------------

public boolean processAliasesFile ()
	{
	File 	l_File;
	boolean	l_Success = false;
	
	if (m_UpdateFiles.containsKey("Aliases.txt"))
		{
		l_File = m_UpdateFiles.get("Aliases.txt");
		l_Success = this.readAliasesFile (l_File);
		}
	return l_Success;
	}

//---------------------------------------------------------------------------

public long getNumberOfAliasFileLines ()
	{
	File 	l_File;
	long	l_NumberOfLines = 0;
	
	if (m_UpdateFiles.containsKey("Aliases.txt"))
		{
		l_File = m_UpdateFiles.get("Aliases.txt");
		l_NumberOfLines = this.getNumberOfLines(l_File);
		}
	return l_NumberOfLines;
	}

//---------------------------------------------------------------------------

public boolean processLocalityFile ()
	{
	File 	l_File;
	boolean	l_Success = false;
	
	if (m_UpdateFiles.containsKey("Localities.txt"))
		{
		l_File = m_UpdateFiles.get("Localities.txt");
		l_Success = this.readLocalitiesFile (l_File);
		}
	return l_Success;
	}

//---------------------------------------------------------------------------

public long getNumberOfLocalityFileLines ()
	{
	File 	l_File;
	long	l_NumberOfLines = 0;
	
	if (m_UpdateFiles.containsKey("Localities.txt"))
		{
		l_File = m_UpdateFiles.get("Localities.txt");
		l_NumberOfLines = this.getNumberOfLines(l_File);
		}
	return l_NumberOfLines;
	}

//---------------------------------------------------------------------------

public boolean processZIPCodeFile ()
	{
	File 	l_File;
	boolean	l_Success = false;
		
	if (m_UpdateFiles.containsKey("Zips.txt"))
		{
		l_File = m_UpdateFiles.get("Zips.txt");
		l_Success = this.readZIPCodeFile (l_File);
		}
	return l_Success;
	}

//---------------------------------------------------------------------------

//public boolean processZIPCodeFile ()
//	{
//	File 	l_File;
//	boolean	l_Success = false;
//		
//	if (m_UpdateFiles.containsKey("Zips.xml"))
//		{
//		l_File = m_UpdateFiles.get("Zips.xml");
//		l_Success = this.readZIPCodeFile (l_File);
//		}
//	return l_Success;
//	}

//---------------------------------------------------------------------------

public long getNumberOfZIPCodeFileLines ()
	{
	File 	l_File;
	long	l_NumberOfLines = 0;
	
	if (m_UpdateFiles.containsKey("Zips.txt"))
		{
		l_File = m_UpdateFiles.get("Zips.txt");
		l_NumberOfLines = this.getNumberOfLines(l_File);
		}
	return l_NumberOfLines;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* End of Class		                                                    *
//***************************************************************************
//---------------------------------------------------------------------------
	
}
