/*
 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.visualvm.jmx.impl;

import com.sun.tools.visualvm.application.jvm.HeapHistogram;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

/**
 *
 * @author Tomas Hurka
 */
class HeapHistogramImpl extends HeapHistogram {
    private static final String BOOLEAN_TEXT = "boolean"; // NOI18N
    private static final String CHAR_TEXT = "char"; // NOI18N
    private static final String BYTE_TEXT = "byte"; // NOI18N
    private static final String SHORT_TEXT = "short"; // NOI18N
    private static final String INT_TEXT = "int"; // NOI18N
    private static final String LONG_TEXT = "long"; // NOI18N
    private static final String FLOAT_TEXT = "float"; // NOI18N
    private static final String DOUBLE_TEXT = "double"; // NOI18N
    private static final String VOID_TEXT = "void"; // NOI18N
    private static final char BOOLEAN_CODE = 'Z'; // NOI18N
    private static final char CHAR_CODE = 'C'; // NOI18N
    private static final char BYTE_CODE = 'B'; // NOI18N
    private static final char SHORT_CODE = 'S'; // NOI18N
    private static final char INT_CODE = 'I'; // NOI18N
    private static final char LONG_CODE = 'J'; // NOI18N
    private static final char FLOAT_CODE = 'F'; // NOI18N
    private static final char DOUBLE_CODE = 'D'; // NOI18N
    private static final char OBJECT_CODE = 'L'; // NOI18N
    Set<ClassInfo> classes;
    Date time;
    long totalBytes;
    long totalInstances;
    long totalHeapBytes;
    long totalHeapInstances;

    HeapHistogramImpl() {
    }

    HeapHistogramImpl(String histogramText) {
        Map<String,ClassInfoImpl> classesMap = new HashMap(1024);
        Map<String,ClassInfoImpl> permGenMap = new HashMap(1024);
        time = new Date();
        Scanner sc = new Scanner(histogramText);
        sc.useRadix(10);
        sc.nextLine();
        sc.nextLine();
        sc.skip("-+");
        sc.nextLine();


        while(sc.hasNext("[0-9]+:")) {  // NOI18N
            ClassInfoImpl newClInfo = new ClassInfoImpl(sc);
            storeClassInfo(newClInfo, classesMap);
            totalHeapBytes += newClInfo.getBytes();
            totalHeapInstances += newClInfo.getInstancesCount();
        }
        sc.next("Total");   // NOI18N
        totalInstances = sc.nextLong();
        totalBytes = sc.nextLong();
        classes = new HashSet(classesMap.values());
    }

    void storeClassInfo(final ClassInfoImpl newClInfo, final Map<String, ClassInfoImpl> map) {
        ClassInfoImpl oldClInfo = map.get(newClInfo.getName());
        if (oldClInfo == null) {
            map.put(newClInfo.getName(),newClInfo);
        } else {
            oldClInfo.bytes += newClInfo.getBytes();
            oldClInfo.instances += newClInfo.getInstancesCount();
        }
    }

    public Date getTime() {
        return (Date) time.clone();
    }

    public Set<ClassInfo> getHeapHistogram() {
        return classes;
    }

    public long getTotalInstances() {
        return totalInstances;
    }

    public long getTotalBytes() {
        return totalBytes;
    }

    public long getTotalHeapInstances() {
        return totalHeapInstances;
    }

    public long getTotalHeapBytes() {
        return totalHeapBytes;
    }

    public Set<ClassInfo> getPermGenHistogram() {
        return Collections.EMPTY_SET;
    }

    public long getTotalPerGenInstances() {
        return -1;
    }

    public long getTotalPermGenHeapBytes() {
        return -1;
    }

    static class ClassInfoImpl extends ClassInfo {
        long instances;
        long bytes;
        String name;

        ClassInfoImpl(Scanner sc) {
            String jvmName;

            sc.next();
            instances = sc.nextLong();
            bytes = sc.nextLong();
            jvmName = sc.next();
            name = convertJVMName(jvmName);
        }

        public String getName() {
            return name;
        }

        public long getInstancesCount() {
            return instances;
        }

        public long getBytes() {
            return bytes;
        }

        public int hashCode() {
            return getName().hashCode();
        }

        public boolean equals(Object obj) {
            if (obj instanceof ClassInfoImpl) {
                return getName().equals(((ClassInfoImpl)obj).getName());
            }
            return false;
        }

        private String convertJVMName(String jvmName) {
            String className = null;
            int index = jvmName.lastIndexOf('[');     // NOI18N

            if (index != -1) {
                switch(jvmName.charAt(index+1)) {
                    case BOOLEAN_CODE:
                        className=BOOLEAN_TEXT;
                        break;
                    case CHAR_CODE:
                        className=CHAR_TEXT;
                        break;
                    case BYTE_CODE:
                        className=BYTE_TEXT;
                        break;
                    case SHORT_CODE:
                        className=SHORT_TEXT;
                        break;
                    case INT_CODE:
                        className=INT_TEXT;
                        break;
                    case LONG_CODE:
                        className=LONG_TEXT;
                        break;
                    case FLOAT_CODE:
                        className=FLOAT_TEXT;
                        break;
                    case DOUBLE_CODE:
                        className=DOUBLE_TEXT;
                        break;
                    case OBJECT_CODE:
                        className=jvmName.substring(index+2,jvmName.length()-1);
                        break;
                    default:
                        System.err.println("Uknown name "+jvmName);     // NOI18N
                        className = jvmName;
                }
                for (int i=0;i<=index;i++) {
                    className+="[]";
                }
            }
            if (className == null) {
                className = jvmName;
            }
            return className.intern();
        }
    }
}
