/*******************************************************************************
 * Copyright 2Fi Business Solutions Ltd.
 *
 * This code is copyrighted. Under no circumstances should any party, people,
 * or organization should redistribute any portions of this code in any form,
 * either verbatim or through electronic media, to any third parties, unless
 * under explicit written permission by 2Fi Business Solutions Ltd.
 ******************************************************************************/
package com.ffii.tbms.counter.service;

import java.text.DecimalFormat;
import java.util.Map;

import com.ffii.core.dao.JdbcDao;
import com.ffii.core.utils.MapUtils;
import com.ffii.core.utils.Params;
import com.ffii.core.web.AbstractService;
import com.ffii.tbms.counter.IdCounter;
import com.ffii.tbms.counter.dao.IdCounterDao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author Patrick
 */
@Service
public class IdCounterService extends AbstractService {

	@Autowired
	private JdbcDao jdbcDao;

	@Autowired
	private IdCounterDao idCounterDao;

	// DecimalFormat cache
	private static final DecimalFormat FORMATTER[] = { new DecimalFormat(""),
			new DecimalFormat("0"), new DecimalFormat("00"), new DecimalFormat("000"), new DecimalFormat("0000"), new DecimalFormat("00000"),
			new DecimalFormat("000000"), new DecimalFormat("0000000"), new DecimalFormat("00000000"), new DecimalFormat("000000000"),
			new DecimalFormat("0000000000"), new DecimalFormat("00000000000"), new DecimalFormat("000000000000") };

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public Integer saveOrUpdate(IdCounter instance) {
		return idCounterDao.saveOrUpdate(instance);
	}

	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public IdCounter find(Integer id) {
		return idCounterDao.find(id);
	}

	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public IdCounter find(String name) {
		return idCounterDao.findByQuery("from com.ffii.tbms.counter.IdCounter idc where idc.name = ?", name);
	}

	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public Map<String, Object> queryForMap(String name) {
		String sql = "SELECT * FROM id_counter WHERE name = :name";

		return jdbcDao.queryForMap(sql, MapUtils.toHashMap(Params.NAME, name));
	}

	/**
	 * Get the next IdCounter (will be created if not found) value as formatted String with the minimum length specified.
	 * <p>
	 * The counter value will be incremented and saved.
	 *
	 * @param name
	 *            the name of the IdCounter
	 * @param length
	 *            the minimum length of the string to return (e.g. a length of 3 will return <code>012</code> if the counter value is 12), however it doesn't
	 *            limit the maximum size of the counter value (e.g. a length of 3 will return <code>1234</code> if the counter value is 1234)
	 *
	 * @return the next IdCounter value as formatted String with the minimum length specified
	 */
	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public String getNextCountString(String name, int length) {
		if (length < 0 || length > 12) throw new IllegalArgumentException("The length must be between 0 to 12");
		return FORMATTER[length].format(getNextCount(name, length));
	}

	/**
	 * Get the next IdCounter (will be created if not found) preview value as formatted String with the minimum length specified.
	 *
	 * @param name
	 *            the name of the IdCounter
	 * @param length
	 *            the minimum length of the string to return (e.g. a length of 3 will return <code>012</code> if the counter value is 12), however it doesn't
	 *            limit the maximum size of the counter value (e.g. a length of 3 will return <code>1234</code> if the counter value is 1234)
	 *
	 * @return the next IdCounter preview value as formatted String with the minimum length specified
	 */
	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public String getNextCountPreviewString(String name, int length) {
		if (length < 0 || length > 12) throw new IllegalArgumentException("The length must be between 0 to 12");
		return FORMATTER[length].format(getNextCountPreview(name, length));
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public int getNextCount(String name, int length) {

		// find existing IdCounter
		IdCounter instance = find(name);

		if (instance == null) {
			// create IdCounter if not found
			instance = new IdCounter();
			instance.setName(name);
			instance.setLength(length);
			instance.setCount(1);
			saveOrUpdate(instance);
		} else {
			// else just increment and update IdCounter
			instance.setCount(instance.getCount() + 1);
			saveOrUpdate(instance);
		}

		// return the incremented counter value
		return instance.getCount();
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public int getNextCountPreview(String name, int length) {

		// find existing IdCounter
		IdCounter instance = find(name);

		if (instance == null) {
			// create IdCounter if not found
			instance = new IdCounter();
			instance.setName(name);
			instance.setLength(length);
			saveOrUpdate(instance);
		}

		// return the incremented counter value
		return instance.getCount() + 1;
	}

}
