/*******************************************************************************
 * Copyright (c) 2008, 2019 Angelo Zerr and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
 *******************************************************************************/
package org.akrogen.tkui.css.swt.examples.csseditors;

import java.io.File;
import java.util.ArrayList;

import org.akrogen.tkui.css.core.css2.CSS2FontPropertiesHelpers;
import org.akrogen.tkui.css.core.dom.IElementProvider;
import org.akrogen.tkui.css.core.dom.properties.css2.CSS2FontProperties;
import org.akrogen.tkui.css.core.engine.CSSEngine;
import org.akrogen.tkui.css.core.examples.csseditors.AbstractCSSEditor;
import org.akrogen.tkui.css.core.serializers.CSSSerializerConfiguration;
import org.akrogen.tkui.css.swt.dom.SWTElementProvider;
import org.akrogen.tkui.css.swt.dom.html.SWTHTMLElementProvider;
import org.akrogen.tkui.css.swt.engine.CSSSWTEngineImpl;
import org.akrogen.tkui.css.swt.serializers.CSSSWTSerializerConfiguration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.w3c.dom.css.CSSValue;

/**
 * Abstract CSS SWT Editor.
 * 
 * @version 1.0.0
 * @author <a href="mailto:angelo.zerr@gmail.com">Angelo ZERR</a>
 * 
 */
public abstract class AbstractCSSSWTEditor extends AbstractCSSEditor {

	protected static final String[] CSS_FILE_EXTENSION = { "*.css" };

	private Display display;

	/**
	 * Textarea wich contains content of CSS style sheet.
	 */
	private StyledText textArea;

	private Button cacheResourcesCheckbox;

	/**
	 * HTML checkbox to set if CSS must be typed with HTML syntaxe or SWT
	 * syntax.
	 */
	private Button htmlCheckbox;

	/**
	 * Label wich display time elapsed when CSS Engine apply styles.
	 */
	private Label statusLabel;

	/**
	 * Apply style when text area change
	 */
	private Button applyStyleWhenTextAreaChangeCheckbox;

	/**
	 * Launch Apply style
	 */
	private Button applyStyleToShellCheckbox;

	private Shell shell;

	private Composite leftPanel;

	private List cssFilesWidget;

	private java.util.List cssFiles = new ArrayList();

	private Text selectedCSSPropertyNameText = null;

	private Text selectedCSSPropertyValueText = null;

	protected int currentLine = -1;

	protected AbstractCSSSWTEditor(String nativeWidgetDir,
			String[] styleFileExtension) {
		super(nativeWidgetDir);
	}

	protected AbstractCSSSWTEditor() {
		super("swt");
	}

	public void display() {

		display = new Display();
		shell = new Shell(display, SWT.SHELL_TRIM);
		GridLayout layout = new GridLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.verticalSpacing = 3;
		shell.setLayout(layout);

		// create Menus
		createMenus(shell);

		/*
		 * Create a sash form.
		 */
		SashForm form = new SashForm(shell, SWT.NONE);
		form.setLayoutData(new GridData(GridData.FILL_BOTH));

		/*
		 * Create left panel with SWT Contents.
		 */
		createLeftPanel(form);

		/*
		 * Create right panel with TextArea which contains CSS to load.
		 */
		createRightPanel(form);
		form.setWeights(new int[] { 30, 80 });

		/*
		 * Now we open the shell.
		 */
		shell.setSize(new Point(800, 600));
		shell.open();
		shell.setText("CSS Editors");
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();
	}

	/*--------------------  Menus  --------------------**/

	/**
	 * Create Menus File, Options
	 * 
	 * @param shell
	 */
	protected void createMenus(Shell shell) {
		Menu menu = new Menu(shell, SWT.BAR);
		shell.setMenuBar(menu);
		// Create File item Menu
		createMenuItemFile(menu, shell);
		// Create Options item Menu
		// createMenuItemOptions(menu);
	}

	/**
	 * Create Menu item File with sub menu New and Open
	 * 
	 * @param menu
	 * @param shell
	 */
	protected void createMenuItemFile(Menu menu, final Shell shell) {
		MenuItem menuFileHeader = new MenuItem(menu, SWT.CASCADE);
		menuFileHeader.setText("&File");
		// File menu
		Menu menuFile = new Menu(shell, SWT.DROP_DOWN);
		menuFileHeader.setMenu(menuFile);
		// New item menu
		MenuItem itemNew = new MenuItem(menuFile, SWT.PUSH);
		itemNew.setText("&New...");
		itemNew.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				fillTextareaWithDefaultStyleSheetContent();
			}
		});
		// Open item menu
		MenuItem itemOpen = new MenuItem(menuFile, SWT.PUSH);
		itemOpen.setText("&Open...");
		itemOpen.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				FileDialog dialog = new FileDialog(shell, SWT.NULL);
				dialog.setFilterExtensions(CSS_FILE_EXTENSION);
				dialog.setText("Select CSS style file");
				dialog.setFilterPath(getBaseStyleDir().getAbsolutePath());
				String path = dialog.open();
				if (path != null) {
					File file = new File(path);
					fillTextareaWithStyleSheetContent(file);
				}
			}
		});

	}

	/**
	 * Create Menu item Options
	 * 
	 * @param menu
	 */
	protected void createMenuItemOptions(Menu menu) {
		MenuItem menuOptionsHeader = new MenuItem(menu, SWT.CASCADE);
		menuOptionsHeader.setText("&Options");
		// Options menu
		Menu menuOptions = new Menu(shell, SWT.DROP_DOWN);
		menuOptionsHeader.setMenu(menuOptions);
		// New item menu
		MenuItem itemNew = new MenuItem(menuOptions, SWT.CHECK);
		itemNew.setText("&Cache Resource...");

	}

	/*--------------------  Panel  --------------------**/

	protected void createLeftPanel(Composite parent) {
		leftPanel = new Composite(parent, SWT.NONE);
		leftPanel.setLayout(new FillLayout());
		createContent(leftPanel);
	}

	protected void createRightPanel(Composite parent) {

		SashForm form = new SashForm(parent, SWT.NONE);
		form.setLayoutData(new GridData(GridData.FILL_BOTH));

		GridLayout layout = new GridLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.verticalSpacing = 3;
		Composite composite = new Composite(form, SWT.NONE);
		composite.setLayout(layout);

		statusLabel = new Label(composite, SWT.NONE);
		statusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		// Create TextArea which contains CSS content.
		textArea = new StyledText(composite, SWT.MULTI | SWT.V_SCROLL
				| SWT.BORDER);
		textArea.setLayoutData(new GridData(GridData.FILL_BOTH));
		textArea.addModifyListener(e -> {
			if (applyStyleWhenTextAreaChangeCheckbox.getSelection())
				applyStyles();
		});

		textArea.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				displaySelectedCSSProperty();
			}
		});
		textArea.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				displaySelectedCSSProperty();
			}
		});

		layout = new GridLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.verticalSpacing = 3;
		composite = new Composite(form, SWT.NONE);
		composite.setLayout(layout);

		Group group = new Group(composite, SWT.NONE);
		group.setText("Selected CSS Property");
		layout = new GridLayout(2, true);
		layout.verticalSpacing = 3;
		group.setLayout(layout);
		group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		Label label = new Label(group, SWT.NONE);
		label.setText("CSS Property name");
		selectedCSSPropertyNameText = new Text(group, SWT.BORDER
				| SWT.READ_ONLY);
		selectedCSSPropertyNameText.setLayoutData(new GridData(
				GridData.FILL_HORIZONTAL));

		label = new Label(group, SWT.NONE);
		label.setText("CSS Property value");
		selectedCSSPropertyValueText = new Text(group, SWT.BORDER);
		selectedCSSPropertyValueText.setLayoutData(new GridData(
				GridData.FILL_HORIZONTAL));
		selectedCSSPropertyValueText.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {

				String property = selectedCSSPropertyNameText.getText();
				property += ":";
				property += selectedCSSPropertyValueText.getText();
				updateSelectedCSSPropertyValue(property);
			}
		});

		final Button b = new Button(group, SWT.PUSH | SWT.BORDER);
		b.setText("Change Color");
		b.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				ColorDialog colorDialog = new ColorDialog(shell);
				colorDialog.setText("ColorDialog Demo");
				String cssValue = selectedCSSPropertyValueText.getText();
				if (cssValue != null && cssValue.length() > 0) {
					try {
						CSSValue value = engine.parsePropertyValue(cssValue);
						RGB rgb = (RGB) engine.convert(value, RGB.class, null);
						if (rgb != null)
							colorDialog.setRGB(rgb);
					} catch (Exception ex) {
						handleExceptions(ex);
					}
					// engine.convert(, RGB.class,);
				}
				RGB newColor = colorDialog.open();
				if (newColor != null) {
					try {
						cssValue = engine.convert(newColor, RGB.class,
								selectedCSSPropertyNameText.getText());
						if (cssValue == null)
							return;
						selectedCSSPropertyValueText.setText(cssValue);
						String property = selectedCSSPropertyNameText.getText();
						property += ":";
						property += selectedCSSPropertyValueText.getText();
						updateSelectedCSSPropertyValue(property);
					} catch (Exception ex) {
						handleExceptions(ex);
					}
				}
			}
		});

		final Button b2 = new Button(group, SWT.PUSH | SWT.BORDER);
		b2.setText("Change Font");
		b2.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				FontDialog fontDialog = new FontDialog(shell);
				fontDialog.setText("Choose the font");
				String value = selectedCSSPropertyValueText.getText();
				try {
					String propertyName = selectedCSSPropertyNameText.getText();
					CSSValue cssValue = engine.parsePropertyValue(value);
					CSS2FontProperties fontProperties = CSS2FontPropertiesHelpers
							.createCSS2FontProperties(cssValue, propertyName);
					FontData fontData = (FontData) engine.convert(fontProperties,
							FontData.class, display);
					if (fontData != null) {
						FontData[] fontDatas = { fontData };
						fontDialog.setFontList(fontDatas);
					}
				} catch (Exception ex) {
					handleExceptions(ex);
				}
				FontData newFontData = fontDialog.open();
				if (newFontData != null) {
					try {
						String cssValue = engine.convert(newFontData,
								FontData.class, selectedCSSPropertyNameText
										.getText());
						if (cssValue == null)
							return;
						selectedCSSPropertyValueText.setText(cssValue);
						String property = selectedCSSPropertyNameText.getText();
						property += ":";
						property += selectedCSSPropertyValueText.getText();
						updateSelectedCSSPropertyValue(property);
					} catch (Exception ex) {
						handleExceptions(ex);
					}
				}
			}
		});

		group = new Group(composite, SWT.NONE);
		group.setText("CSS Engine options");
		layout = new GridLayout();
		layout.verticalSpacing = 3;
		group.setLayout(layout);
		group.setLayoutData(new GridData(GridData.FILL_BOTH));

		cacheResourcesCheckbox = new Button(group, SWT.CHECK);
		cacheResourcesCheckbox.setText("Cache Color, Font and Cursor");
		cacheResourcesCheckbox.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if (!cacheResourcesCheckbox.getSelection()) {
					engine.getResourcesRegistry().dispose();
				}
			}
		});
		cacheResourcesCheckbox.setSelection(true);

		applyStyleWhenTextAreaChangeCheckbox = new Button(group, SWT.CHECK);
		applyStyleWhenTextAreaChangeCheckbox
				.setText("Apply style when textarea change");
		applyStyleWhenTextAreaChangeCheckbox.setSelection(true);

		htmlCheckbox = new Button(group, SWT.CHECK);
		htmlCheckbox.setText("is HTML Selector? (otherwise it's SWT Selector)");
		htmlCheckbox.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				populateCSSFiles();
				applyStylesFromSelectedFile();
			}
		});

		// Create CSS files list
		cssFilesWidget = new List(group, SWT.NONE);
		cssFilesWidget.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		cssFilesWidget.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				applyStylesFromSelectedFile();
			}
		});
		populateCSSFiles();

		// Create Apply Style Button.
		applyStyleToShellCheckbox = new Button(group, SWT.CHECK);
		applyStyleToShellCheckbox.setText("Apply sytle to fully Shell?");
		applyStyleToShellCheckbox.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if (applyStyleToShellCheckbox.getSelection()) {
					applyStyles();
				} else {
					// Apply styles with NONE style to reset
					// styles applied into TextArea...
					engine = getCSSEngine();
					engine.applyStyles(shell, true);
					// Apply styles
					applyStyles();
				}

			}
		});

		Button applyStyleButton = new Button(group, SWT.BORDER);
		applyStyleButton.setText("Apply style");
		applyStyleButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				applyStyles();
			}
		});

		form.setWeights(new int[] { 120, 90 });

		// Select the first CSS style file
		applyStylesFromSelectedFile();
	}

	public abstract void createContent(Composite parent);

	protected void displaySelectedCSSProperty() {
		selectedCSSPropertyNameText.setText("");
		selectedCSSPropertyValueText.setText("");
		int start = -1;
		int end = -1;

//		if (currentLine != -1)
//			textArea.setLineBackground(currentLine, 1, null);

		currentLine = textArea.getLineAtOffset(textArea.getCaretOffset());
		if (currentLine + 1 >= textArea.getLineCount())
			return;
		start = textArea.getOffsetAtLine(currentLine);
		end = textArea.getOffsetAtLine(currentLine + 1) - 2;
		if (start <= end) {
			String lineText = textArea.getText(start, end);
			lineText = lineText.trim();
			int index = lineText.indexOf(':');
			if (index > 0) {
				if (lineText.indexOf('{') != -1)
					return;
				String property = lineText.substring(0, index);
				String value = lineText.substring(index + 1, lineText.length());
				value = value.replaceAll(";", "");
				// Remove comment
				int commentIndex = value.indexOf("/*");
				if (commentIndex > 0)
					value = value.substring(0, commentIndex);
				selectedCSSPropertyNameText.setText(property);
				selectedCSSPropertyValueText.setText(value);

//				textArea.setLineBackground(currentLine, 1, display
//						.getSystemColor(SWT.COLOR_MAGENTA));
			}
		}
	}

	protected void updateSelectedCSSPropertyValue(String text) {
		if (currentLine != -1 && currentLine + 1 >= textArea.getLineCount())
			return;
		int start = textArea.getOffsetAtLine(currentLine);
		int end = textArea.getOffsetAtLine(currentLine + 1) - 2;

		String startLineText = textArea.getText(0, start);
		String endLineText = textArea.getText(end, textArea.getCharCount() - 1);
		if (!text.endsWith(";") && !endLineText.startsWith(";"))
			text += ";";
		if (endLineText.startsWith(":"))
			endLineText.substring(1, endLineText.length());
		String newContent = startLineText + text + endLineText;
		textArea.setText(newContent);
	}

	/*-------------------- AbstractCSSEditor methods implementation  --------------------**/

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#createCSSEngine()
	 */
	protected CSSEngine createCSSEngine() {
		return new CSSSWTEngineImpl(shell.getDisplay());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getHTMLElementProvider()
	 */
	protected IElementProvider getHTMLElementProvider() {
		return SWTHTMLElementProvider.INSTANCE;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getNativeWidgetElementProvider()
	 */
	protected IElementProvider getNativeWidgetElementProvider() {
		return SWTElementProvider.INSTANCE;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#isHTMLSelector()
	 */
	protected boolean isHTMLSelector() {
		return htmlCheckbox.getSelection();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getStyleSheetContent()
	 */
	protected String getStyleSheetContent() {
		return textArea.getText();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#setStyleSheetContent(java.lang.String)
	 */
	protected void setStyleSheetContent(String content) {
		textArea.setText(content);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#setCSSEngineStatuts(java.lang.String)
	 */
	protected void setCSSEngineStatuts(String status) {
		statusLabel.setText(status);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#mustApplyStylesToWindow()
	 */
	protected boolean mustApplyStylesToWindow() {
		return applyStyleToShellCheckbox.getSelection();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getLeftPanelNativeWidget()
	 */
	protected Object getLeftPanelNativeWidget() {
		return leftPanel;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getWindowNativeWidget()
	 */
	protected Object getWindowNativeWidget() {
		return shell;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getCSSNativeWidgetSerializerConfiguration()
	 */
	protected CSSSerializerConfiguration getCSSNativeWidgetSerializerConfiguration() {
		return CSSSWTSerializerConfiguration.INSTANCE;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getCSSFileWidgetItemCount()
	 */
	protected int getCSSFilesWidgetItemCount() {
		return cssFilesWidget.getItemCount();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#getCSSFileWidgetSelectionIndex()
	 */
	protected int getCSSFilesWidgetSelectionIndex() {
		return cssFilesWidget.getSelectionIndex();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#selectCSSFileWidget(int)
	 */
	protected void selectCSSFilesWidget(int index) {
		cssFilesWidget.select(index);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#addCSSFilesWidget(java.lang.String)
	 */
	protected void addItemCSSFilesWidget(String item) {
		cssFilesWidget.add(item);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.akrogen.tkui.core.css.examples.csseditors.AbstractCSSEditor#removeAllCSSFilesWidget()
	 */
	protected void removeAllCSSFilesWidget() {
		cssFilesWidget.removeAll();
	}
}
