import java.io.File;
import java.io.FileWriter;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.FileNotFoundException;

import java.net.URL;
import java.net.URLConnection;

import java.util.Date;
import java.util.Hashtable;

import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class LinkLiftPlugin
{
	// ToDo:
	// fill the following constant with the value received by the HTTP-GET-parameter "ls"
	protected final static String GET_PARAMETER_LS								= "";
	
	
	
	// ###################################################################################
	// ###################################################################################
	
	// enter your personal linklift-website-key in here:
	protected final static String LINKLIFT_WEBSITE_KEY							= "1999ad315N0";	// You may replace the website-key in order to adapt this plugin for your other websites
	
	/**	The script-version-final staticants are used for a check
	 *	if a newer version of your plug-in is available on the LinkLift-server.
	 *      Note: the plug-in will not update itself, at this time.
	 */	
	protected final static String LL_PLUGIN_LANGUAGE							= "java";
	protected final static String LL_PLUGIN_VERSION								= "";
	protected final static String LL_PLUGIN_DATE								= "1970-01-01";
	protected final static String LL_PLUGIN_CREATION_DATE						= "20090403173348";
	
	protected final static String LL_PLUGIN_SECRET								= "4spnBYggiI";
	
	
	
	
	/**	In order to not block the page-load-progrss
	 *	a time-limit (in seconds) is set when receiving 
	 *      new data from the LinkLift-server.
	 */
	protected final static int LL_DATA_TIMEOUT									= 7;
	
	
	/**	
	 *	The server-host to connect to in order to download data from LinkLift-server.
	 */
	protected final static String LL_SERVER_HOST								= "external.linklift.net";
	protected final static String LL_SERVER_URL									= "http://" + LL_SERVER_HOST + "/";
	
	
	/**	"LL_data.xml" is the local XML-file used to store textlink-data
	 *	that has been downloaded from the LinkLift-Server.
	 */
	protected final static String LL_TEXTLINK_DATAFILE							= "LL_99aN9d31015_1999ad315N0.xml";
	
	
	
	protected final static int LINKS_NUMBER_MAX									= 10;
	
	protected static Hashtable<String, String[]> textlink_data					= new Hashtable<String, String[]>();
	protected static String[] linklift_fields									= new String[]	{ "prefix"
																								, "url"
																								, "text"
																								, "postfix"
																								, "rss_prefix"
																								, "rss_url"
																								, "rss_text"
																								, "rss_postfix"
																								};
	
	
	
	
// #################################################################################################
// ###  METHODS  ###################################################################################
// #################################################################################################

/**
 * The method retrieves textlink-data to your adspace from the LinkLift-server.
 * The data is received in XML-format and contains information about all textlinks currently booked on your adspace.
 * The received XML is saved as instance-property xml_cache.
 * The method is invoked by ll_textlink_code() if the local XML-file is not existent or out-of-date.
 *
 * @author akniep (Andreas Rayo Kniep)
 * @since 2006-09-18
 * @return The textlink-data that was retrieved from the LinkLift-Data-Server
 */
protected String ll_retrieve_xml_from_ll_server() 
{
	final String request		= LL_SERVER_URL + "/external/textlink_data/" 
												+ "?" + "website_key"		+ "=" + LINKLIFT_WEBSITE_KEY 
												+ "&" + "linklift_secret"	+ "=" + LL_PLUGIN_SECRET 
												+ "&" + "plugin_language"	+ "=" + LL_PLUGIN_LANGUAGE 
												+ "&" + "plugin_version"	+ "=" + LL_PLUGIN_VERSION 
												+ "&" + "plugin_date"		+ "=" + LL_PLUGIN_DATE 
												+ "";
	String xml = null;
	
	try 
	{
		URL url = new URL(request);
		URLConnection connection = url.openConnection();
		
		connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
		connection.setConnectTimeout(LL_DATA_TIMEOUT * 1000);
		
		BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
		
		StringBuilder xml_raw = new StringBuilder();
		
		String line = null;
		while ((line = in.readLine()) != null) 
		{
			xml_raw.append(line);
			xml_raw.append("\r\n");
		} //while()
		
		xml = xml_raw.toString();
	}
	catch (final IOException e)
	{
		System.err.println("Error: " + e.getMessage());
	} //try-catch
	
	return xml;
} //ll_retrieve_xml_from_ll_server()

/**
 * The function retrieves textlink-data to your adspace from the local XML-file (i.e. the file-system).
 * The data is read in XML-format and contains information about all textlinks currently booked on your adspace.
 * Usually, the local XML-file gets updated after a certain period of time by calling ll_retrieve_xml_from_ll_server().
 * The function is invoked by ll_textlink_code() if there is a local XML-file and it is not out-of-date.
 *
 * @author akniep (Andreas Rayo Kniep)
 * @since 2006-09-18
 * @return Textlink-data that has been stored in the local XML-file in XML-format, or an empty String if an error occured.
 */
protected String ll_retrieve_xml_from_file_system() 
{
	final File xml_file	= new File(LL_TEXTLINK_DATAFILE);
	String xml			= null;
	
	try 
	{
		// Open the file that is the first 
		// command line parameter
		FileInputStream	fstream	= new FileInputStream(xml_file);
		
		// Get the object of DataInputStream
		DataInputStream	in		= new DataInputStream(fstream);
		BufferedReader	br		= new BufferedReader(new InputStreamReader(in));
		
		String strLine;
		
		// Read File Line By Line
		while (null != (strLine = br.readLine())) 
		{
			xml += strLine;
		} //while()
		
		// Close the input stream
		in.close();
	}
	catch (final Exception e)
	{
		System.err.println("Error: " + e.getMessage());
	} //try-catch
	
	return xml;
} //ll_retrieve_xml_from_file_system()

/**
 * The function writes textlink-data to your adspace into the local XML-file (i.e. the file-system).
 * The data is received in XML-format and contains information about all textlinks currently booked on your adspace.
 * Usually, the delivered textlink-data has just been received, i.e. downloaded, from the LinkLift-server using ll_retrieve_xml_from_ll_server().
 * The function is invoked by ll_textlink_code() after calling ll_retrieve_xml_from_ll_server() and if the received data exceeds a certain length (of bytes).
 *
 * @author akniep (Andreas Rayo Kniep)
 * @since 2006-09-18
 * @param xml The textlink-data in XML-format that, usually, has just been received from the LinkLift-server.
 * @return true if writing to the file-system was successful
 */
protected boolean ll_write_xml_to_file_system( final String xml ) 
{
	BufferedWriter bufferedWriter = null;
	boolean success = false;
	
	try
	{
		//Construct the BufferedWriter object
		bufferedWriter = new BufferedWriter(new FileWriter(LL_TEXTLINK_DATAFILE));
		
		//Start writing to the output stream
		bufferedWriter.write(xml);
		
		success = true;
	}
	catch (final FileNotFoundException e)
	{
		System.err.println("Error: " + e.getMessage());
	}
	catch (final IOException e)
	{
		System.err.println("Error: " + e.getMessage());
	}
	finally
	{
		//Close the BufferedWriter
		try
		{
			if (bufferedWriter != null)
			{
				bufferedWriter.flush();
				bufferedWriter.close();
			} //if
		}
		catch (final IOException e)
		{
			System.err.println("Error: " + e.getMessage());
		} //try-catch
	} //try-catch-finally
	
	return success;
} //ll_write_xml_to_file_system()

/**
 * The method parses the textlink-data to your adspace out of a String given in XML-format.
 * Usually, the delivered string contains the XML-data either just received from the LinkLift-server or read out of the local XML-file.
 * The method returns a multi-dimensional Array of textlinks containing information like link_url, link_text, link_prefix, link_postfix, and so on.
 * The method is invoked by ll_textlink_code() after calling either ll_retrieve_xml_from_ll_server() or ll_retrieve_xml_from_file_systems().
 *
 * @author akniep (Andreas Rayo Kniep)
 * @since 2006-09-18, 2006-12-03, 2007-12-10
 * @param xml The textlink-data as String and in XML-format that either has just been received from the LinkLift-server or read out of the local XML-file, may be left empty in order to save the instance's xml_cache.
 * @return always true, data will be saved to the classes property "textlink_data"
 */
protected boolean ll_retrieve_textlink_data_from_xml( final String xml )
{
	Pattern p;
	Matcher matches;
	String[] stringArray;
	
	for (final String field : linklift_fields) 
	{
		p			= Pattern.compile("<" + field + ">(.*?)</" + field + ">", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
		matches		= p.matcher(xml);
		
		
		stringArray	= new String[10];
		
		int i = 0;
		while (matches.find()) 
		{
			stringArray[i++] = matches.group(1);
		} //while()
		
		textlink_data.put(field, stringArray);
	} //for(field)
	
	return true;
} //ll_retrieve_textlink_data_from_xml()

/**
 * The method
 *  - retrieves textlink-data to your adspace from the LinkLift-server and stores it in a local XML-file (for reuse) - calling ll_retrieve_xml_from_ll_server();
 *      or retrieves the textlink-data from that local XML-file in order to minimize outbound-traffic - calling ll_retrieve_xml_from_file_systems().
 *  - parses the downloaded or read textlink-data in XML-format into an utilisable array of textlinks - calling ll_retrieve_textlink_data_from_xml().
 *  - generates and outputs plain HTML-links using some CSS-styles in order to obtain the looks you chose on the LinkLift-website;
 *      intentionally, the generated code tries to be as ordinary as possible in order to integrate best with your own HTML-code.
 * 
 * No value is returned since the generated HTML-code is directly outputted to your website (except for delivering $return = true).
 * The method is invoked either at the end of the plugin (using PHP-plugin) or at the position of your choice within your website or blog (using one of the CMS-/Blog-software-plugins).
 *
 * @author akniep (Andreas Rayo Kniep)
 * @since 2006-09-18, 2006-12-03, 2007-10-26
 * @return the generated HTML-code containing your current textlinks
 */
protected String ll_textlink_code() 
{
	final String getParameterLs = GET_PARAMETER_LS;
	
	
	final File xml_file = new File( LL_TEXTLINK_DATAFILE );
	
	if (! xml_file.exists())
	{
		try
		{
			if (! xml_file.createNewFile())
				System.err.println( "Textlink-Data-File (" + xml_file.toString() + ") can not be created!" );
		}
		catch ( final IOException ex )
		{
			System.err.println( "Textlink-Data-File (" + xml_file.toString() + ") can not be created: " + ex.toString() );
		}
		catch ( final SecurityException ex )
		{
			System.err.println( "Textlink-Data-File (" + xml_file.toString() + ") can not be created: " + ex.toString() );
		} //try-catch-catch
	} //if
	
	if (   (! xml_file.isFile())
		|| (! xml_file.canRead())
		|| (! xml_file.canWrite())   )
	{
		System.err.println( "Textlink-Data-File (" + xml_file.toString() + ") is not a readable and writable file!" );
	} //if
	
	
	
	
	final Date now = new Date();
	
	long dateMod = xml_file.lastModified();
	long dateNow = now.getTime();
	
	
	String xml = "";
	//Falls Informationen in der Datei älter sind als 1h oder die Länge der Datei kleiner als 40 Zeichen, dann wird eine Verbindung mit Linklift Server aufgebaut
	if (   (dateMod < (dateNow - (3600 * 1000)))
		|| (40 > xml_file.length())   ) 
	{
		xml = ll_retrieve_xml_from_ll_server();
	} //if
	
	
	if (40 < xml.length()) 
		ll_write_xml_to_file_system(xml);
	else
		xml = ll_retrieve_xml_from_file_system();
	
	boolean result = ll_retrieve_textlink_data_from_xml(xml);
	
	
	// ToDo: check das geparste Resultat
	
	// creating and outputting textlinks
	// generating HTML-links
	// ---------------------------------------------------------------------------v
	String[] prefixArray	= (String[]) textlink_data.get("prefix");
	String[] urlArray		= (String[]) textlink_data.get("url");
	String[] textArray		= (String[]) textlink_data.get("text");
	String[] postfixArray	= (String[]) textlink_data.get("postfix");
	
	final boolean conditionNoHtmlTags = false;
	
	
	StringBuilder textlink_code = new StringBuilder();
	
	textlink_code.append("\r\n");
	
	if (! conditionNoHtmlTags)
	{
		textlink_code.append("<ul>");
		textlink_code.append("\r\n");
	} //if
	
	
	for (int i = 0; i < urlArray.length; i++) 
	{
		if (   (null == urlArray[i])
			|| (null == textArray[i])   )
		{
			continue;
		} //if
		
		
		// do not show (test-)links containing the plugin-secret ...
		if (   (0 <= urlArray[i].indexOf(LL_PLUGIN_SECRET))
			&& (! LL_PLUGIN_SECRET.equals(getParameterLs))   ) 
		{
			continue;
		} //if
		
		
		textlink_code.append("\t");
		if (! conditionNoHtmlTags)
			textlink_code.append("<li>");
		if (i < prefixArray.length) 
			textlink_code.append(prefixArray[i]);
		textlink_code.append("<a href=\"");
		textlink_code.append(urlArray[i]);
		textlink_code.append("\">");
		if (i < textArray.length)
			textlink_code.append(textArray[i]);
		textlink_code.append("</a>");
		if (i < postfixArray.length) 
			textlink_code.append(postfixArray[i]);
		if (! conditionNoHtmlTags)
			textlink_code.append("</li>");
		else
			textlink_code.append("<br />");
		textlink_code.append("\r\n");
	} //for(i)
	
	
	if (! conditionNoHtmlTags)
	{
		textlink_code.append("</ul>");
		textlink_code.append("\r\n");
	} //if
	
	
	
	return textlink_code.toString();
	// ---------------------------------------------------------------------------^
	
	
} //ll_textlink_code()




public static String execute( final boolean returnHtml )
{
	final String textlink_code = new LinkLiftPlugin().ll_textlink_code();
	
	if (returnHtml) 
		return textlink_code;
	
	
	System.out.println(textlink_code);
	
	return null;
} //execute()

public static String execute()
{
	return LinkLiftPlugin.execute(true);
} //execute()


//Function to be executed when Page loads...
public static void main( final String[] args )
{
	LinkLiftPlugin.execute(false);
} //Page_Load



} //class


