/*******************************************************************************
 * 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.meta.impl.documentation;

import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;

import org.jboss.tools.common.model.XModelObjectConstants;
import org.jboss.tools.common.model.XModelObject;

public class MetaValidator {
    private IconsValidator iv = new IconsValidator();
    private EntitiesValidator ev = new EntitiesValidator();
//    private MappingsValidator mv = new MappingsValidator();

    public MetaValidator() {}

    public void validate(XModelObject metaroot) {
        if(!"MetaRoot".equals(metaroot.getModelEntity().getName())) //$NON-NLS-1$
          throw new IllegalArgumentException("Meta validator must be called with MetaRoot."); //$NON-NLS-1$
        iv.validate(metaroot.getChildren("MetaIcons")[0]); //$NON-NLS-1$
        ev.validate(metaroot.getChildren("MetaEntities")[0]); //$NON-NLS-1$
////        mv.validate(metaroot.getChildren("MetaMappings")[0]);
    }

    public static final void message(String message) {
		///XStudioPlugin.log(message);
    }

    public static final String id(XModelObject object) {
        return object.getAttributeValue(XModelObjectConstants.ATTR_ELEMENT_TYPE) + " " + //$NON-NLS-1$
               object.getModelEntity().getRenderer().getTitle(object);
    }

    public static boolean isBasic(XModelObject object) {
        String e = object.getModelEntity().getName();
        return ("MetaEntity".equals(e) || "MetaMapping".equals(e)); //$NON-NLS-1$ //$NON-NLS-2$
    }

    public static String entityId(XModelObject object) {
        while(object != null) {
            if(isBasic(object)) return id(object);
            object = object.getParent();
        }
        return "!!!!!!!!!!!!"; //$NON-NLS-1$
    }

    public static final String longid(XModelObject object) {
        return id(object) + ((isBasic(object)) ? "" : MessageFormat.format(" of {0}", entityId(object))); //$NON-NLS-1$
    }

    public static final void checkClass(XModelObject object, String attribute, boolean mandatory, String mapping) {
        checkClass(object, attribute, "", mandatory, mapping); //$NON-NLS-1$
    }

    public static final void checkClass(XModelObject object, String attribute, String prefix, boolean mandatory, String mapping) {
        String pref = MessageFormat.format("Error in attribute ''{0}'' in {1}: ", attribute,
				longid(object));
        String classname = object.getAttributeValue(attribute);
        int i = classname.indexOf('%');
        if(classname.length() == 0) {
            if(!mandatory) return;
            message(pref + " class name cannot be empty.");
        } else if(i == 0) {
            if(mapping == null) {
                message(pref + (MessageFormat.format(" alias {0} is not allowed.", classname)));
            } else if(classname.charAt(classname.length() - 1) != '%') {
                message(pref + (MessageFormat.format(" incorrect alias {0}", classname)));
            } else {
                MappingsValidator.classmappings.add(mapping);
                String alias = classname.substring(1, classname.length() - 1);
                XModelObject v = object.getModel().getByPath("MetaModel/Mappings/" + mapping); //$NON-NLS-1$
                if(v == null) message(MessageFormat.format("Mapping {0} is not found.", mapping));
                if(v.getChildByPath(alias) != null) return;
                message(pref + (MessageFormat.format(" incorrect alias {0}", classname)));
            }
        } else {
            try {
                Class.forName(prefix + classname).newInstance();
            } catch (ClassNotFoundException e) {
            	  message(pref + (MessageFormat.format(" class ''{0}'' not found.", classname)));              
            } catch (InstantiationException e) {
            	  message(pref + (MessageFormat.format(" class ''{0}'' not found.", classname)));
			} catch (IllegalAccessException e) {
				  message(pref + (MessageFormat.format(" class ''{0}'' not found.", classname)));
			}
        }
    }
}

class IconsValidator {
    public static final Vector<String> icons = new Vector<String>();
    private int iconsrootlength = 0;

    public void validate(XModelObject object) {
        iconsrootlength = object.getPath().length() + 1;
        validate2(object);
    }

    private void validate2(XModelObject object) {
        XModelObject[] gs = object.getChildren("MetaIconGroup"); //$NON-NLS-1$
        for (int i = 0; i < gs.length; i++) validate2(gs[i]);
        XModelObject[] is = object.getChildren("MetaIcon"); //$NON-NLS-1$
        for (int i = 0; i < is.length; i++) {
            String s = is[i].getPath().substring(iconsrootlength).replace('/', '.');
            String p = is[i].getAttributeValue("path"); //$NON-NLS-1$
            icons.addElement(s);
            if(int.class.getResource(p) == null)
              MetaValidator.message(MessageFormat.format(
					"Error in icon {0}: Resource {1} is not found.", s,
					p));
        }
    }

}

class EntitiesValidator {
    public static final Hashtable<String,Vector<String>> entities = new Hashtable<String,Vector<String>>();
    private ChildrenValidator cv = new ChildrenValidator();
    private AttributesValidator av = new AttributesValidator();
    private ActionsValidator tv = new ActionsValidator();

    public void validate(XModelObject object) {
        collect(object);
        XModelObject[] gs = object.getChildren("MetaEntityGroup"); //$NON-NLS-1$
        for (int i = 0; i < gs.length; i++) validate(gs[i]);
        XModelObject[] es = object.getChildren("MetaEntity"); //$NON-NLS-1$
        for (int i = 0; i < es.length; i++) {
            MetaValidator.checkClass(es[i], "implementation", true, null); //$NON-NLS-1$
            MetaValidator.checkClass(es[i], "loader", false, null); //$NON-NLS-1$
            MetaValidator.checkClass(es[i], "generator", false, null); //$NON-NLS-1$
            MetaValidator.checkClass(es[i], "editor", false, "ObjectEditor"); //$NON-NLS-1$ //$NON-NLS-2$
            MetaValidator.checkClass(es[i], "adopt manager", false, null); //$NON-NLS-1$
            cv.validate(es[i]);
            av.validate(es[i]);
            tv.validate(es[i]);
        }
    }

    private void collect(XModelObject object) {
        XModelObject[] gs = object.getChildren("MetaEntityGroup"); //$NON-NLS-1$
        for (int i = 0; i < gs.length; i++) collect(gs[i]);
        XModelObject[] es = object.getChildren("MetaEntity"); //$NON-NLS-1$
        for (int i = 0; i < es.length; i++) {
            XModelObject[] as = es[i].getChildByPath("Attributes").getChildren(); //$NON-NLS-1$
            Vector<String> v = new Vector<String>();
            for (int j = 0; j < as.length; j++) v.addElement(as[j].getAttributeValue(XModelObjectConstants.ATTR_NAME));
            entities.put(es[i].getAttributeValue(XModelObjectConstants.ATTR_NAME), v);
        }
    }
}

class ChildrenValidator {
    public void validate(XModelObject object) {
        XModelObject[] cs = object.getChildByPath("Children").getChildren(); //$NON-NLS-1$
        for (int i = 0; i < cs.length; i++) {
            String c = cs[i].getAttributeValue(XModelObjectConstants.ATTR_NAME);
            if(EntitiesValidator.entities.get(c) == null) {
                MetaValidator.message(MessageFormat
						.format(
								"Error in {0}: child {1} not found in the entity list.",
								MetaValidator.id(object), c));
            }
        }
    }
}

class AttributesValidator {

    public void validate(XModelObject object) {
        XModelObject[] as = object.getChildByPath("Attributes").getChildren(); //$NON-NLS-1$
        for (int i = 0; i < as.length; i++) {
            String an = as[i].getAttributeValue(XModelObjectConstants.ATTR_NAME);
            MetaValidator.checkClass(as[i], "loader", "org.jboss.tools.common.meta.impl.adapters.XAdapter", false, null); //$NON-NLS-1$ //$NON-NLS-2$
            XModelObject ed = as[i].getChildren("MetaAttributeEditor")[0]; //$NON-NLS-1$
            String en = ed.getAttributeValue(XModelObjectConstants.ATTR_NAME);
            if("GUI".equals(en)) continue; //$NON-NLS-1$
            XModelObject mi = ed.getModel().getByPath("MetaModel/Mappings/AttributeEditor/" + en); //$NON-NLS-1$
            if(mi == null) {
                MetaValidator.message(MessageFormat
						.format(
								"Error in attribute ''{0}'' of {1}: incorrect editor ''{2}''.",
								an, MetaValidator.id(object), en));
            }
        }
    }
}

class ActionsValidator {
    private EntityDataValidator dv = new EntityDataValidator();

    public void validate(XModelObject object) {
        XModelObject[] ls = object.getChildren("MetaActionList"); //$NON-NLS-1$
        for (int i = 0; i < ls.length; i++) validate(ls[i]);
        XModelObject[] as = object.getChildren("MetaAction"); //$NON-NLS-1$
        for (int i = 0; i < as.length; i++) {
//            String n = as[i].getAttributeValue("display name");
            String in = as[i].getAttributeValue("icon"); //$NON-NLS-1$
            if(in.trim().length() == 0)
              MetaValidator.message(MessageFormat.format("Error in {0}: icon not specified", MetaValidator.longid(as[i])));
            if(in.length() > 0 && !IconsValidator.icons.contains(in))
              MetaValidator.message(MessageFormat.format("Error in {0}: icon ''{1}'' not found",
					MetaValidator.longid(as[i]), in));
            MetaValidator.checkClass(as[i], "handler", true, null); //$NON-NLS-1$
            String wm = (System.getProperty("testmodel") != null) ? "TestWizards" : "Wizards"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            MetaValidator.checkClass(as[i], "wizard", false, wm); //$NON-NLS-1$
            dv.validate(as[i]);
        }
    }
}

class EntityDataValidator {
    public void validate(XModelObject object) {
        XModelObject[] ds = object.getChildren("MetaEntityData"); //$NON-NLS-1$
        for (int i = 0; i < ds.length; i++) {
            String en = ds[i].getAttributeValue("entity name"); //$NON-NLS-1$
            Vector v = (Vector)EntitiesValidator.entities.get(en);
            if(v == null) {
                MetaValidator.message(MessageFormat.format("Error in {0}: entity ''{1}'' not found.",
						MetaValidator.longid(object), en));
            } else {
                XModelObject[] as = ds[i].getChildren("MetaAttributeData"); //$NON-NLS-1$
                for (int j = 0; j < as.length; j++) {
                    String an = as[j].getAttributeValue("attribute name"); //$NON-NLS-1$
                    if(v.contains(an)) continue;
                    MetaValidator.message(MessageFormat
							.format(
									"Error in {0}: attribute ''{1}'' not found in entity {2}.",
									MetaValidator.longid(object), an, en));
                }
            }
        }
    }
}

class MappingsValidator {
    private MappingToClassValidator cv = new MappingToClassValidator();
    public static Set<String> classmappings = new HashSet<String>();

    public void validate(XModelObject object) {
        classmappings.add("FilteredTrees"); //$NON-NLS-1$
        if(System.getProperty("testmodel") != null) { //$NON-NLS-1$
            classmappings.remove("ObjectEditor"); //$NON-NLS-1$
        } else {
            classmappings.add("AttributeEditor"); //$NON-NLS-1$
        }
        Object[] mtoc = classmappings.toArray();
        for (int i = 0; i < mtoc.length; i++) {
            cv.validate(object.getChildByPath((String)mtoc[i]));
        }
    }
}


class MappingToClassValidator {
    public void validate(XModelObject object) {
        if(object == null) return;
        XModelObject[] is = object.getChildren();
        for (int i = 0; i < is.length; i++)
          MetaValidator.checkClass(is[i], "value", true, null); //$NON-NLS-1$
    }
}

