package com.dev.swms.server.model.inventory;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import com.dev.swms.server.model.abstractEntity.AbstractMoveDoc;
import com.dev.swms.server.model.abstractEntity.AbstractMoveDocDetail;
import com.dev.swms.server.model.base.BOMComponent;
import com.dev.swms.server.model.base.BillType;
import com.dev.swms.server.model.base.Item;
import com.dev.swms.server.model.base.Location;
import com.dev.swms.server.model.base.LotInfo;
import com.dev.swms.server.model.base.NewLotInfo;
import com.dev.swms.server.model.base.Structure;
import com.dev.swms.server.model.base.PackageUnit;
import com.dev.swms.server.model.base.Warehouse;
import com.dev.swms.server.model.shipping.PickTicketStatus;
import com.dev.swms.server.model.shipping.WaveDocStatus;
import com.dev.energy.server.exception.BusinessException;
import com.dev.energy.server.web.security.UserHolder;

public class ProcessDoc extends AbstractMoveDoc {
	private static final long serialVersionUID = 4673430471235765259L;

	public static final String PROCESS_DOC = "PROCESS_DOC";
	
	/** 仓库*/
	private Warehouse warehouse;

	/** 货主*/
	private Structure company;

	/** 加工单号*/
	private String code;

	/** 创建日期*/
	private Date createDate;

	/** 状态*/
	private String status;

	/** 待加工数量(BOM)*/
	private double quantity;

	/** 分配数量(BOM)*/
	private double allocatedQuantity;

	/** 拣货数量(BOM)*/
	private double pickedQuantity;

	/**加工数量(BOM)*/
	private double processQuantity; 

	/** 描述*/
	private String description;

	/** 最后修改人ID*/
	private Long userID; 

	/** 最后修改人姓名*/
	String userName; 

	/** 最后修改时间*/
	private Date updateTime; 
	
	/** 单据类型 */
	private BillType billType;
	
	/** 作业费用 */
	private double processFee;
	
	/** 加工库位 */
	private Location processLoc;
	
	/** 明细*/
	private Set<ProcessDocDetail> details;
	
	/** 子件明细 */
	private Set<ProcessDocBOMDetail> bomDetails;
	
	public int hashCode() {
		return new HashCodeBuilder().append(warehouse).append(code)
				.toHashCode();
	}

	public boolean equals(final Object other) {
		if (!(other instanceof ProcessDoc))
			return false;
		ProcessDoc castOther = (ProcessDoc) other;
		return new EqualsBuilder().append(warehouse, castOther.warehouse)
				.append(code, castOther.code).isEquals();
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public Structure getCompany() {
		return company;
	}

	public void setCompany(Structure company) {
		this.company = company;
	}

	public Date getCreateDate() {
		return createDate;
	}

	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Set<ProcessDocDetail> getDetails() {
		return details;
	}

	public void setDetails(Set<ProcessDocDetail> details) {
		this.details = details;
	}

	public double getProcessQuantity() {
		return processQuantity;
	}

	public void setProcessQuantity(double processQuantity) {
		this.processQuantity = processQuantity;
	}

	public double getQuantity() {
		return quantity;
	}

	public void setQuantity(double quantity) {
		this.quantity = quantity;
	}

	public double getAllocatedQuantity() {
		return allocatedQuantity;
	}

	public double getPickedQuantity() {
		return pickedQuantity;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public Date getUpdateTime() {
		return updateTime;
	}

	public void setUpdateTime(Date updateTime) {
		this.updateTime = updateTime;
	}

	public Long getUserID() {
		return userID;
	}

	public void setUserID(Long userID) {
		this.userID = userID;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Warehouse getWarehouse() {
		return warehouse;
	}

	public void setWarehouse(Warehouse warehouse) {
		this.warehouse = warehouse;
	}
	
	/**
	 * 记录收货单的最新修改用户和修改时间信息
	 * 
	 */
	public void setUserTracking() {
		this.setUserID(UserHolder.getUser().getId());
		this.setUserName(UserHolder.getUser().getName());
		this.setUpdateTime(new Date());
	}
	
	/**
	 * 新增或者修改processDocDetail的时候修改加工单的待加工数量
	 * @param detail
	 */
	public void editQuantityForEditDetail(ProcessDocDetail detail) {
		this.details.add(detail);
		if (!detail.isNew()) {
			for (ProcessDocDetail dl : this.getDetails()) {
				if (dl.getId() == detail.getId()) {
					this.setQuantity(this.getQuantity() - dl.getQuantity());
				}
			}
		}
		this.setQuantity(this.getQuantity() + detail.getQuantity());
		detail.setProcessDoc(this);
		setUserTracking();
	}
	
	/**
	 * 新增明细到加工单,修改加工单的待加工数量
	 * @param detail
	 */
	public void addProcessDocDetail(ProcessDocDetail detail) {
		detail.setProcessDoc(this);
		this.details.add(detail);
		this.quantity += detail.getQuantity();
		addBOMDetailByProcessDocDetail(detail);
		setUserTracking();
	}
	
	/**
	 * 依据加工作业单明细添加子件明细
	 * @param processDocDetail
	 */
	private void addBOMDetailByProcessDocDetail(ProcessDocDetail processDocDetail){
		for(BOMComponent component:processDocDetail.getItem().getComponents()){
			Item item = component.getComponent();
			ProcessDocBOMDetail bomDetail = getProcessDocBOMDetailByItem(item);
			if(bomDetail==null){
				bomDetail = new ProcessDocBOMDetail();
				bomDetail.setItem(item);
				bomDetail.setOrderQuantity(component.getQuantity()*processDocDetail.getQuantity());
				bomDetail.setPackageUnit(item.getMinPackageUnit());
				NewLotInfo lotInfo = new NewLotInfo();
				//lotInfo.setPropL1(processDocDetail.getLotInfo().getPropL1());
				//lotInfo.setPropC4(processDocDetail.getLotInfo().getPropC4());
				bomDetail.setNewLotInfo(lotInfo);
				bomDetail.setProcessDoc(this);
				this.bomDetails.add(bomDetail);
			}else{
				bomDetail.setOrderQuantity(bomDetail.getOrderQuantity()+component.getQuantity()*processDocDetail.getQuantity());
			}
		}
	}
	
	/**
	 * 修改加工作业单子件明细数量
	 * @param processDocDetail
	 * @param deltaQuantity
	 */
	private void modifyBOMDetailByProcessDocDetail(ProcessDocDetail processDocDetail,double deltaQuantity){
		for(BOMComponent component:processDocDetail.getItem().getComponents()){
			Item item = component.getComponent();
			ProcessDocBOMDetail bomDetail = getProcessDocBOMDetailByItem(item);
			if(bomDetail==null){
				continue;
			}
			bomDetail.setOrderQuantity(bomDetail.getOrderQuantity()+component.getQuantity()*deltaQuantity);
			if(bomDetail.getOrderQuantity()==.0){
				this.bomDetails.remove(bomDetail);
			}
		}
	}
	
	public ProcessDocBOMDetail getProcessDocBOMDetailByItem(Item item){
		for(ProcessDocBOMDetail detail:this.getBomDetails()){
			if(detail.getItem().getId().equals(item.getId())){
				return detail;
			}
		}
		return null;
	}
	
	
	
	/**
	 * 删除明细的时候修改加工单的待加工数量
	 * @param details
	 */
	public void editQuantityForDelDetail(List<ProcessDocDetail> details){
		
	}
	
	/**
	 * 检查是否有重复的processDocDetail
	 */
	public ProcessDocDetail getProcessDocDetailByCondition(Item item, LotInfo lotInfo, PackageUnit unit) {
		if (this.details != null) {
			for (ProcessDocDetail detail : this.getDetails()) {
				if (item.getId().equals(detail.getItem().getId())
						&& lotInfo.equals(detail.getLotInfo())
						&& unit.equals(detail.getPackageUnit())) {
					return detail;
				}
			}
		}
		return null;
	}
	
	/**
	 * 增加加工单和明细的待加工数量
	 * 
	 * @param docDetail
	 *            待增加期待数量的收货单明细
	 * @param quantity
	 *            待增加的期待数量(包装数量)
	 */
	public void addQuantity(ProcessDocDetail detail,
			double quantity) {
		this.quantity += quantity*detail.getPackageUnit().getConvertFigure();
//		detail.addQuantity(quantity);
		this.modifyBOMDetailByProcessDocDetail(detail,quantity);
		setUserTracking();
	}
	
	/**
	 * 删除加工单明细，并扣减加工单的待加工数量
	 * 
	 * @param detailIds
	 *        
	 */
	public void removeDetails(List<Long> detailIds) {
		if (details != null	&& ProcessDocStatus.INPUT.equals(this.status)) {
			Set<ProcessDocDetail> toBeRemoved = new HashSet<ProcessDocDetail>();
			for (ProcessDocDetail detail : details) {
				if (detailIds.contains(detail.getId())) {
					toBeRemoved.add(detail);
				}
			}
			for (ProcessDocDetail detail : toBeRemoved) {
				details.remove(detail);
				this.quantity -= detail.getQuantity();
				this.modifyBOMDetailByProcessDocDetail(detail,-detail.getQuantity());
			}
			setUserTracking();
		}
	}
	
	/**
	 * 发货单预分配
	 * 更新发货单状态为'预分配'，并修改预分配数量
	 * @param quantity 预分配数量(MU)
	 */
	public void preAllocate(Double quantity) {
		if (ProcessDocStatus.ACTIVE.equals(this.status)) {
			this.setStatus(ProcessDocStatus.ALLOCATED);
		}
		this.allocatedQuantity += quantity;
	}
	
	/**
	 * 发货单取消预分配
	 * 减少预分配数量，如果预分配数量为0，设置发货单状态为'生效'
	 * @param quantity
	 */
	public void unPreAllocate() {
		this.allocatedQuantity = 0.0;
		this.setStatus(PickTicketStatus.ACTIVE);
	}
	
	public void pick(double oldQty,double quantity){
		this.pickedQuantity-=oldQty;
		this.pickedQuantity+=quantity;
		if(this.pickedQuantity>=this.allocatedQuantity){
			this.status = ProcessDocStatus.PICKED;
		}else{
			this.status = ProcessDocStatus.PICKING;
		}
	}
	
	public void process(double oldQty,double quantity){
		this.processQuantity-=oldQty;
		this.processQuantity+=quantity;
		this.status = ProcessDocStatus.PROCESS;
	}

	@Override
	public void setAllocatedQuantity(double allocatedQuantity) {
		this.allocatedQuantity = allocatedQuantity;
	}

	@Override
	public double getOrderQuantity() {
		return this.quantity;
	}

	@Override
	public void setOrderQuantity(double orderQuantity) {
		this.quantity = orderQuantity;
	}

	@Override
	public void setPickedQuantity(double pickedQuantity) {
		this.pickedQuantity = pickedQuantity;
	}

	@Override
	public double getShippedQuantity() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setShippedQuantity(double shippedQuantity) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public List<AbstractMoveDocDetail> getAbstractMoveDocDetails() {
		List<AbstractMoveDocDetail> moveDocDetails = new ArrayList<AbstractMoveDocDetail>();
		for(ProcessDocBOMDetail docBOMDetail:this.getBomDetails()){
			moveDocDetails.add(docBOMDetail);
		}
		return moveDocDetails;
	}

	@Override
	public Task allocate(String bhCode,AbstractMoveDocDetail moveDocDetail, Inventory inventory, double quantity) {
		if (!(ProcessDocStatus.ACTIVE.equals(this.status) || ProcessDocStatus.ALLOCATED.equals(this.status))) {
			throw new BusinessException("processDoc.status.error");
		}
		Task t=moveDocDetail.allocate(bhCode,inventory, quantity);
		return t;
	}
	
	/**
	 * 分配
	 * @param processDocDetail
	 * @param quantity
	 */
	public void allocate(ProcessDocDetail processDocDetail,double quantity){
		processDocDetail.setAllocatedQuantity(processDocDetail.getAllocatedQuantity()+quantity);
		this.setAllocatedQuantity(this.getAllocatedQuantity()+quantity);
		this.status = ProcessDocStatus.ALLOCATED;
        setUserTracking();
	}
	
	/**
	 * 对指定发货单明细取消分配
	 * 只能对已分配或发运中的发货单取消分配
	 * @param quantity 取消分配数量
	 */
	public void unallocate() {
		if (!(ProcessDocStatus.ALLOCATED.equals(this.status))) {
			throw new BusinessException("processDoc.status.error");
		}
		for(ProcessDocDetail detail:this.getDetails()){
			this.allocatedQuantity -= detail.getAllocatedQuantity();
			detail.setAllocatedQuantity(.0);
		}
		if (this.allocatedQuantity == 0) {
			this.status = WaveDocStatus.ACTIVE;
		}
        setUserTracking();
	}
	
	/**
	 * 拣货
	 * @param processDocDetail
	 * @param quantity
	 */
	public void pick(ProcessDocDetail processDocDetail,double quantity){
		processDocDetail.setPickedQuantity(processDocDetail.getPickedQuantity()+quantity);
		this.setPickedQuantity(this.getPickedQuantity()+quantity);
		if(this.allocatedQuantity==this.pickedQuantity){
			this.status = ProcessDocStatus.PICKED;
		}else{
			this.status = ProcessDocStatus.PICKING;
		}
        setUserTracking();
	}

	public BillType getBillType() {
		return this.billType;
	}

	public void setBillType(BillType billType) {
		this.billType = billType;
	}
	
	public Set<ProcessDocBOMDetail> getBomDetails() {
		return bomDetails;
	}

	public void setBomDetails(Set<ProcessDocBOMDetail> bomDetails) {
		this.bomDetails = bomDetails;
	}

	public Location getProcessLoc() {
		return processLoc;
	}

	public void setProcessLoc(Location processLoc) {
		this.processLoc = processLoc;
	}

	public double getProcessFee() {
		return processFee;
	}

	public void setProcessFee(double processFee) {
		this.processFee = processFee;
	}
	
	/**
	 * 设置加工费用
	 * @param price
	 * @param quantity
	 */
	public void addProcessFee(double price){
		this.processFee += price;
	}

	@Override
	public void setApplyDoc(String code) {
	}
}
