/******************************************************************************
 * Product: Adempiere ERP & CRM Smart Business Solution                       *
 * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
 * This program is free software; you can redistribute it and/or modify it    *
 * under the terms version 2 of the GNU General Public License as published   *
 * by the Free Software Foundation. This program 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 this program; if not, write to the Free Software Foundation, Inc.,    *
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.                     *
 * For the text or an alternative of this public license, you may reach us    *
 * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA        *
 * or via info@compiere.org or http://www.compiere.org/license.html           *
 *****************************************************************************/
package org.adempiere.webui.panel;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.logging.Level;

import org.adempiere.webui.ClientInfo;
import org.adempiere.webui.component.Tabpanel;
import org.adempiere.webui.component.ToolBarButton;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.component.ZkCssHelper;
import org.adempiere.webui.part.WindowContainer;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.adempiere.webui.window.InfoSchedule;
import org.compiere.model.MAssignmentSlot;
import org.compiere.model.ScheduleUtil;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.calendar.Calendars;
import org.zkoss.calendar.event.CalendarsEvent;
import org.zkoss.calendar.impl.SimpleCalendarEvent;
import org.zkoss.calendar.impl.SimpleCalendarModel;
import org.zkoss.util.Locales;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Borderlayout;
import org.zkoss.zul.Button;
import org.zkoss.zul.Label;
import org.zkoss.zul.North;
import org.zkoss.zul.South;

/**
 *	Window to view and manage scheduling of resources (S_Resource and S_ResourceAssignment)
 *
 * 	@author 	Jorg Janke
 * 
 *  Zk Port
 *  @author Low Heng Sin
 */
public class WSchedule extends Window implements EventListener<Event>
{
	/**
	 * generated serial id
	 */
	private static final long serialVersionUID = -4819513326165148245L;
	private static final String ON_MOBILE_SET_SELECTED_TAB_ECHO = "onMobileSetSelectedTabEcho";

	@SuppressWarnings("unused")
	private InfoSchedule infoSchedule;

	/**
	 *	Constructor
	 *  @param is InfoSchedule for call back
	 */
	public WSchedule (InfoSchedule is)
	{		
		infoSchedule = is;
		
		try
		{
			init();
		}
		catch(Exception e)
		{
			log.log(Level.SEVERE, "VSchedule", e);
		}		
	}	//	WSchedule

	/**	Logger			*/
	private static final CLogger log = CLogger.getCLogger(WSchedule.class);


	private ToolBarButton button;

	@SuppressWarnings("unused")
	private Date m_center;

	private Component calendarContainer;

	private Calendars calendars;
	private SimpleCalendarModel scm;

	private Button btnCurrentDate;
	private Label lblDate;

	private int S_Resource_ID;

	private Component divArrowLeft;

	private Component divArrowRight;

	private Component divTabDay;

	private Component divTabWeek;

	private Component divTabWeekdays;

	private Component divTabMonth;

	private Borderlayout borderlayout;

	private Component btnRefresh;
	
	/**
	 * 	Layout window
	 *  <pre>
	 *  timePanel (West)
	 *  schedulePanel (in schedulePane - Center)
	 *  </pre>
	 * 	@throws Exception
	 */
	private void init() throws Exception
	{
		this.getChildren().clear();
				
		calendarContainer = Executions.createComponents(ThemeManager.getThemeResource("zul/calendar/calendar.zul"), this, null);
		
		borderlayout = (Borderlayout) calendarContainer.getFellow("main");
		borderlayout.setStyle("position: absolute; width: 98%; margin: auto;");
		ZKUpdateUtil.setHeight(borderlayout, "100%");
		if (borderlayout.getSouth() != null) {
			borderlayout.getSouth().detach();
		}

		Component tmp = calendarContainer.getFellow("btnSwitchTimeZone");
		if (tmp != null)
			tmp.detach();		
		tmp = calendarContainer.getFellow("lbxRequestTypes");
		if (tmp != null)
			tmp.getParent().detach();
		tmp = calendarContainer.getFellow("FDOW");
		if (tmp != null)
			tmp.detach();
		
		calendars = (Calendars) calendarContainer.getFellow("cal");
		TimeZone timezone = SessionManager.getAppDesktop().getClientInfo().timeZone;
		calendars.addTimeZone(timezone.getID(), timezone);
		
		calendars.addEventListener(CalendarsEvent.ON_EVENT_CREATE, this);
		calendars.addEventListener(CalendarsEvent.ON_EVENT_EDIT, this);
		calendars.addEventListener(CalendarsEvent.ON_EVENT_UPDATE, this);
		
		this.appendChild(calendarContainer);		
		
		btnCurrentDate = (Button) calendarContainer.getFellow("btnCurrentDate");
		btnCurrentDate.addEventListener(Events.ON_CLICK, this);
		
		btnRefresh = calendarContainer.getFellow("btnRefresh");
		btnRefresh.addEventListener(Events.ON_CLICK, this);
		
		lblDate = (Label) calendarContainer.getFellow("lblDate");
		lblDate.addEventListener(Events.ON_CREATE, this);
		
		divArrowLeft = calendarContainer.getFellow("divArrowLeft");
		divArrowLeft.addEventListener("onMoveDate", this);
		
		divArrowRight = calendarContainer.getFellow("divArrowRight");
		divArrowRight.addEventListener("onMoveDate", this);
		
		divTabDay = calendarContainer.getFellow("divTabDay");
		divTabDay.addEventListener("onUpdateView", this);
		
		divTabWeek = calendarContainer.getFellow("divTabWeek");
		divTabWeek.addEventListener("onUpdateView", this);
		
		divTabWeekdays = calendarContainer.getFellow("divTabWeekdays");
		divTabWeekdays.addEventListener("onUpdateView", this);
		
		divTabMonth = calendarContainer.getFellow("divTabMonth");
		divTabMonth.addEventListener("onUpdateView", this);
		
		button = new ToolBarButton();
		button.setLabel("Edit");
		button.setStyle("visibility: hidden; height: 0px; width: 0px");
		button.addEventListener(Events.ON_CLICK, this);
		this.appendChild(button);
		
		divTabClicked(7);
		
		addEventListener("onAfterReCreate", this);
		
		if (ClientInfo.isMobile()) {
			addCallback(AFTER_PAGE_ATTACHED, t -> afterPageAttached());
			addEventListener(ON_MOBILE_SET_SELECTED_TAB_ECHO, evt -> calendars.invalidate());
		}
	}

	private void afterPageAttached() {
		Component p = getParent();
		while (p != null) {
			if (p instanceof Tabpanel) {
				p.addEventListener(WindowContainer.ON_MOBILE_SET_SELECTED_TAB, evt -> onMobileSelected());
				break;
			}
			p = p.getParent();
		}
	}

	private void onMobileSelected() {
		Events.echoEvent(ON_MOBILE_SET_SELECTED_TAB_ECHO, this, null);
	}

	/**
	 * 	Recreate View
	 * 	@param S_Resource_ID Resource
	 * 	@param date Date
	 */
	public void recreate (int S_Resource_ID, Date date)
	{
		this.S_Resource_ID = S_Resource_ID;
		calendars.setCurrentDate(date);
		
		Events.echoEvent("onAfterReCreate", this, null);
	}	//	recreate

	/**
	 * Update calendar model ({@link #scm})
	 */
	private void updateModel() {
		ScheduleUtil m_model = new ScheduleUtil (Env.getCtx());
		
		//		Calculate Start Day
		GregorianCalendar cal = new GregorianCalendar();
		cal.setTime(calendars.getCurrentDate());
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.add(Calendar.DAY_OF_MONTH, -7);
		Timestamp startDate = new Timestamp(cal.getTimeInMillis());
		//	Calculate End Date
		cal.add(Calendar.MONTH, 1);
		cal.add(Calendar.DAY_OF_MONTH, 14);
		Timestamp endDate = new Timestamp (cal.getTimeInMillis());
		
		scm = new SimpleCalendarModel();
		if (S_Resource_ID > 0) {
			MAssignmentSlot[] list = m_model.getAssignmentSlots (S_Resource_ID, startDate, endDate, null, true, null);
			
			for(MAssignmentSlot mas : list) {
				SimpleCalendarEvent event = new SimpleCalendarEvent();
				event.setBeginDate(mas.getStartTime());
				event.setEndDate(mas.getEndTime());
				event.setTitle(mas.getName());
				event.setContent(mas.getDescription() != null ? mas.getDescription() : mas.getName());
				event.setHeaderColor('#'+ZkCssHelper.createHexColorString(mas.getColor(true)));
				event.setContentColor('#'+ZkCssHelper.createHexColorString(mas.getColor(true)));
				if (!mas.isAssignment() || mas.getMAssignment().isConfirmed())
					event.setLocked(true);
				scm.add(event);
			}
		}
		
		calendars.setModel(scm);
	}
	
	/**
	 * @return SimpleCalendarModel
	 */
	public SimpleCalendarModel getModel() {
		return scm;
	}
	
	@Override
	public void onEvent(Event event) throws Exception {
		String type = event.getName();
		if (type.equals(Events.ON_CLICK)) {
			if (event.getTarget() == btnCurrentDate) {
				btnCurrentDateClicked();
			} else if (event.getTarget() == btnRefresh) {
				updateModel();
			}
		} else if (type.equals(Events.ON_CREATE)) {
			if (event.getTarget() == lblDate)
				updateDateLabel();
		} else if (type.equals("onMoveDate")) {
			if (event.getTarget() == divArrowLeft)
				divArrowClicked(false);
			else if (event.getTarget() == divArrowRight)
				divArrowClicked(true);
		} else if (type.equals("onUpdateView")) {
			String text = String.valueOf(event.getData());
			int days = Msg.getMsg(Env.getCtx(),"Day").equals(text) ? 1: Msg.getMsg(Env.getCtx(),"5Days").equals(text) ? 5: Msg.getMsg(Env.getCtx(),"Week" ).equals(text) ? 7: 0;
			divTabClicked(days);
		} else if (type.equals("onAfterReCreate")) {
			updateModel();
		} else {
			Events.sendEvent(this, event);
		}
	}
	
	/**
	 * Select current day
	 */
	private void btnCurrentDateClicked() {
		calendars.setCurrentDate(Calendar.getInstance(calendars.getDefaultTimeZone()).getTime());
		updateDateLabel();
		updateModel();
	}
	
	/**
	 * Update label after calendar selection change
	 */
	private void updateDateLabel() {
		Date b = calendars.getBeginDate();
		Date e = calendars.getEndDate();
		SimpleDateFormat sdfV = new SimpleDateFormat("yyyy/MMM/dd", Locales.getCurrent());
		sdfV.setTimeZone(calendars.getDefaultTimeZone());
		lblDate.setValue(sdfV.format(b) + " - " + sdfV.format(e));
	}
	
	/**
	 * Move to next/previous calendar page
	 * @param isNext
	 */
	private void divArrowClicked(boolean isNext) {
		if (isNext)
			calendars.nextPage();
		else
			calendars.previousPage();
		updateDateLabel();
		updateModel();
	}
	
	/**
	 * select different calendar tab/presentation
	 * @param days
	 */
	private void divTabClicked(int days) {		
		if (days > 0) {
			calendars.setMold("default");
			calendars.setDays(days);
		} else {
			calendars.setMold("month");
		}
		updateDateLabel();
		updateModel();
	}

	/**
	 * Add pane to north of {@link #borderlayout}
	 * @param pane
	 */
	public void addNorthPane(Component pane) {
		if (borderlayout != null) {
			if (borderlayout.getNorth() != null) {
				borderlayout.getNorth().detach();
			}
			North north = new North();
			north.appendChild(pane);
			borderlayout.appendChild(north);
		}
	}
	
	/**
	 * Add pane to south of {@link #borderlayout}
	 * @param pane
	 * @param height
	 */
	public void addSouthPane(Component pane, String height) {
		if (borderlayout != null) {
			if (borderlayout.getSouth() != null) {
				borderlayout.getSouth().detach();
			}
			South south = new South();
			south.appendChild(pane);
			if (!Util.isEmpty(height)) {
				ZKUpdateUtil.setHeight(south, height);
			}
			borderlayout.appendChild(south);
		}
	}
	
	/**
	 * Remove {@link #btnRefresh}
	 */
	public void removeRefreshButton() {
		if (btnRefresh != null) {
			btnRefresh.detach();
			btnRefresh = null;
		}
	}
}	//	WSchedule
