/*
 * Copyright (C) 2015-2016 Jacksgong(blog.dreamtobe.cn)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.dreamtobe.threaddebugger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * The thread category.
 */

public class ThreadCategory implements Cloneable {
    private String mStartWidthKey;
    private String mAlias;
    private List<ThreadInfo> mInfoList;

    boolean is(String threadName) {
        return threadName != null &&
                (threadName.compareToIgnoreCase(threadName) ==
                        threadName.length() - mStartWidthKey.length());
    }

    int size() {
        return mInfoList == null ? 0 : mInfoList.size();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(mAlias);
        if (mInfoList == null || mInfoList.isEmpty()) {
            builder.append("[]");
        } else {
            builder.append('[');
            Iterator<ThreadInfo> it = mInfoList.iterator();
            while (it.hasNext()) {
                ThreadInfo next = it.next();
                builder.append(next.threadName);
                if (it.hasNext()) {
                    builder.append(", ");
                }
            }
            builder.append(']');
        }
        return builder.toString();
    }

    public String getAlias() {
        return mAlias;
    }

    private final static List<String> EMPTY = new ArrayList<>();
    private final static List<ThreadInfo> EMPTY_INFO = new ArrayList<>();

    public List<String> diff(ThreadCategory category) {
        if (mInfoList == null && (category == null || category.mInfoList == null)) {
            return EMPTY;
        }


        final List<String> diffNameList = new ArrayList<>();

        final List<ThreadInfo> curNameList = (List<ThreadInfo>) ((ArrayList) mInfoList).clone();
        final List<ThreadInfo> preNameList = (category == null || category.mInfoList == null) ?
                EMPTY_INFO : (List<ThreadInfo>) ((ArrayList) category.mInfoList).clone();
        for (ThreadInfo info : curNameList) {
            if (!preNameList.contains(info)) {
                diffNameList.add("(+)" + info.threadName);
            }
        }

        for (ThreadInfo info : preNameList) {
            if (!curNameList.contains(info)) {
                diffNameList.add("(-)" + info.threadName);
            }
        }

        return diffNameList;
    }

    public boolean isDiff(ThreadCategory category) {
        if (category == null) {
            return true;
        }

        final int diffSize = size() - category.size();
        return diffSize != 0 || diff(category).size() > 0;

    }


    public static class Builder {
        private final ThreadCategory mCategory = new ThreadCategory();

        public Builder(String key) {
            this(key, key);
        }

        public Builder(String startWithKey, String alias) {
            mCategory.mAlias = alias;
            mCategory.mStartWidthKey = startWithKey.toLowerCase();
        }

        /**
         * @return Whether digested.
         */
        public boolean process(final int hashCode, final String threadName) {
            if (mCategory.is(threadName)) {
                add(hashCode, threadName);
                return true;
            }

            return false;
        }

        public void add(int hashCode, String threadName) {
            if (mCategory.mInfoList == null) {
                mCategory.mInfoList = new ArrayList<>();
            }

            mCategory.mInfoList.add(new ThreadInfo(hashCode, threadName));
        }

        public void reset() {
            if (mCategory.mInfoList != null) {
                mCategory.mInfoList.clear();
            }
        }

        public ThreadCategory build() {
            return mCategory;
        }
    }

    @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
    @Override
    protected ThreadCategory clone() {
        ThreadCategory clone;
        try {
            clone = (ThreadCategory) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            clone = new ThreadCategory();
        }

        //noinspection unchecked
        clone.mInfoList = mInfoList == null ? null :
                (List<ThreadInfo>) ((ArrayList) mInfoList).clone();

        return clone;
    }

    private static class ThreadInfo {
        private final int hashCode;
        private final String threadName;

        ThreadInfo(int hashCode, String threadName) {
            this.hashCode = hashCode;
            this.threadName = threadName;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof ThreadInfo)) {
                return false;
            }

            final ThreadInfo another = (ThreadInfo) o;
            return another.hashCode == hashCode && threadName.equals(another.threadName);
        }
    }
}

@@ -964,23 +964,20 @@
         
-return 
+if (
 threadNa
@@ -983,18 +983,18 @@
 ame 
-!
+=
 = null
- &&
+) %7B
 %0A   
@@ -1006,58 +1006,21 @@
     
-    (threadName.compareToIgnoreCase(threadName) ==
+return false;
 %0A   
@@ -1020,32 +1020,35 @@
 se;%0A        
+%7D%0A%0A
         
         thre
@@ -1035,23 +1035,32 @@
         
-       
+final int diff =
  threadN
@@ -1097,18 +1097,102 @@
 length()
-)
 ;
+%0A        return diff %3E= 0 && threadName.compareToIgnoreCase(mStartWidthKey) == diff;%0A
 %0A    %7D%0A%0A

optimize the arithmetic of checking whether the thread name belong to the thread category