package hiro.yoshioka.sdh;

import hiro.yoshioka.util.ColorNameEnum;
import hiro.yoshioka.util.ColorUtil;
import hiro.yoshioka.util.FileExtentionType;
import hiro.yoshioka.util.FileUtil;
import hiro.yoshioka.util.LineSeparatorEnum;
import hiro.yoshioka.util.StringUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;

public class DefaultHyperLinkAction implements IRDHHyperLinkAction {
	public static final Pattern HREF_PATTERN = Pattern
			.compile("<a\\s+href=\"([^\"]+)\"[^>]+>(.+?)</a>");
	public static final Pattern HREF_PATTERN2 = Pattern
			.compile("<a\\s+href=\'([^']+)\'[^>]+>(.+?)</a>");
	protected static Log log = LogFactory.getLog(DefaultHyperLinkAction.class);
	Set<Integer> linkSet;

	public DefaultHyperLinkAction(int[] linkIdx) {
		this.linkSet = new LinkedHashSet<Integer>();
		for (int i = 0; i < linkIdx.length; i++) {
			linkSet.add(linkIdx[i]);
		}
	}

	public DefaultHyperLinkAction(Collection<Integer> numSet) {
		linkSet = new LinkedHashSet<Integer>(numSet);
	}

	private static List<HyperLinkData> parse(String string) {
		if (string.trim().length() > 0) {
			List<HyperLinkData> retList = new ArrayList<DefaultHyperLinkAction.HyperLinkData>();
			for (String line : StringUtil.getLines(string)) {
				if (StringUtil.isEmpty(line)) {
					continue;
				}
				String[] linkInfos = DefaultHyperLinkAction
						.getHyperLinkStrings(line);
				if (linkInfos == null) {
					File f = new File(line);
					if (f.exists() && f.canRead()) {
						if (f.isFile()) {
							retList.add(new HyperLinkData(HyperLinkType.file, f
									.getAbsolutePath(), String.format(
									"%s (%s)", f.getName(),
									FileUtil.getLengthString(f))));
						} else if (f.isDirectory()) {
							retList.add(new HyperLinkData(HyperLinkType.file, f
									.getAbsolutePath(), String.format("%s/",
									f.getName(), FileUtil.getLengthString(f))));
						}
					} else {
						retList.add(new HyperLinkData(HyperLinkType.other,
								line, line));
					}
				} else {
					retList.add(new HyperLinkData(HyperLinkType.href,
							linkInfos[0], linkInfos[1]));
				}
			}
			return retList;
		}
		return Collections.EMPTY_LIST;
	}

	public static final String getDisplayString(String string) {
		List<HyperLinkData> retList = parse(string);
		if (retList.size() == 0) {
			return string;
		}
		return StringUtil
				.join(retList, LineSeparatorEnum.LF.getLineSeparator());
	}

	private static final String[] getHyperLinkStrings(String str) {
		if (str.startsWith("<a")) {
			Matcher m = HREF_PATTERN.matcher(str);
			if (m.find()) {
				return new String[] { m.group(1), m.group(2) };
			}
			m = HREF_PATTERN2.matcher(str);
			if (m.find()) {
				return new String[] { m.group(1), m.group(2) };
			}
		}
		return null;
	}

	protected boolean checkActivateHyperLink(CSVRecordDataHolder cdh,
			int colIdx, int row) {
		if (linkSet == null || !linkSet.contains(colIdx)) {
			return false;
		}
		return true;
	}

	public void activateHyperLink(CSVRecordDataHolder cdh, int colIdx, int row) {
		if (!checkActivateHyperLink(cdh, colIdx, row)) {
			return;
		}
		try {
			List<HyperLinkData> retList = parse(cdh.getStringRecordRow(row)[colIdx]
					.getString());
			if (retList.size() == 0) {
				return;
			}

			Shell shell = Display.getCurrent().getActiveShell();
			LaunchDialog dialog = new LaunchDialog(shell, retList);
			dialog.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Integer[] getHyperLinkIndex() {
		return linkSet.toArray(new Integer[linkSet.size()]);
	}

	public boolean isHyperLinkColumn(int colIdx) {
		return linkSet.contains(colIdx);
	}

	static enum HyperLinkType {
		href, file, other
	}

	static class HyperLinkData {
		HyperLinkType type;
		File file, pfile;
		FileExtentionType extType;
		String launchCommand;
		String displayString;

		public HyperLinkData(HyperLinkType type, String source,
				String displayString) {
			this.type = type;
			this.launchCommand = source;
			this.displayString = displayString;
			try {
				switch (this.type) {
				case file:
					this.file = new File(source);
					this.pfile = this.file.getParentFile();
					this.extType = FileExtentionType.parseByFile(this.file);
					break;
				case href:
					this.extType = FileExtentionType.parseByPath(launchCommand);
					break;
				}
			} catch (Exception e) {
				log.warn(StringUtil.EMPTY_STRING, e);
			}
		}

		public boolean isImage() {
			if (extType == null) {
				return false;
			}
			return extType.isImage();
		}

		@Override
		public String toString() {
			return displayString;
		}
	}

	class LaunchDialog extends Dialog implements SelectionListener {
		List<HyperLinkData> linkList;

		protected LaunchDialog(Shell parentShell, List<HyperLinkData> linkList) {
			super(parentShell);
			this.linkList = linkList;
		}

		@Override
		protected void buttonPressed(int buttonId) {
			if (IDialogConstants.CLOSE_ID == buttonId) {
				setReturnCode(CANCEL);
				close();
			} else {
				super.buttonPressed(buttonId);
			}
		}

		@Override
		protected void createButtonsForButtonBar(Composite parent) {
			createButton(parent, IDialogConstants.CLOSE_ID,
					IDialogConstants.CLOSE_LABEL, false);
		}

		@Override
		protected boolean isResizable() {
			return true;
		}

		private Label createLabel(Composite parent, String text, Color bgColor) {
			Label lb = new Label(parent, SWT.NONE);
			lb.setText(text);
			lb.setBackground(bgColor);
			GridData gdata = new GridData(GridData.VERTICAL_ALIGN_CENTER
					| GridData.GRAB_HORIZONTAL | GridData.FILL_BOTH
					| GridData.GRAB_VERTICAL);
			lb.setLayoutData(gdata);
			return lb;
		}

		@Override
		protected Control createDialogArea(Composite parent) {
			getShell().setText("Launch Dialog");
			Composite c = (Composite) super.createDialogArea(parent);
			Composite main = new Composite(c, SWT.NONE);
			main.setBackground(ColorUtil.getColor(ColorNameEnum.ASAGI));
			GridLayout gl = new GridLayout(4, false);
			gl.horizontalSpacing = 2;
			gl.verticalSpacing = 2;
			gl.marginBottom = 2;
			gl.marginTop = 2;
			gl.marginWidth = 2;
			gl.marginHeight = 1;
			gl.marginLeft = 2;
			gl.marginRight = 2;

			main.setLayout(gl);
			String[] headers = new String[] { "NO", "NAME", "PATH", "SIZE" };
			Label lb = null;
			for (String header : headers) {
				lb = createLabel(main, header,
						ColorUtil.getColor(ColorNameEnum.SIMA));
			}

			for (int i = 0; i < linkList.size(); i++) {
				lb = createLabel(main, String.format("%02d", (i + 1)),
						ColorUtil.getColor(ColorNameEnum.SHIRAFUJI));
				HyperLinkData data = linkList.get(i);

				if (HyperLinkType.file.equals(data.type)) {
					Link nameLink = new Link(main, SWT.NONE);
					nameLink.setText("<a>" + data.file.getName() + "</a>");
					nameLink.setData(data.file.getAbsolutePath());
					nameLink.addSelectionListener(this);
					nameLink.setBackground(ColorUtil
							.getColor(ColorNameEnum.SHIRAFUJI));
					GridData gdata = new GridData(
							GridData.VERTICAL_ALIGN_CENTER
									| GridData.GRAB_HORIZONTAL
									| GridData.FILL_BOTH
									| GridData.GRAB_VERTICAL);
					nameLink.setLayoutData(gdata);
				} else {
					lb = createLabel(main, data.displayString,
							ColorUtil.getColor(ColorNameEnum.SHIRAFUJI));
				}
				if (StringUtil.isEmpty(data.launchCommand)) {
					lb = createLabel(main, "-",
							ColorUtil.getColor(ColorNameEnum.SHIRAFUJI));
				} else {
					Link pathLink = new Link(main, SWT.NONE);
					if (HyperLinkType.file.equals(data.type)) {
						pathLink.setText(String.format(
								"~/blob/%s  <a>(open folder)</a>",
								data.pfile.getName()));
						pathLink.setData(data.pfile.getAbsolutePath());
					} else {
						pathLink.setText("<a>" + data.launchCommand + "</a>");
						pathLink.setData(data.launchCommand);
					}
					pathLink.addSelectionListener(this);
					pathLink.setBackground(ColorUtil
							.getColor(ColorNameEnum.SHIRAFUJI));
					GridData gdata = new GridData(
							GridData.VERTICAL_ALIGN_CENTER
									| GridData.GRAB_HORIZONTAL
									| GridData.FILL_BOTH
									| GridData.GRAB_VERTICAL);
					pathLink.setLayoutData(gdata);
				}
				if (HyperLinkType.file.equals(data.type)) {
					lb = createLabel(main, FileUtil.getLengthString(data.file),
							ColorUtil.getColor(ColorNameEnum.SHIRAFUJI));
				} else {
					lb = createLabel(main, "-",
							ColorUtil.getColor(ColorNameEnum.SHIRAFUJI));
				}
			}

			main.setLayoutData(new GridData(GridData.FILL_BOTH));
			return c;
		}

		@Override
		public void widgetDefaultSelected(SelectionEvent ev) {
		}

		@Override
		public void widgetSelected(SelectionEvent ev) {
			try {
				String cmd = (String) ((Link) ev.getSource()).getData();
				Program.launch(cmd);
			} catch (Exception e) {
				log.warn(StringUtil.EMPTY_STRING, e);
			}
		}
	}
}
