/*******************************************************************************
 * Copyright (c) 2014 Bruno Medeiros and other Contributors.
 * All rights reserved. This program and the accompanying materials
 * are 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:
 *     Bruno Medeiros - initial API and implementation
 *******************************************************************************/
package melnorme.lang.ide.debug.ui.viewmodel;

import java.util.Map;

import melnorme.lang.ide.core.LangCore;
import melnorme.utilbox.core.CommonException;
import melnorme.utilbox.misc.StringUtil;

import org.eclipse.cdt.dsf.debug.ui.viewmodel.ErrorLabelText;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.MessagesForDebugVM;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueLabelText;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.SyncVariableDataAccess;
import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbVariableVMNode;
import org.eclipse.cdt.dsf.mi.service.MIExpressions;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;

//@SuppressWarnings("restriction")
public class GdbVariableVMNode_Override extends GdbVariableVMNode {
	
	GdbVariableVMNode_Override(AbstractDMVMProvider provider, DsfSession session,
			SyncVariableDataAccess syncVariableDataAccess) {
		super(provider, session, syncVariableDataAccess);
	}
	
	@Override
	protected IElementLabelProvider createLabelProvider() {
		// Note: beware of changes in the super method:
		IElementLabelProvider labelProvider = super.createLabelProvider();
		
		if(labelProvider instanceof PropertiesBasedLabelProvider) { // Shoud be true
			PropertiesBasedLabelProvider provider = (PropertiesBasedLabelProvider) labelProvider;
			customizeLabelProvider(provider);
		}
		return labelProvider;
	}
	
	/**
	 * This method modifies CDT's default label provider in two ways:
	 * See {@link FormattedValueLabelText_Override} and {@link ErrorLabelText_Extension}
	 */
	protected static void customizeLabelProvider(PropertiesBasedLabelProvider provider) {
		LabelColumnInfo columnInfo = provider.getColumnInfo(IDebugVMConstants.COLUMN_ID__VALUE);
		LabelAttribute[] labelAttributes = columnInfo.getLabelAttributes();
		
		
		int preferredIndex = -1;
		
		// Find the best FormattedValueLabelText to replace (the one that would print "details" format)
		// this should the one at index 1, according to super.createLabelProvider() in
		// org.eclipse.cdt.dsf.ui_2.4.0.201402142303
		for (int i = 0; i < labelAttributes.length; i++) {
			LabelAttribute labelAttribute = labelAttributes[i];
			if(labelAttribute instanceof FormattedValueLabelText) {
				preferredIndex = i;
			}
			if(labelAttribute instanceof ErrorLabelText) {
				labelAttributes[i] = new ErrorLabelText_Extension();
			}
		}
		if(preferredIndex != -1) {
			labelAttributes[preferredIndex] = new FormattedValueLabelText_Override();
		} else {
			LangCore.logInternalError(new CommonException(
				" Failed to replace CDT's FormattedValueLabelText for the Variables view"));
		}
		
		provider.setColumnInfo(IDebugVMConstants.COLUMN_ID__VALUE, new LabelColumnInfo(labelAttributes));
	}
	
	/**
	 * Override parent behaviour: don't add "(Details)" suffix
	 */
	public static class FormattedValueLabelText_Override extends FormattedValueLabelText {
		
	    protected String PROP_ACTIVE_FORMAT = IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT;
	    protected String PROP_ACTIVE_FORMAT_VALUE = IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE;

		public FormattedValueLabelText_Override() {
		}
		
		@Override
		protected Object getPropertyValue(String propertyName, IStatus status, Map<String, Object> properties) {
	        if ( PROP_ACTIVE_FORMAT_VALUE.equals(propertyName) ) {
	            Object activeFormat = properties.get(PROP_ACTIVE_FORMAT);
	            if(activeFormat.equals(MIExpressions.DETAILS_FORMAT)){
	            	return properties.get(propertyName);
	            }
	        }
	        return super.getPropertyValue(propertyName, status, properties);
		}
		
	}
	
	/**
	 * Trim certain error messages from the backend.
	 * TODO: review this with future versions of CDT
	 */
	public static final class ErrorLabelText_Extension extends ErrorLabelText {
		
		protected static final String[] MESSAGES_TO_TRIM = {
			"Cannot access memory at address",
			"There is no member named",
		};
		
		
		@Override
		protected Object getPropertyValue(String propertyName, IStatus status,
				Map<String, Object> properties) {
	        if (PROP_ERROR_MESSAGE.equals(propertyName)) {
	            String message = status.getMessage();
				if (status.getChildren().length < 2) {
					if(message.contains("Error message from debugger back end:")) {
						for (String messageToTrim : MESSAGES_TO_TRIM) {
							if(message.contains(messageToTrim)) {
								message = StringUtil.substringFromMatch(messageToTrim, message);
								break;
							}
						}
					}
					
	                return replaceNewlines(message);
	            } else {
	                StringBuffer buf = new StringBuffer(message);
	                for  (IStatus childStatus : status.getChildren()) {
	                    buf.append(MessagesForDebugVM.ErrorLabelText_Error_message__text_page_break_delimiter);
	                    buf.append( replaceNewlines(childStatus.getMessage()) );
	                }
	                return buf.toString();
	            }
	        }
			return super.getPropertyValue(propertyName, status, properties);
		}
		
	    protected String replaceNewlines(String message) {
	        return message.replaceAll("\n",
	        	MessagesForDebugVM.ErrorLabelText_Error_message__text_page_break_delimiter); //$NON-NLS-1$
	    }
	    
	}

}