package org.ofbiz.order.order;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.GeneralException;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.entity.GenericDelegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.entity.condition.EntityConditionList;
import org.ofbiz.entity.condition.EntityExpr;
import org.ofbiz.entity.condition.EntityOperator;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.ServiceUtil;

public class ExtendedOrderServices extends OrderServices {
	
	public static Map cancelOrder(DispatchContext dctx, Map context){
		GenericDelegator delegator = dctx.getDelegator();
		String orderId = (String) context.get("orderId");
		GenericValue userLogin = (GenericValue) context.get("userLogin");

		try {
			GenericValue orderHeader = delegator.findByPrimaryKey("OrderHeader", UtilMisc.toMap("orderId", orderId));
			String orderStatusId = orderHeader.getString("statusId");
			Map statusFields = UtilMisc.toMap("statusId", orderStatusId, "statusIdTo", "ORDER_CANCELLED");
			GenericValue statusChange = delegator.findByPrimaryKeyCache("StatusValidChange", statusFields);
			 if (statusChange == null) {
                 return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderErrorCouldNotChangeOrderStatusStatusIsNotAValidChange", (Locale) context.get("locale")));
             }
			
			OrderChangeHelper.cancelOrder2(dctx.getDispatcher(), userLogin, orderId);
			return ServiceUtil.returnSuccess();
		} catch (GeneralException e) {
			return ServiceUtil.returnError(e.getMessage());
		}
	}
	
	public static Map cancelReservations(DispatchContext dctx, Map context){
		GenericDelegator delegator = dctx.getDelegator();
		String orderId = (String) context.get("orderId");
		
		try {
			List orderItems = delegator.findByAnd("OrderItem", UtilMisc.toMap(
					"orderId", orderId));
			Iterator it = orderItems.iterator();
			while (it.hasNext()) {
				GenericValue orderItem = (GenericValue) it.next();
				if (OrderItemUtil.isRentalOrderItem(orderItem)) {
					String orderItemSeqId = orderItem
							.getString("orderItemSeqId");
					Debug.logInfo("order Item is rental"
							+ orderItem.getString("orderId") + "_"
							+ orderItemSeqId, module);
					// procurar os workefforts para este item
					List woifs = delegator.findByAnd(
							"WorkOrderItemFulfillment", UtilMisc.toMap(
									"orderId", orderId, "orderItemSeqId",
									orderItemSeqId));
					Iterator woifsIt = woifs.iterator();
					Debug.logInfo("woifs.size:  " + woifs.size(), module);
					while (woifsIt.hasNext()) {
						String workEffortId = ((GenericValue) woifsIt.next())
								.getString("workEffortId");
						Debug.logInfo("workEffortId " + workEffortId, module);

						GenericValue workEffort = delegator.findByPrimaryKey(
								"WorkEffort", UtilMisc.toMap("workEffortId",
										workEffortId));

						// obter o intervalo da reserva que queremos cancelar
						Date startDate = workEffort
								.getTimestamp("estimatedStartDate");
						Debug.logInfo("startDate " + startDate, module);

						Date endDate = workEffort
								.getTimestamp("estimatedCompletionDate");
						Debug.logInfo("endDate " + endDate, module);

						Calendar cal = Calendar.getInstance();
						cal.setTime(startDate);
						cal.set(Calendar.HOUR, 0);
						cal.set(Calendar.MINUTE, 0);
						cal.set(Calendar.SECOND, 0);
						cal.set(Calendar.MILLISECOND, 0);
						Date correctedStartDate = cal.getTime();
						cal.setTime(endDate);
						cal.set(Calendar.HOUR, 0);
						cal.set(Calendar.MINUTE, 0);
						cal.set(Calendar.SECOND, 0);
						cal.set(Calendar.MILLISECOND, 0);
						Date correctedEndDate = cal.getTime();

						// quantity
						Double quantityToReject = workEffort
								.getDouble("quantityToProduce");
						Debug.logInfo("quantity " + quantityToReject, module);
						Double quantityRejected = workEffort
						.getDouble("quantityRejected");
						Debug.logInfo("quantity rejected " + quantityRejected, module);
						if(quantityRejected != null && quantityRejected.doubleValue() > 0){
							String errorMsg = "Work effort appears to be already annulled";
							Debug.logError(errorMsg,
									module);
							return ServiceUtil.returnError(errorMsg);
						}

						// descobrir o fixed assetId do item através da tabela
						// fixed_asset_product

						String productId = orderItem.getString("productId");
						Debug.logInfo("productId " + productId, module);

						List mainCondList = UtilMisc.toList(new EntityExpr(
								"productId", EntityOperator.EQUALS, productId));
						mainCondList.add(new EntityExpr("fromDate",
								EntityOperator.LESS_THAN_EQUAL_TO,
								new Timestamp(correctedStartDate.getTime())));

						// List conditionList = new ArrayList();
						// TODO pesquisar com thruDate
						// conditionList.add(new EntityExpr("thruDate",
						// EntityOperator.GREATER_THAN_EQUAL_TO,correctedEndDate));
						// conditionList.add(new EntityExpr("thruDate",
						// EntityOperator.,correctedEndDate));
						// EntityCondition ec = new
						// EntityConditionList(conditionList,
						// EntityOperator.OR);
						// mainCondList.add(ec);

						EntityCondition mainCond = new EntityConditionList(
								mainCondList, EntityOperator.AND);
						List fixedAssetIds = delegator.findByCondition(
								"FixedAssetProduct", mainCond, null, null);
						int size = fixedAssetIds.size();
						if (size == 0) {
							String errorMsg = "no fixed assets found for this";
							Debug.logError(errorMsg,
									module);
							return ServiceUtil.returnError(errorMsg);
						}
						String fixedAssetId;
						int millisInADay = 86400 * 1000;
						
						Debug.logError(size + "fixed assets found for this",
								module);
						Iterator faIt = fixedAssetIds.iterator();
						fixedAssetId = null;
						long startTime = correctedStartDate.getTime();
						// iterar para cada possível calendário
						
						double totalToReject = 0;
						while (faIt.hasNext()) {
							GenericValue fixedAssetProduct = (GenericValue) faIt
									.next();
							Timestamp thruDate = fixedAssetProduct
									.getTimestamp("thruDate");

							fixedAssetId = fixedAssetProduct.getString("fixedAssetId");
							GenericValue fixedAsset =
								 delegator.findByPrimaryKey("FixedAsset",
								 UtilMisc.toMap("fixedAssetId",fixedAssetId));
							
							
							String calendarId = fixedAsset
									.getString("calendarId");
							long endTime;
							if (thruDate == null) {
								endTime = correctedEndDate.getTime();
							} else {
								Calendar tilCal = Calendar.getInstance();
								tilCal.setTime(thruDate);
								tilCal.set(Calendar.HOUR, 0);
								tilCal.set(Calendar.MINUTE, 0);
								tilCal.set(Calendar.SECOND, 0);
								tilCal.set(Calendar.MILLISECOND, 0);
								endTime = tilCal.getTime().getTime();
								startTime = endTime;
							}
							long days = (endTime - startTime) / millisInADay;
							cancelReservations(delegator, calendarId,
									startTime, days, quantityToReject);
							totalToReject+=days*quantityToReject.doubleValue();
						}
						workEffort.set("quantityRejected",quantityToReject);
						workEffort.store();
					}
				} else {
					Debug.logInfo("order Item is not rental"
							+ orderItem.getString("orderId") + "_"
							+ orderItem.getString("orderItemSeqId"), module);
				}
			}
		} catch (GenericEntityException e) {
			Debug.logError("Unable to cancel reservations from Calendar - Order: " + orderId, module);
			return ServiceUtil.returnError(e.getMessage());
		}
		
		return ServiceUtil.returnSuccess();

	}
	
	
	private static void cancelReservations(GenericDelegator delegator,
			String calendarId, long startTime, long days, Double quantity)
			throws GenericEntityException {
		int millisInADay = 86400 * 1000;
		Debug.logInfo("days " + days, module);
		while (days-- > 0) {
			Timestamp exceptionDateStartTime = new Timestamp(startTime + days
					* millisInADay);
			Debug.logInfo("iterTime: " + exceptionDateStartTime.toString(),
					module);
			GenericValue calendarRow = delegator.findByPrimaryKey(
					"TechDataCalendarExcDay", UtilMisc.toMap("calendarId",
							calendarId, "exceptionDateStartTime",
							exceptionDateStartTime));
			// descontar na linha correspondente ao intervalo a cancelar -
			// tabela a quantidade quantity
			Double usedCapacity = calendarRow.getDouble("usedCapacity");
			Debug.logInfo("usedCapacity " + usedCapacity, module);
			double newUsedCapacity = usedCapacity.doubleValue()
					- quantity.doubleValue();
			Debug.logInfo("new usedCapacity " + newUsedCapacity, module);
			calendarRow.set("usedCapacity", new Double(newUsedCapacity));
			calendarRow.store();
		}
	}

	
	

}
