package org.areasy.common.parser.excel.read.biff;

/*
 * Copyright (c) 2007-2020 AREasy Runtime
 *
 * This library, AREasy Runtime and API for BMC Remedy AR System, is free software ("Licensed Software");
 * you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * including but not limited to, the implied warranty of MERCHANTABILITY, NONINFRINGEMENT,
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 */

import org.areasy.common.logger.Logger;
import org.areasy.common.logger.LoggerFactory;
import org.areasy.common.parser.excel.*;
import org.areasy.common.parser.excel.biff.DefaultSheetRange;
import org.areasy.common.parser.excel.biff.IntegerHelper;
import org.areasy.common.parser.excel.biff.RecordData;
import org.areasy.common.parser.excel.biff.StringHelper;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * A number record.  This is stored as 8 bytes, as opposed to the
 * 4 byte RK record
 */
public class HyperlinkRecord extends RecordData implements Hyperlink
{
	/**
	 * The logger
	 */
	private static Logger logger = LoggerFactory.getLog(HyperlinkRecord.class);

	/**
	 * The first row
	 */
	private int firstRow;
	/**
	 * The last row
	 */
	private int lastRow;
	/**
	 * The first column
	 */
	private int firstColumn;
	/**
	 * The last column
	 */
	private int lastColumn;

	/**
	 * The URL referred to by this hyperlink
	 */
	private URL url;

	/**
	 * The local file referred to by this hyperlink
	 */
	private File file;

	/**
	 * The location in this workbook referred to by this hyperlink
	 */
	private String location;

	/**
	 * The range of cells which activate this hyperlink
	 */
	private DefaultSheetRange range;

	/**
	 * The type of this hyperlink
	 */
	private LinkType linkType;

	/**
	 * The excel type of hyperlink
	 */
	private static class LinkType
	{
	}

	;

	private static final LinkType urlLink = new LinkType();
	private static final LinkType fileLink = new LinkType();
	private static final LinkType workbookLink = new LinkType();
	private static final LinkType unknown = new LinkType();

	/**
	 * Constructs this object from the raw data
	 *
	 * @param t  the raw data
	 * @param s  the sheet
	 * @param ws the workbook settings
	 */
	HyperlinkRecord(Record t, Sheet s, WorkbookSettings ws)
	{
		super(t);

		linkType = unknown;

		byte[] data = getRecord().getData();

		// Build up the range of cells occupied by this hyperlink
		firstRow = IntegerHelper.getInt(data[0], data[1]);
		lastRow = IntegerHelper.getInt(data[2], data[3]);
		firstColumn = IntegerHelper.getInt(data[4], data[5]);
		lastColumn = IntegerHelper.getInt(data[6], data[7]);
		range = new DefaultSheetRange(s,
				firstColumn, firstRow,
				lastColumn, lastRow);

		int options = IntegerHelper.getInt(data[28], data[29], data[30], data[31]);

		boolean description = (options & 0x14) != 0;
		int startpos = 32;
		int descbytes = 0;
		if (description)
		{
			int descchars = IntegerHelper.getInt
					(data[startpos], data[startpos + 1],
							data[startpos + 2], data[startpos + 3]);
			descbytes = descchars * 2 + 4;
		}

		startpos += descbytes;

		boolean targetFrame = (options & 0x80) != 0;
		int targetbytes = 0;
		if (targetFrame)
		{
			int targetchars = IntegerHelper.getInt
					(data[startpos], data[startpos + 1],
							data[startpos + 2], data[startpos + 3]);
			targetbytes = targetchars * 2 + 4;
		}

		startpos += targetbytes;

		// Try and determine the type
		if ((options & 0x3) == 0x03)
		{
			linkType = urlLink;

			// check the guid monicker
			if (data[startpos] == 0x03)
			{
				linkType = fileLink;
			}
		}
		else if ((options & 0x01) != 0)
		{
			linkType = fileLink;
			// check the guid monicker
			if (data[startpos] == (byte) 0xe0)
			{
				linkType = urlLink;
			}
		}
		else if ((options & 0x08) != 0)
		{
			linkType = workbookLink;
		}

		// Try and determine the type
		if (linkType == urlLink)
		{
			String urlString = null;
			try
			{
				startpos += 16;

				// Get the url, ignoring the 0 char at the end
				int bytes = IntegerHelper.getInt(data[startpos],
						data[startpos + 1],
						data[startpos + 2],
						data[startpos + 3]);

				urlString = StringHelper.getUnicodeString(data, bytes / 2 - 1,
						startpos + 4);
				url = new URL(urlString);
			}
			catch (MalformedURLException e)
			{
				logger.warn("URL " + urlString + " is malformed.  Trying a file");
				try
				{
					linkType = fileLink;
					file = new File(urlString);
				}
				catch (Exception e3)
				{
					logger.warn("Cannot set to file.  Setting a default URL");

					// Set a default URL
					try
					{
						linkType = urlLink;
						url = new URL("http://www.andykhan.com/jexcelapi/index.html");
					}
					catch (MalformedURLException e2)
					{
						// fail silently
					}
				}
			}
			catch (Throwable e)
			{
				StringBuffer sb1 = new StringBuffer();
				StringBuffer sb2 = new StringBuffer();
				CellReferenceHelper.getCellReference(firstColumn, firstRow, sb1);
				CellReferenceHelper.getCellReference(lastColumn, lastRow, sb2);
				sb1.insert(0, "Exception when parsing URL ");
				sb1.append('\"').append(sb2.toString()).append("\".  Using default.");
				logger.warn(sb1, e);

				// Set a default URL
				try
				{
					url = new URL("http://www.andykhan.com/jexcelapi/index.html");
				}
				catch (MalformedURLException e2)
				{
					// fail silently
				}
			}
		}
		else if (linkType == fileLink)
		{
			try
			{
				startpos += 16;

				// Get the name of the local file, ignoring the zero character at the
				// end
				int upLevelCount = IntegerHelper.getInt(data[startpos],
						data[startpos + 1]);
				int chars = IntegerHelper.getInt(data[startpos + 2],
						data[startpos + 3],
						data[startpos + 4],
						data[startpos + 5]);
				String fileName = StringHelper.getString(data, chars - 1,
						startpos + 6, ws);

				StringBuffer sb = new StringBuffer();

				for (int i = 0; i < upLevelCount; i++)
				{
					sb.append("..\\");
				}

				sb.append(fileName);

				file = new File(sb.toString());
			}
			catch (Throwable e)
			{
				logger.warn("Exception when parsing file " +
						e.getClass().getName() + ".");
				file = new File(".");
			}
		}
		else if (linkType == workbookLink)
		{
			int chars = IntegerHelper.getInt(data[32], data[33], data[34], data[35]);
			location = StringHelper.getUnicodeString(data, chars - 1, 36);
		}
		else
		{
			// give up
			logger.warn("Cannot determine link type");
			return;
		}
	}

	/**
	 * Determines whether this is a hyperlink to a file
	 *
	 * @return TRUE if this is a hyperlink to a file, FALSE otherwise
	 */
	public boolean isFile()
	{
		return linkType == fileLink;
	}

	/**
	 * Determines whether this is a hyperlink to a web resource
	 *
	 * @return TRUE if this is a URL
	 */
	public boolean isURL()
	{
		return linkType == urlLink;
	}

	/**
	 * Determines whether this is a hyperlink to a location in this workbook
	 *
	 * @return TRUE if this is a link to an internal location
	 */
	public boolean isLocation()
	{
		return linkType == workbookLink;
	}

	/**
	 * Returns the row number of the top left cell
	 *
	 * @return the row number of this cell
	 */
	public int getRow()
	{
		return firstRow;
	}

	/**
	 * Returns the column number of the top left cell
	 *
	 * @return the column number of this cell
	 */
	public int getColumn()
	{
		return firstColumn;
	}

	/**
	 * Returns the row number of the bottom right cell
	 *
	 * @return the row number of this cell
	 */
	public int getLastRow()
	{
		return lastRow;
	}

	/**
	 * Returns the column number of the bottom right cell
	 *
	 * @return the column number of this cell
	 */
	public int getLastColumn()
	{
		return lastColumn;
	}

	/**
	 * Gets the URL referenced by this Hyperlink
	 *
	 * @return the URL, or NULL if this hyperlink is not a URL
	 */
	public URL getURL()
	{
		return url;
	}

	/**
	 * Returns the local file eferenced by this Hyperlink
	 *
	 * @return the file, or NULL if this hyperlink is not a file
	 */
	public File getFile()
	{
		return file;
	}

	/**
	 * Exposes the base class method.  This is used when copying hyperlinks
	 *
	 * @return the Record data
	 */
	public Record getRecord()
	{
		return super.getRecord();
	}

	/**
	 * Gets the range of cells which activate this hyperlink
	 * The get sheet index methods will all return -1, because the
	 * cells will all be present on the same sheet
	 *
	 * @return the range of cells which activate the hyperlink
	 */
	public Range getRange()
	{
		return range;
	}

	/**
	 * Gets the location referenced by this hyperlink
	 *
	 * @return the location
	 */
	public String getLocation()
	{
		return location;
	}
}







