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

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.BillType;
import com.dev.swms.server.model.base.Item;
import com.dev.swms.server.model.base.Location;
import com.dev.swms.server.model.base.LocationType;
import com.dev.swms.server.model.base.LotInfo;
import com.dev.swms.server.model.base.NewLotInfo;
import com.dev.swms.server.model.base.PackageUnit;
import com.dev.swms.server.model.base.Structure;
import com.dev.swms.server.model.base.Warehouse;
import com.dev.swms.server.model.base.Worker;
import com.dev.swms.server.model.inventory.Inventory;
import com.dev.swms.server.model.inventory.Task;
import com.dev.swms.server.model.rules.WorkModel;
import com.dev.swms.server.utils.DoubleUtils;
import com.dev.energy.server.exception.BusinessException;
import com.dev.energy.server.model.EntityFactory;
import com.dev.energy.server.web.security.UserHolder;

public class WaveDoc extends AbstractMoveDoc {
	public Structure getCompany() {
		return company;
	}
	public void setCompany(Structure company) {
		this.company = company;
	}

	private static final long serialVersionUID = 1L;
	/** 仓库 */
	private Warehouse warehouse;
	/** 波次号 */
	private String code;
	/** 波次状态 */
	private String status;
	/** 订单数量 */
	private double orderQuantity; //补货数量
	/** 重量 */
	private double weight;
	/** 体积 */
	private double volumn;
	/** 预分配数量 */
	private double preAllocatedQuantity;
	/** 分配数量 */
	private double allocatedQuantity;
	/** 拣货数量（MU数量） */
	private double pickedQuantity;
	/** 分单数量（MU数量） */
	private double billAllocatedQuantity;
	/** 备货库位 */
	private Location prepareGoodsLoc;
	/** 波次明细 */
	private Set<WaveDocDetail> waveDocDetails;
	/** 最后修改人ID */
	private Long userID;
	/** 最后修改人姓名 */
	private String userName;
	/** 最后修改时间 */
	private Date updateTime;
	/** 波次规则的开始时间 */
	private Date startDate;
	
	/** 波次规则的结束时间 */
	private Date endDate;
	
	/** 作业模式 */
	private String workModel;
	/** 对应波次规则 */
	private String waveRules;
	/** 创建时间 */
	private Date createDate;
	/** 周转方式 */
	private String turnOverMode;
	/** 单据打印标志 */
	private int printCount = 0;
	/** 批拣方式 */
	private String pickBatchType;
	/**生成该波次的波次规则ID*/
	private Long waveRuleId;
	/** 补货单号 */
	private String applyDoc;
	
	private Structure company;//货主
	
	private double quantity;//数量
	
	
	/** 作业人员 */
	private Worker worker1;
	/** 作业人员 */
	private Worker worker2;
	/** 作业人员 */
	private Worker worker3;
	/** 作业人员 */
	private Worker worker4;
	

	public Worker getWorker1() {
		return worker1;
	}
	public void setWorker1(Worker worker1) {
		this.worker1 = worker1;
	}
	public Worker getWorker2() {
		return worker2;
	}
	public void setWorker2(Worker worker2) {
		this.worker2 = worker2;
	}
	public Worker getWorker3() {
		return worker3;
	}
	public void setWorker3(Worker worker3) {
		this.worker3 = worker3;
	}
	public Worker getWorker4() {
		return worker4;
	}
	public void setWorker4(Worker worker4) {
		this.worker4 = worker4;
	}
	public double getQuantity() {
		return quantity;
	}
	public void setQuantity(double quantity) {
		this.quantity = quantity;
	}
	public double getPreAllocatedQuantity() {
		return preAllocatedQuantity;
	}
	public void setPreAllocatedQuantity(double preAllocatedQuantity) {
		this.preAllocatedQuantity = preAllocatedQuantity;
	}
	public Date getEndDate() {
		return endDate;
	}
	public void setEndDate(Date endDate) {
		this.endDate = endDate;
	}
	public String getApplyDoc() {
		return applyDoc;
	}
	public void setApplyDoc(String applyDoc) {
		this.applyDoc = applyDoc;
	}
	public Long getWaveRuleId() {
		return waveRuleId;
	}
	public void setWaveRuleId(Long waveRuleId) {
		this.waveRuleId = waveRuleId;
	}
	public String getPickBatchType() {
		return pickBatchType;
	}
	public void setPickBatchType(String pickBatchType) {
		this.pickBatchType = pickBatchType;
	}
	public int getPrintCount() {
		return printCount;
	}
	public void setPrintCount(int printCount) {
		this.printCount = printCount;
	}
	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 double getAllocatedQuantity() {
		return allocatedQuantity;
	}
	public void setAllocatedQuantity(double allocatedQuantity) {
		this.allocatedQuantity = allocatedQuantity;
	}
	public double getBillAllocatedQuantity() {
		return billAllocatedQuantity;
	}
	public void setBillAllocatedQuantity(double billAllocatedQuantity) {
		this.billAllocatedQuantity = billAllocatedQuantity;
	}
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	public double getOrderQuantity() {
		return orderQuantity;
	}
	public void setOrderQuantity(double orderQuantity) {
		this.orderQuantity = orderQuantity;
	}
	public double getPickedQuantity() {
		return pickedQuantity;
	}
	public void setPickedQuantity(double pickedQuantity) {
		this.pickedQuantity = pickedQuantity;
	}
	public Warehouse getWarehouse() {
		return warehouse;
	}
	public void setWarehouse(Warehouse warehouse) {
		this.warehouse = warehouse;
	}
	public String getStatus() {
		return status;
	}
	public void setStatus(String status) {
		this.status = status;
	}
	public double getVolumn() {
		return volumn;
	}
	public void setVolumn(double volumn) {
		this.volumn = volumn;
	}
	public double getWeight() {
		return weight;
	}
	public void setWeight(double weight) {
		this.weight = weight;
	}	
	public Set<WaveDocDetail> getWaveDocDetails() {
		return waveDocDetails;
	}
	public void setWaveDocDetails(Set<WaveDocDetail> waveDocDetails) {
		this.waveDocDetails = waveDocDetails;
	}
	@Override
	public double getShippedQuantity() {
		return 0;
	}
	@Override
	public void setShippedQuantity(double shippedQuantity) {
	}
	public Location getPrepareGoodsLoc() {
		return prepareGoodsLoc;
	}
	public void setPrepareGoodsLoc(Location prepareGoodsLoc) {
		this.prepareGoodsLoc = prepareGoodsLoc;
	}
	public Date getStartDate() {
		return startDate;
	}
	public void setStartDate(Date startDate) {
		this.startDate = startDate;
	}
	public String getWaveRules() {
		return waveRules;
	}
	public void setWaveRules(String waveRules) {
		this.waveRules = waveRules;
	}
	public String getWorkModel() {
		return workModel;
	}
	public void setWorkModel(String workModel) {
		this.workModel = workModel;
	}
	@Override
	public BillType getBillType() {
		return null;
	}
	
	@Override
	public List<AbstractMoveDocDetail> getAbstractMoveDocDetails() {
		List<AbstractMoveDocDetail> details = new ArrayList<AbstractMoveDocDetail>();
		for(WaveDocDetail detail :this.waveDocDetails){
			details.add(detail);
		}
        return details;

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

	public int hashCode() {
		return new HashCodeBuilder().append(warehouse).append(code).toHashCode();
	}
	
	
	/**
	 * 按单方式添加拣货单到波次作业单
	 * @param pickTickets
	 */
	public void addPickTicketsByPickTicketModel(List<PickTicket> pickTickets){
		this.setWarehouse(pickTickets.get(0).getWarehouse());
		if(this.getWaveDocDetails()==null){
			this.setWaveDocDetails(new HashSet<WaveDocDetail>());
		}
		for(PickTicket pickTicket:pickTickets){
			pickTicket.setWaveCode(this.getCode());
			for(PickTicketDetail pickTicketDetail:pickTicket.getPickTicketDetails()){//一条拣货单明细一条件波次作业明细
				WaveDocDetail detail = new WaveDocDetail();
				detail.setWaveDoc(this);
				detail.addPickTicketDetail(pickTicketDetail,true);
				this.waveDocDetails.add(detail);
			}
		}
	}
	
	/**
	 * 按批拣方式添加拣货单到波次作业单
	 * 系统自动将所有的包装级别全部按2级包装（包装行号为2）产生波次明细，不够包装的零头数单独产生零头的波次明细
	 * @param pickTickets
	 */
	@SuppressWarnings("unchecked")
	public void addPickTicketsByPickBatchModel(List<PickTicket> pickTickets){
		//TODO:解决算法缺陷：当明细为大包装时，最好不将包装打散重处理，应该只对最小包装进行合并大包装
		this.setWarehouse(pickTickets.get(0).getWarehouse());
		if(this.getWaveDocDetails()==null){
			this.setWaveDocDetails(new HashSet<WaveDocDetail>());
		}
		for(PickTicket pickTicket:pickTickets){
			pickTicket.setWaveCode(this.getCode());
			for (PickTicketDetail ptd : pickTicket.getPickTicketDetails()) {
				Item item = ptd.getItem();
				NewLotInfo lotinfo=ptd.getNewLotInfo();
				addWaveDocDetail(item, lotinfo, ptd.getPackageUnit(), ptd.getOrderQuantity(),ptd.isOldRecord());
//				Item item = ptd.getItem();
//				double qty=ptd.getOrderQtyOfMasterUnit();
//				NewLotInfo lotinfo=ptd.getNewLotInfo();
//				if(item.getCAcoefficient()!=null&&item.getCAcoefficient()>0&&item.getLocation()!=null){
//					double caQty=(qty/item.getCAcoefficient())*item.getCAcoefficient();
//					double puQty=qty-(qty/item.getCAcoefficient())*item.getCAcoefficient();
//					addWaveDocDetail(item, lotinfo, ptd.getPackageUnit(), caQty,item.getCAcoefficient(),LocationType.STORAGE);
//					addWaveDocDetail(item, lotinfo, ptd.getPackageUnit(), puQty,item.getCAcoefficient(),LocationType.TRANSIT);
//				}else{
//					if(item.getLocation()!=null){
//						addWaveDocDetail(item, lotinfo, ptd.getPackageUnit(), qty,0D,LocationType.TRANSIT);
//					}else{
//						addWaveDocDetail(item, lotinfo, ptd.getPackageUnit(), qty,0D,null);
//					}
//					
//				}
			}
		}
		/**
		Map<String,TempWaveDocDetail> itemPickTicketDetailMap = new HashMap<String, TempWaveDocDetail>();
		for(PickTicket pickTicket:pickTickets){
			pickTicket.setWaveCode(this.getCode());
			for (PickTicketDetail ptd : pickTicket.getPickTicketDetails()) {
				String str = ""+ptd.getItem().getId()+"_"+ptd.getNewLotInfo();
				if(itemPickTicketDetailMap.get(str)==null){
					TempWaveDocDetail twdd = new TempWaveDocDetail(ptd.getItem(), ptd.getNewLotInfo());
					twdd.addQuantity(ptd.getOrderQtyOfMasterUnit());
					itemPickTicketDetailMap.put(str,twdd);
				}else{
					TempWaveDocDetail twdd = itemPickTicketDetailMap.get(str);
					twdd.addQuantity(ptd.getOrderQtyOfMasterUnit());
				}
			}
		}
		
		Iterator it = itemPickTicketDetailMap.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			TempWaveDocDetail twdd = (TempWaveDocDetail) entry.getValue();
					Item item = twdd.getItem();
					NewLotInfo newLotInfo = twdd.getNewLotInfo();
					Double orderQtyOfMasterUnit = twdd.getQuantity();
					PackageUnit piecePack = item.getMinPackageUnit();
					PackageUnit casePack = item.getPackageUnitByLineNo(2);
					
					Double caseQty = Math.floor(orderQtyOfMasterUnit / casePack.getConvertFigure());
					Double picecQty = DoubleUtils.formatByPrecision(orderQtyOfMasterUnit - caseQty*casePack.getConvertFigure(), piecePack.getPrecision());
					if (caseQty > 0) {
						//创建大包装明细
						addWaveDocDetail(item, newLotInfo, casePack, caseQty);
					} 
					if (picecQty > 0) {
						//创建小包装明细
						addWaveDocDetail(item, newLotInfo, piecePack, picecQty);
					}
							
		}**/
	}
	
	/**
	 * 根据订单明细创建波次明细
	 * @param item
	 * @param newLotInfo
	 * @param orderQtyOfMasterUnit
	 */
	private void addWaveDocDetail(Item item, NewLotInfo newLotInfo,PackageUnit unit, Double quantity,Boolean bh) {
		WaveDocDetail wdd = null;
		for(WaveDocDetail waveDocDetail : this.getWaveDocDetails()){
			if(waveDocDetail.getItem().getId() == item.getId()
					&& (newLotInfo==null||waveDocDetail.getNewLotInfo().equals(newLotInfo))
					&& waveDocDetail.getPackageUnit().getId() == unit.getId()){
				wdd = waveDocDetail;
			} 
		}
		if (wdd == null) {
			wdd = EntityFactory.getEntity(WaveDocDetail.class);
			wdd.setWaveDoc(this);
			this.getWaveDocDetails().add(wdd);
			wdd.setItem(item);
			wdd.setCaCoefficient(item.getCAcoefficient()==null?0:item.getCAcoefficient());
			wdd.setLocType(null);
			wdd.setNewLotInfo(newLotInfo);
			wdd.setPackageUnit(unit);
		}
		double bhQty=0;
		if(bh){
			bhQty=quantity;
		}
		wdd.addQuantity(quantity,bhQty);
	}
	
	/**
	 * 记录收货单的最新修改用户和修改时间信息
	 *
	 */
	public void setUserTracking() {
		this.setUserID(UserHolder.getUser().getId());
		this.setUserName(UserHolder.getUser().getName());
		this.setUpdateTime(new Date());
	}
	
	/**
	 * 波次作业单为按单方式时删除拣货单
	 * @param pickTickets
	 * @return
	 */
	public List<WaveDocDetail> deletePickTicketsByPickTicketModel(List<PickTicket> pickTickets){
		List<WaveDocDetail> details = new ArrayList<WaveDocDetail>();
		for(PickTicket pickTicket:pickTickets){
			pickTicket.setWaveCode(null);
			for(WaveDocDetail waveDocDetail:this.waveDocDetails){
				if(waveDocDetail.getPickTicket().getId()==pickTicket.getId()){
					this.setWeight(this.getWeight()-waveDocDetail.getPackageUnit().getWeight()*waveDocDetail.getOrderQuantity());
					this.setVolumn(this.getVolumn()-waveDocDetail.getPackageUnit().getVolume()*waveDocDetail.getOrderQuantity());
					this.setOrderQuantity(this.getOrderQuantity()-waveDocDetail.getOrderQuantity());
					details.add(waveDocDetail);
				}
			}
		}
		return details;
	}
	
	/**
	 * 波次作业单是批拣方式时删除拣货单
	 * @param pickTickets
	 * @return
	 */
	public List<WaveDocDetail> deletePickTicketsByPickBatchModel(List<PickTicket> pickTickets){
		List<WaveDocDetail> details = new ArrayList<WaveDocDetail>();
		for(PickTicket pickTicket:pickTickets){
			pickTicket.setWaveCode(null);
			for(PickTicketDetail pickTicketDetail:pickTicket.getPickTicketDetails()){
				for(WaveDocDetail waveDocDetail:this.waveDocDetails){
					if(waveDocDetail.getItem().getId() == pickTicketDetail.getItem().getId()
							//&& waveDocDetail.getLotInfo().equals(pickTicketDetail.getLotInfo())
							&& waveDocDetail.getPackageUnit().getId()==pickTicketDetail.getPackageUnit().getId()){
						this.setWeight(this.getWeight()-pickTicketDetail.getPackageUnit().getWeight()*pickTicketDetail.getOrderQuantity());
						this.setVolumn(this.getVolumn()-pickTicketDetail.getPackageUnit().getVolume()*pickTicketDetail.getOrderQuantity());
						this.setOrderQuantity(this.getOrderQuantity()-pickTicketDetail.getOrderQuantity());
						waveDocDetail.deletePickTicketDetail(pickTicketDetail);
						if(waveDocDetail.getOrderQtyOfMasterUnit()==0){
							details.add(waveDocDetail);
						}
						break;
					}
				}
			}
		}
		return details;
	}
	
	/**
	 * 对指定的发货单明细分配
	 * 只能对打开、已分配或部分发运状态的发货单做拣货分配
	 * @param moveDocDetail 待分配的发货单明细
	 * @param inventory 待分配的库存
	 * @param quantity 分配数量
	 */
	public Task allocate(String bhCode,AbstractMoveDocDetail moveDocDetail, Inventory inventory, double quantity) {
		if (!(WaveDocStatus.OPEN.equals(this.status) ||WaveDocStatus.ACTIVE.equals(this.status) || WaveDocStatus.ALLOCATE.equals(this.status))) {
			throw new BusinessException("waveDoc.status.error");
		}
		Task t=moveDocDetail.allocate(null,inventory, quantity);
		this.allocatedQuantity += quantity;
		if (DoubleUtils.compareByPrecision(this.allocatedQuantity, this.orderQuantity, 
				moveDocDetail.getPackageUnit().getPrecision())==0) {
			this.setApplyDoc("");
		}
		this.status = WaveDocStatus.ALLOCATE;
        setUserTracking();
        return t;
	}
	
	/**
	 * 对指定发货单明细取消分配
	 * 只能对已分配或发运中的发货单取消分配
	 * @param quantity 取消分配数量
	 */
	public void unallocate(double quantity) {
		if (!(WaveDocStatus.ALLOCATE.equals(this.status))
				//分单后关闭波次时对已分配未拣货部份取消分配
				&&!(WaveDocStatus.SPLIT.equals(this.status))
				&&!(WaveDocStatus.SPLIT_ALL.equals(this.status))) {
			throw new BusinessException("waveDoc.status.error");
		}
		this.allocatedQuantity -= quantity;
		if (this.allocatedQuantity == 0) {
//			this.status = WaveDocStatus.ACTIVE;
			this.status = WaveDocStatus.OPEN;
		}
        setUserTracking();
	}
	
	/**
	 * 拣货确认
	 * @param picked
	 * @param convertFigure
	 * @param detailConvertFigure
	 */
	public void pickConfirm(double pickedQuantity){
		if (!(WaveDocStatus.ALLOCATE.equals(this.status) 
				|| WaveDocStatus.PICKUP.equals(this.status)|| WaveDocStatus.PICK_ALL.equals(this.status)  )) {
			throw new BusinessException("waveDoc.status.error");
		}
		this.pickedQuantity += pickedQuantity;
		//拣货确认后状态判断
		if (this.orderQuantity == this.pickedQuantity) {
			this.status = WaveDocStatus.PICK_ALL;
		} else {
			this.status = WaveDocStatus.PICKUP;
		}
	}
	
	/**
	 * 关闭波次作业单
	 */
	public void close(){
		this.status = WaveDocStatus.CLOSE;
		this.setUserTracking();
	}
	
	/**
	 * 分单
	 * @param pickTicketDetail 分单明细
	 * @param moveQty          分单数量
	 */
	public void split(PickTicketDetail pickTicketDetail,double moveQty){
		WaveDocDetail waveDocDetail = null;
		for(WaveDocDetail detail:this.getWaveDocDetails()){
			if(detail.getItem().getId()==pickTicketDetail.getItem().getId()
					&& detail.getPackageUnit().getId()==pickTicketDetail.getPackageUnit().getId()
					&& ((pickTicketDetail.getNewLotInfo()==null && detail.getNewLotInfo() == null) 
							|| (detail.getNewLotInfo().equals(pickTicketDetail.getNewLotInfo())))) {
				//是否按单作业模式，关联拣货单的为按单作业模式
				if (WorkModel.PickTicketModel.equals(this.getWorkModel())
						&& detail.getPickTicket().getId() == pickTicketDetail.getPickTicket().getId()) {
					waveDocDetail = detail;
					break;
				}
				if(WorkModel.PickBatchModel.equals(this.getWorkModel())){
					waveDocDetail = detail;
					break;
				}
			}
		}
		if(waveDocDetail==null){
			return;
		}
		this.setBillAllocatedQuantity(this.getBillAllocatedQuantity()+moveQty);
		if(this.getPickedQuantity()==this.getBillAllocatedQuantity()){
			this.setStatus(WaveDocStatus.SPLIT_ALL);
		}else{
			this.setStatus(WaveDocStatus.SPLIT);
		}
	}
	
	/**
	 * 取消分单(不能取消已经发货的数量)
	 */
	public void cancelSplit(double bolQty){
//		for(WaveDocDetail detail:this.getWaveDocDetails()){
//			detail.setBillAllocatedQuantity(bolQty);
//		}
		this.setBillAllocatedQuantity(this.getBillAllocatedQuantity()-bolQty);
		if(this.getBillAllocatedQuantity()>0.0){
			this.setStatus(WaveDocStatus.SPLIT);
		}else{
			if(this.orderQuantity==this.pickedQuantity){
				this.setStatus(WaveDocStatus.PICK_ALL);
			}else{
				this.setStatus(WaveDocStatus.PICKUP);
			}
		}
	}
	
	public Date getCreateDate() {
		return createDate;
	}
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}
	public String getTurnOverMode() {
		return turnOverMode;
	}
	public void setTurnOverMode(String turnOverMode) {
		this.turnOverMode = turnOverMode;
	}
	
	public boolean isWaveWorkModel(){
		return WorkModel.PickBatchModel.equals(this.getWorkModel());
	}
	
	public void addQuantity(PackageUnit packageUnit, double qty) {
		this.orderQuantity += packageUnit.getConvertFigure() * qty;
		this.weight += packageUnit.getWeight() * qty;
		this.volumn += packageUnit.getVolume() * qty;
	}
}
