package siprp.database.cayenne.objects;

import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.cayenne.CayenneDataObject;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.PersistenceState;

import siprp.database.cayenne.providers.MainDAO;

import com.evolute.utils.strings.StringPlainer;
import com.evolute.utils.strings.UnicodeChecker;

public class BaseObject extends CayenneDataObject implements Comparable<BaseObject>
{

	static {
		UnicodeChecker.setUseDoubleSlash( true );
	}
	
	private static final long serialVersionUID = 1L;
	
	protected static MainDAO dao = new MainDAO();
	
	public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance( DateFormat.SHORT, new Locale( "pt", "PT" ) );

	protected static final String isNewMessage = " ";
	
	public boolean isTransient()
	{
		return getPersistenceState() == PersistenceState.TRANSIENT;
	}
	
	public boolean isNew()
	{
		return getPersistenceState() == PersistenceState.NEW;
	}
	
	public boolean isCommited()
	{
		return getPersistenceState() == PersistenceState.COMMITTED;
	}
	
	public boolean isModified()
	{
		return getPersistenceState() == PersistenceState.MODIFIED;
	}
	
	public void commit()
	{
		dao.commit();
	}
	
	public void rollback()
	{
		dao.rollback();
	}
	
	public void save() throws Exception
	{
		if( isTransient() )
		{
			dao.getContext().registerNewObject( this );
		}
		commit();
	}
	
	private boolean hasDeletedDate()
	{
		boolean result = false;
		try {
			result = getClass().getField("DELETED_DATE_PROPERTY") != null;
		} catch (SecurityException e) {
		} catch (NoSuchFieldException e) {
		}
		return result;
	}
	
	public void delete() throws Exception
	{
		if( hasDeletedDate() )
		{
			writeProperty( "deletedDate", new Date() );
		}
		else
		{
			dao.getContext().deleteObject( this );
		}
		commit();
	}
	
	protected String parseToUnicode( String string )
	{
		String result = string == null ? null : UnicodeChecker.parseToUnicode( string );
		return result == null ? null : result.replaceAll( "\\\\\\\\", "\\\\" );
	}
	
	protected String parseFromUnicode( String string )
	{
		return string == null ? null : UnicodeChecker.parseFromUnicode( string );
	}

	@Override
	public int compareTo( BaseObject object )
	{
		int result = 0;
		if( dao.hasOrder( this ) && dao.hasOrder( object ) )
		{
			Object o1 = this.readProperty( "ordem" );
			o1 = o1 != null ? o1 : this.readProperty( "order" );
			if( o1 != null && (o1 instanceof Integer) )
			{
				Object o2 = object.readProperty( "ordem" );
				o2 = o2 != null ? o2 : object.readProperty( "order" );
				if( o2 != null && (o2 instanceof Integer) )
				{
					result = ((Integer)o1).compareTo( (Integer)o2 );
				}
			}
		}
		if( result == 0 )
		{
			result = StringPlainer.convertString( this.toString() ).compareTo( object == null ? null : StringPlainer.convertString( object.toString() ) );
		}
		return result;
	}
	
	private <OBJ_CLASS extends BaseObject > boolean isRelational( OBJ_CLASS object )
	{
		boolean result = false;
		ObjectId id = object.getObjectId();
		if( id != null && !id.isTemporary() )
		{
			Map<String,Object> ids = id.getIdSnapshot();
			if( ids != null && ids.size() == 2 )
			{
				result = true;
			}
		}
		return result;
	}
	
	private <OBJ_CLASS extends BaseObject > boolean isRelDeleted( OBJ_CLASS object )
	{
		boolean result = false;
		if( object != null )
		{
			for( Method method : object.getClass().getMethods() )
			{
				if( method.getName().startsWith("getTo") )
				{
					try
					{
						Object returned = method.invoke(object);
						if( returned != null && (returned instanceof BaseObject ) )
						{
							result |= null != ((BaseObject)returned).readProperty("deletedDate");
						}
					}
					catch (Exception e)
					{
						e.printStackTrace();
					}
				}
			}
		}
		return result;
	}
	
	@Override
	public Object readProperty(String propertyName) {
		return (propertyName != null && propertyName.endsWith("Array") ) ? getReferringObjects(propertyName) : super.readProperty(propertyName);
	}

	@SuppressWarnings("unchecked")
	private <OBJ_CLASS extends BaseObject> List<OBJ_CLASS> getReferringObjects( String arrayProperty )
	{
		List<OBJ_CLASS> result = (List<OBJ_CLASS>) super.readProperty(arrayProperty);
		List<OBJ_CLASS> toDelete = new LinkedList<OBJ_CLASS>();
		Boolean isRel = null;
		for( OBJ_CLASS o : result )
		{
			if( isRel == null )
			{
				isRel = isRelational( o );
			}
			if( null != o.readProperty( "deletedDate" ) || isRel && isRelDeleted(o) )
			{
				toDelete.add(o);
			}
		}
		for( OBJ_CLASS o : toDelete )
		{
			result.remove(o);
		}
		return result;
	}
	
}
