/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.sysds.runtime.compress;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.sysds.runtime.compress.colgroup.AColGroup;

/**
 * Compression Statistics contain the main information gathered from the compression, such as sizes of the original
 * matrix, vs the compressed representation at different stages of the compression.
 */
public class CompressionStatistics {

	/** Size of the original input */
	public long originalSize;
	/** Size if the input is dense */
	public long denseSize;
	/** Size if the input is sparse */
	public long sparseSize;
	/** Estimated size of compressing individual columns */
	public long estimatedSizeCols;
	/** Estimated size of compressing after co-coding */
	public long estimatedSizeCoCoded;
	/** Compression size after compressing but before finalize */
	public long compressedInitialSize;
	/** Compressed size */
	public long compressedSize;

	/** Cost calculated by the cost estimator on input */
	public double originalCost = Double.NaN;
	/** Summed cost estimated from individual columns */
	public double estimatedCostCols = Double.NaN;
	/** Summed cost after cocoding */
	public double estimatedCostCoCoded = Double.NaN;
	/** Compressed cost after compression but before finalize */
	public double compressedInitialCost = Double.NaN;
	/** Cost of the compressed representation */
	public double compressedCost = Double.NaN;

	/** local hashmap to count the column group instances */
	private Map<String, int[]> colGroupCounts;

	/**
	 * Set array of counts regarding col group types.
	 * 
	 * The position corresponds with the enum ordinal.
	 * 
	 * @param colGroups list of ColGroups used in compression.
	 */
	protected void setColGroupsCounts(List<AColGroup> colGroups) {
		HashMap<String, int[]> ret = new HashMap<>();
		for(AColGroup c : colGroups) {
			String ct = c.getClass().getSimpleName();
			int colCount = c.getNumCols();
			int[] values;
			if(ret.containsKey(ct)) {
				values = ret.get(ct);
				values[0] += 1;
				values[1] += colCount;
			}
			else {
				values = new int[] {1, colCount};
			}
			ret.put(ct, values);
		}
		this.colGroupCounts = ret;
	}

	public String getGroupsTypesString() {
		StringBuilder sb = new StringBuilder();

		for(String ctKey : colGroupCounts.keySet())
			sb.append(ctKey + ":" + colGroupCounts.get(ctKey)[0] + " ");

		return sb.toString();
	}

	public String getGroupsSizesString() {
		StringBuilder sb = new StringBuilder();

		for(String ctKey : colGroupCounts.keySet())
			sb.append(ctKey + ":" + colGroupCounts.get(ctKey)[1] + " ");

		return sb.toString();
	}

	public double getRatio() {
		return compressedSize == 0.0 ? Double.POSITIVE_INFINITY : (double) originalSize / compressedSize;
	}

	public double getCostRatio() {
		return compressedSize == 0.0 ? Double.POSITIVE_INFINITY : (double) originalCost / compressedCost;
	}

	public double getDenseRatio() {
		return compressedSize == 0.0 ? Double.POSITIVE_INFINITY : (double) denseSize / compressedSize;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("\nCompressionStatistics:");
		sb.append("\nDense Size            : " + denseSize);
		sb.append("\nOriginal Size         : " + originalSize);
		sb.append("\nCompressed Size       : " + compressedSize);
		sb.append("\nCompressionRatio      : " + getRatio());
		sb.append("\nDenseCompressionRatio : " + getDenseRatio());

		if(colGroupCounts != null) {
			sb.append("\nCompressionTypes      : " + getGroupsTypesString());
			sb.append("\nCompressionGroupSizes : " + getGroupsSizesString());
		}
		return sb.toString();
	}

}
