// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.util;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

/**
 * Constructs pretty string representation of object value.
 */
public class PrettyStringBuilder {

	protected int maxItemsToShow = 10;
	protected int maxDeep = 3;
	protected int deep;
	protected String nullValue = "<null>";
	protected String moreValue = ",...";

	/**
	 * Sets the max number of items of arrays, collections and maps to show.
	 */
	public PrettyStringBuilder maxItemsToShow(int maxItemsToShow) {
		this.maxItemsToShow = maxItemsToShow;
		return this;
	}

	/**
	 * Sets how deep to examine inner objects.
	 */
	public PrettyStringBuilder maxDeep(int maxDeep) {
		this.maxDeep = maxDeep;
		return this;
	}

	/**
	 * Sets <code>null</code> value representation.
	 */
	public PrettyStringBuilder showNullAs(String nullValue) {
		this.nullValue = nullValue;
		return this;
	}

	/**
	 * Sets string for 'more'.
	 */
	public PrettyStringBuilder showMoreAs(String moreValue) {
		this.moreValue = moreValue;
		return this;
	}

	/**
	 * Returns pretty value from object value.
	 */
	protected String toPrettyString(Object obj) {
		deep++;
		if (obj == null) {
			deep--;
			return nullValue;
		}
		if (deep == maxDeep) {
			deep--;
			return obj.toString();
		}
		StringBuilder s = new StringBuilder();
		Class c = obj.getClass();
		if (c.isArray()) {
			int arrayLen = Array.getLength(obj);
			int len = Math.min(arrayLen, maxItemsToShow);
			s.append('[');
			for (int i = 0; i < len; i++) {
				s.append(toPrettyString(Array.get(obj, i)));
				if (i != len - 1) {
					s.append(',');
				}
			}
			if (len < arrayLen) {
				s.append(moreValue);
			}
			s.append(']');
		}
		else if (obj instanceof Collection) {
			Collection coll = (Collection) obj;
			int len = Math.min(coll.size(), maxItemsToShow);
			Iterator it = coll.iterator();
			int i = 0;
			s.append('(');
			while ((it.hasNext() && (i < maxItemsToShow))) {
				s.append(toPrettyString(it.next()));
				if (i != len - 1) {
					s.append(',');
				}
				i++;
			}
			if (i < coll.size()) {
				s.append(moreValue);
			}
			s.append(')');
		}
		else if (obj instanceof Map) {
			Map map = (Map) obj;
			int len = Math.min(map.size(), maxItemsToShow);
			Iterator it = map.keySet().iterator();
			int i = 0;
			s.append('{');
			while ((it.hasNext() && (i < maxItemsToShow))) {
				Object key = it.next();
				s.append(key).append(':');
				s.append(toPrettyString(map.get(key)));
				if (i != len - 1) {
					s.append(',');
				}
				i++;
			}
			if (i < map.size()) {
				s.append(moreValue);
			}
			s.append('}');
		}
		else {
			s.append(obj.toString());
		}

		deep--;

		return s.toString();
	}

	/**
	 * Returns pretty string representation of the object.
	 */
	public String toString(Object value) {
		return toPrettyString(value);
	}

	/**
	 * Static version for quick access.
	 */
	public static String str(Object value) {
		return new PrettyStringBuilder().toPrettyString(value);
	}

}
