/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.core.castor.castors;

import nuts.core.bean.BeanHandler;
import nuts.core.castor.AbstractCastor;
import nuts.core.castor.CastContext;
import nuts.core.castor.Castors;
import nuts.core.lang.CycleDetectStrategy;

import java.lang.reflect.Type;


public class JavaBeanCastor<T> extends AbstractCastor<Object, T> {
	private Castors castors;
	private BeanHandler<T> beanHandler;
	
	public JavaBeanCastor(Type fromType, Type toType, Castors castors) {
		super(fromType, toType);
		
		this.castors = castors;
		beanHandler = castors.getBeanHandler(toType);
	}

	@Override
	protected T createTarget() {
		return beanHandler.createObject();
	}

	@Override
	@SuppressWarnings("unchecked")
	public T convertValue(Object value, CastContext context) {
		T bean = createTarget();
		
		BeanHandler bh = castors.getBeanHandler(value.getClass());
		
		for (String pn : beanHandler.getWritePropertyNames()) {
			if (bh.canReadProperty(pn)) {
				Object pv = bh.getPropertyValue(value, pn);
				if (context.isCycled(value)) {
					switch (context.getCycleDetectStrategy()) {
					case CycleDetectStrategy.CYCLE_DETECT_NOPROP:
						continue;
					case CycleDetectStrategy.CYCLE_DETECT_LENIENT:
						pv = null;
						break;
					default:
						throw cycleError(context, pn, value);
					}
				}

				Type pt = beanHandler.getPropertyType(pn);
				context.push(pn, value);
				try {
					pv = castors.cast(pv, pt, context);
					beanHandler.setPropertyValue(bean, pn, pv);
				}
				catch (Throwable e) {
					throw wrapError(context, e);
				}
				finally {
					context.popup();
				}
			}
		}
		return bean; 
	}
}
