/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.markers;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.swt.widgets.Display;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.filesystems.impl.FolderImpl;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.model.util.PositionHolder;

public class ResourceMarkers {
	public static String TEXT_PROBLEM = "org.jboss.tools.common.model.textproblemmarker"; //$NON-NLS-1$
	public static String CONSTRAINT_PROBLEM = "org.jboss.tools.jst.web.constraintsmarker"; //$NON-NLS-1$
	public static String JST_WEB_PROBLEM = "org.jboss.tools.jst.web.strutsmarker"; //$NON-NLS-1$
	public static String OLD_CONSTRAINT_PROBLEM = "org.jboss.tools.jst.web.ui.constraintsmarker"; //$NON-NLS-1$
	public static String OLD_JST_WEB_PROBLEM = "org.jboss.tools.jst.web.ui.strutsmarker"; //$NON-NLS-1$
	private XModelObject object;
	
	String type = null;
	String oldType = null;
	
	public ResourceMarkers(String type) {
		this.type = type;
	}
	
	public ResourceMarkers(String type, String oldType) {
		this.type = type;
		this.oldType = oldType;
	}
	
	public void setModelObject(XModelObject object) {
		this.object = object;
	}
	
	public void update() {
		if(object == null || !object.isActive()) return;		
		update0();
	}
	
	private void update0() {
		Set<XMarker> dms = null;
		Set<XMarker> added = null;
		IResource r = EclipseResourceUtil.getResource(object);
		if(r == null || !r.exists()) return;

		if(object.getParent() instanceof FolderImpl) {
			if( ((FolderImpl)object.getParent()).isOverlapped() ) {
				return;
			}
		}

		dms = getOwnedMarkers(r);
		String[] errorList = getErrors();
		for (int i = 0; i < errorList.length; i++) {
			String error = errorList[i];
			if(error == null || error.length() == 0) continue;
			String message = getTrueMessage(error);
			String path = getObjectPathForError(i);
			int location = getLocation(i);
			if(location < 0) location = getLocation(error);
			String attr = getObjectAttributeForError(i);
			XMarker marker = findMarker(path, message, attr, dms);
			if(marker != null) {
				dms.remove(marker);
				continue;
			}
			marker = new XMarker();
			if(added == null) {
				added = new HashSet<XMarker>();
			}
			added.add(marker);
			marker.setType(type);
			marker.setMessage(message);
			marker.setPath(path);
			if(attr != null && attr.length() > 0) {
				marker.setAttribute(attr);
			}
		}
		if(r instanceof IFile) {
			IFile file = (IFile)r;
			if(dms != null) {
				XMarkerManager.getInstance().clearXMarkers(file, dms);
			}
			if(added != null) {
				XMarkerManager.getInstance().addXMarkers(file, added);
			}
			if((dms != null && !dms.isEmpty()) || (added != null && !added.isEmpty())) {
				XMarkerManager.getInstance().forceReload(file);
			}
		}
	}

	public static void updateLocation(IMarker marker, int location, int start, int end) throws CoreException {
		if(location >= 0 && marker.getAttribute(IMarker.LINE_NUMBER, -1) != location) {
			marker.setAttribute(IMarker.LINE_NUMBER, location);
		}
		if(start >= 0 && marker.getAttribute(IMarker.CHAR_START, -1) != start) {
			marker.setAttribute(IMarker.CHAR_START, start);
		}
		if(end >= 0 && marker.getAttribute(IMarker.CHAR_END, -1) != end) {
			marker.setAttribute(IMarker.CHAR_END, end);
		}
	}
	
	private XMarker findMarker(String path, String message, String attr, Set<XMarker> dms) {
		if(dms == null) return null;
		for (XMarker m: dms) {
			if(!message.equals(m.getMessage())) continue;
			if(attr != null && !attr.equals(m.getAttribute())) continue;
			if(oldType != null && oldType.equals(m.getType())) continue;
			if(!path.equals(m.getPath())) {
				m.setPath(path);
			}
			return m;
		}
		return null;
	}
	
	protected String getObjectPathForError(int i) {
		return object == null ? null : object.getPath();
	}
	
	protected String getObjectAttributeForError(int i) {
		return null;
	}
	
	protected String getTrueMessage(String message) {
		StringBuffer sb = new StringBuffer();
		int c = 0;
		while(c < message.length()) {
			int i1 = message.indexOf('@', c);
			if(i1 < 0) break;
			int i2 = message.indexOf('@', i1 + 1);
			if(i2 < 0) break;
			int i3 = message.indexOf('@', i2 + 1);
			if(i3 < 0) break;
			if(c > 0) sb.append(message.substring(0, i1));
			sb.append(message.substring(i2 + 1, i3));
			c = i3 + 1;
		}
		sb.append(message.substring(c));
		return sb.toString();		
	}
	
	public void clear() {
		IResource r = EclipseResourceUtil.getResource(object);
		if(!(r instanceof IFile)) return;
		Set<XMarker> ms = getOwnedMarkers((IFile)r);
		if(ms != null) {
			synchronized (XMarkerManager.getInstance()) {
				Set<XMarker> ms1 = XMarkerManager.getInstance().getMarkers((IFile)r);
				if(ms1 != null) {
					for (XMarker m: ms) ms1.remove(m);
				}
			}
		}		
	}
	
    private Set<XMarker> getOwnedMarkers(IResource r) {
    	if(!(r instanceof IFile)) {
    		return null;
    	}
    	Set<XMarker> l = null;
        synchronized (XMarkerManager.getInstance()) {
        	Set<XMarker> ms = XMarkerManager.getInstance().getMarkers((IFile)r);
        	if(ms != null) for (XMarker m: ms) {
        		if(isOwnedMarker(m)) {
        			if(l == null) l = new HashSet<XMarker>();
        			l.add(m);
        		}
        	}
        }
        return l;
    }

    boolean isOwnedMarker(XMarker m) {
    	if(m == null) return false;
    	String _type = m.getType();
    	if(_type == null) return true;
    	if(_type.startsWith("org.jboss.tools.")) { //$NON-NLS-1$
    		return _type.equals(type) || (oldType != null && _type.equals(oldType));
    	}
    	return false;
    }

	protected String[] getErrors() {
		return new String[0];	
	}

	protected int getLocation(int i) {
		return -1;
	}
	
	protected int getLocation(String error) {
		return -1;
	}
	
	protected int getStart(int i) {
		return -1;
	}
	
	protected int getEnd(int i) {
		return -1;
	}
	

	public static void refreshProblemMarkersAsync(final XModelObject file) {
		if(!file.isActive()) return;		
		Display.getDefault().asyncExec(new Runnable(){
			public void run() {
				refreshProblemMarkers(file);
			}
		});		
	}

	public static void refreshProblemMarkers(XModelObject file) {
		IResource r = (IResource)file.getAdapter(IResource.class);
		if(r == null) return;
		try {
			IMarker[] ms = r.findMarkers(ResourceMarkers.JST_WEB_PROBLEM, true, 1);
			if(ms != null) for (int i = 0; i < ms.length; i++) {
				String path = ms[i].getAttribute("path", null); //$NON-NLS-1$
				if(path == null) continue;
				XModelObject o = file.getModel().getByPath(path);
				if(o == null) {
					ms[i].delete();
					continue;
				}
				String attr = ms[i].getAttribute("attribute", null); //$NON-NLS-1$
				PositionHolder h = PositionHolder.getPosition(o, attr);
				h.update();
				updateLocation(ms[i], h.getLine(), h.getStart(), h.getEnd());
			}
		} catch (CoreException e) {
			//ignore
		}
	}

}
