package com.sun.sig_ext;

import com.sun.tdk.signaturetest.errors.ErrorFormatter;
import com.sun.tdk.signaturetest.errors.MessageType;
import com.sun.tdk.signaturetest.model.ClassDescription;
import com.sun.tdk.signaturetest.model.ConstructorDescr;
import com.sun.tdk.signaturetest.model.MemberCollection;
import com.sun.tdk.signaturetest.model.MemberDescription;
import com.sun.tdk.signaturetest.model.MemberType;
import com.sun.tdk.signaturetest.model.MethodDescr;
import com.sun.tdk.signaturetest.plugin.Filter;
import com.sun.tdk.signaturetest.plugin.MessageTransformer;
import com.sun.tdk.signaturetest.plugin.Plugin;
import com.sun.tdk.signaturetest.plugin.PluginAPI;
import com.sun.tdk.signaturetest.plugin.Transformer;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

public class Imfilter implements Plugin, Filter, Transformer, MessageTransformer {

    @Override
    public void init(PluginAPI api) {
        api.setFilter(PluginAPI.BEFORE_TEST, this);
        api.setTransformer(PluginAPI.BEFORE_TEST, this);
        api.setMessageTransformer(PluginAPI.BEFORE_MESSAGE_SORT, this);
    }

    @Override
    public void release() {
    }

    @Override
    public ClassDescription transform(ClassDescription cls) throws ClassNotFoundException {
        MemberCollection cleaned = new MemberCollection();
        for (Iterator e = cls.getMembersIterator(); e.hasNext();) {
            MemberDescription mr = (MemberDescription) e.next();
            if (mr.isMethod()) {
                cleaned.addMember(mr);
            }
        }

        cls.setMembers(cleaned);
        return cls;
    }

    @Override
    public boolean accept(ClassDescription cls) {
        return cls.isInterface();
    }

    @Override
    public List changeMessageList(List messages) {
        Iterator<ErrorFormatter.Message> it = messages.iterator();
        while (it.hasNext()) {
            ErrorFormatter.Message m = it.next();
            MessageType mt = m.messageType;
            if (mt != MessageType.ADD_METHS && mt != MessageType.MISS_METHS) {
                it.remove();
                continue;
            }
            String dcn = m.errorObject.getDeclaringClassName();
            if (!m.className.equals(dcn)) {
                it.remove();
                continue;
            }
        }
        // change message pair from abstract to default to single message
        Collections.sort(messages, new ErrorComparator());
        MessageType toDefault = new MessageType("Changed from abstract to default", -2);

        for (int i = 0; i < messages.size() - 1; i++) {
            ErrorFormatter.Message m1 = (ErrorFormatter.Message) messages.get(i);
            if (m1.messageType == MessageType.MISS_METHS) {
                ErrorFormatter.Message m2 = (ErrorFormatter.Message) messages.get(i + 1);
                if (m2.messageType == MessageType.ADD_METHS && m1.className.equals(m2.className)) {
                    MethodDescr mth1 = (MethodDescr) m1.errorObject;
                    MethodDescr mth2 = (MethodDescr) m2.errorObject;
                    if (mth1.getSignature().equals(mth2.getSignature()) && mth1.isAbstract() && !mth2.isAbstract()) {
                        m1.messageType = toDefault;
                        messages.set(i + 1, null);
                        i++;
                    }
                }
            }
        }

        it = messages.iterator();
        while (it.hasNext()) {
            ErrorFormatter.Message m = it.next();
            if (m == null) {
                it.remove();
            }
        }
        
        return messages;
    }

    private static class ErrorComparator implements Comparator {

        @Override
        public int compare(Object o1, Object o2) {
            ErrorFormatter.Message msg1 = (ErrorFormatter.Message) o1;
            ErrorFormatter.Message msg2 = (ErrorFormatter.Message) o2;
            MemberDescription md1 = msg1.errorObject;
            MemberDescription md2 = msg2.errorObject;

            int comp = md1.getQualifiedName().compareTo(md2.getQualifiedName());

            if (comp == 0) {
                comp = md1.getMemberType().compareTo(md2.getMemberType());
                if (comp == 0) {
                    if (md1 instanceof MethodDescr && md2 instanceof MethodDescr) {
                        MethodDescr mth1 = (MethodDescr) md1;
                        MethodDescr mth2 = (MethodDescr) md2;
                        comp = mth1.getSignature().compareTo(mth2.getSignature());
                    }
                }
                if (comp == 0) {
                    comp = msg1.className.compareTo(msg2.className);
                }
            }
            return comp;
        }
    }
}
