package eu.dnetlib.client.loader;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Logger;

import javax.inject.Inject;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Singleton;

import eu.dnetlib.client.GreetingService;
import eu.dnetlib.client.GreetingServiceAsync;
import eu.dnetlib.client.LoadService;
import eu.dnetlib.client.LoadServiceAsync;
import eu.dnetlib.client.loader.ReloadListener.errorCodes;
import eu.dnetlib.client.shared.StartUpComponents;
import eu.dnetlib.client.updaters.Updater;
import eu.dnetlib.efg1914.authoring.components.DeletedComponent;
import eu.dnetlib.efg1914.authoring.components.Frame;
import eu.dnetlib.efg1914.authoring.components.Item;
import eu.dnetlib.efg1914.authoring.components.Theme;
import eu.dnetlib.efg1914.authoring.components.Topic;
import eu.dnetlib.efg1914.authoring.users.User;

@Singleton
public class ComponentLoaderImpl implements ComponentLoader {

	private static Logger log = Logger.getLogger("ComponentLoader ");
	@Inject
	private StartUpComponents startupComponents;
	private int WAIT_TIME;

	// reload every
	// 600000 -- for ten minutes
	// 120000 2min
	private LoadServiceAsync loadservice = GWT.create(LoadService.class);

	private List<ReloadListener> reloadListenerAdapters = new ArrayList<ReloadListener>();
	private GreetingServiceAsync greetingService = GWT.create(GreetingService.class);

	private int reloadInstance = 0;
	@Inject
	private Updater updater;
	private Timer timer = null;
	private String updateTime = "2013-11-11 12:30:02";
	private String lastUpdateTime = "2013-11-11 12:30:02";

	@Inject
	public ComponentLoaderImpl() {

	}

	public void setUser(User user) {
		this.startupComponents.setUser(user);
	}

	public void init() {
		reloadListenerAdapters.clear();
		reloadInstance = 0;
		timer = new Timer() {
			@Override
			public void run() {
				reload();

			}
		};

		timer.schedule(1);

	}

	public void reload() {

		log.info("Attempting to loaaad");

		if (reloadInstance != 0) {

			log.info("\n\n~~~~~~~~~~~~~~~~~~~~~Reload AFTER~~~~~~~~~~~~~~~~~~~~~~ ");
 			lastUpdateTime = new String(updateTime);
			DateTimeFormat fmt = DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss ");
			updateTime = new String(fmt.format(new Date()));

			log.info("Reload AFTER " + lastUpdateTime);
			loadservice.loadAfter(this.startupComponents.getUser(), lastUpdateTime, new AsyncCallback<StartUpComponents>() {
				public void onSuccess(StartUpComponents comp) {

					log.info("refreshing items...");

					updateComponents(comp);
					log.info(" ~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ \n\n\n");

					for (ReloadListener listener : reloadListenerAdapters) {
						listener.reload();

					}

					reloadInstance++;

				}

				public void onFailure(Throwable arg0) {
					log.warning("fail refreshing items...");

					for (ReloadListener listener : reloadListenerAdapters) {
						// if it fails reload from lastupdate time
						// not current time
						arg0.printStackTrace();

						updateTime = new String(lastUpdateTime);

						if (reloadInstance == 0)

						{
							listener.error(errorCodes.FIRST_LOAD_ERROR);

						} else {
							listener.error(errorCodes.RELOAD_ERROR);

						}

					}
					log.warning("fail refreshing items...");

					arg0.printStackTrace();
					log.warning("fail refreshing items..." + arg0.getLocalizedMessage());

				}

			});
		} else {
			DateTimeFormat fmt = DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss ");
			updateTime = fmt.format(new Date());
			log.info("Load " + updateTime);

			lastUpdateTime = new String(updateTime);
			loadservice.load(this.startupComponents.getUser(), new AsyncCallback<StartUpComponents>() {
				public void onSuccess(StartUpComponents comp) {

					log.info("loading items...");

					fillStartupComponents(comp);
					// set time between runs of reloading script
					WAIT_TIME = comp.getReloadTime();
					timer.scheduleRepeating(WAIT_TIME);
					log.info("timer " + WAIT_TIME);

					for (ReloadListener listener : reloadListenerAdapters) {

						listener.load();

					}

					reloadInstance++;

				}

				public void onFailure(Throwable arg0) {
					log.warning("fail refreshing items...");
					for (ReloadListener listener : reloadListenerAdapters) {

						if (reloadInstance == 0)

						{
							listener.error(errorCodes.FIRST_LOAD_ERROR);

						} else {
							listener.error(errorCodes.RELOAD_ERROR);

						}

					}
					arg0.printStackTrace();
				}

			});
		}
	}

	public void stop() {
		timer.cancel();
		reloadInstance = 0;
		this.startupComponents.clearLists();
		log.info("Reloading thread stopped...");
	}

	private void fillStartupComponents(StartUpComponents comp) {
		this.startupComponents.clearLists();
		this.startupComponents.getAllItems().putAll(comp.getAllItems());
		this.startupComponents.getAllFrames().putAll(comp.getAllFrames());
		this.startupComponents.getAllTopics().putAll(comp.getAllTopics());
		this.startupComponents.getAllThemes().putAll(comp.getAllThemes());
		this.startupComponents.getArchives().putAll(comp.getArchives());
		this.startupComponents.getCurators().putAll(comp.getCurators());

		this.startupComponents.getPermissions().putAll(comp.getPermissions());
		this.startupComponents.setServerPath(comp.getServerPath());
		this.startupComponents.setPreviewPage(comp.getPreviewPage());
		this.startupComponents.setConfiguration(comp.getConfiguration());
		this.startupComponents.setMyLockedComponentList(new ArrayList<String>());
		this.startupComponents.getAliasIds().putAll(comp.getAliasIds());
	
	}

	private void updateComponents(StartUpComponents newComponents) {
		try {
			this.startupComponents.setPreviewPage(newComponents.getPreviewPage());
			this.startupComponents.setAliasIds(newComponents.getAliasIds());

			if (newComponents.getConfiguration() != null) {
				log.info("config is updated!!");
				updater.updateConfiguration(newComponents.getConfiguration(), startupComponents.getUser().getId(), false);
			}
			// ITEMS
			log.info("~~~~~~~~~~~~updated items:::: " + newComponents.getAllItems().size());
 
			List<String> array = new ArrayList<String>();
			for (Item item : newComponents.getAllItems().values()) {

				log.info("item lastmodified--->" + item.getHeader().getLastModified());
				if (startupComponents.getAllItems().get(item.getId()) != null) {
					updater.updateItem(item, startupComponents.getUser().getId(), false);
					array.add(item.getId());
				}
			}
			for (String str : array) {
				newComponents.getAllItems().remove(str);
			}
			for (Entry<String, Item> e : newComponents.getAllItems().entrySet()) {
				updater.createNewItem(startupComponents.getUser().getId(), e.getValue(), false);
			}
			// FRAMES
			log.info("~~~~~~~~~~~~updated frames(+intro frames):::: " + newComponents.getAllFrames().size());
 			array = new ArrayList<String>();
			for (Frame frame : newComponents.getAllFrames().values()) {
				log.info("frame lastmodified--->" + frame.getHeader().getLastModified());
				// a frame can be an itro frame for topic or
				// theme if has template
				// "intro" or "ft0"
				if (!frame.getTemplate().equals("intro") && !frame.getTemplate().equals("ft0") && startupComponents.getAllFrames().get(frame.getId()) != null) {
					updater.updateFrame(frame, startupComponents.getUser().getId(), false);
					array.add(frame.getId());
				}
			}
			for (String str : array) {
				newComponents.getAllFrames().remove(str);
			}
			for (Entry<String, Frame> e : newComponents.getAllFrames().entrySet()) {
				if (!e.getValue().getTemplate().equals("intro") && !e.getValue().getTemplate().equals("ft0")) {
					updater.createNewFrame(startupComponents.getUser().getId(), e.getValue(), false);
				}
			}
			// TOPICS
			log.info("~~~~~~~~~~~~updated topics:::: " + newComponents.getAllTopics().size());
			array = new ArrayList<String>();
 
			for (Topic t : newComponents.getAllTopics().values()) {
				log.info("topic lastmodified--->" + t.getHeader().getLastModified());
				if (startupComponents.getAllTopics().get(t.getId()) != null) {
					updater.updateTopic(t, startupComponents.getUser().getId(), false);
					array.add(t.getId());
				}
			}
			for (String str : array) {
				newComponents.getAllTopics().remove(str);

			}
			for (Entry<String, Topic> e : newComponents.getAllTopics().entrySet()) {
				updater.createNewTopic(startupComponents.getUser().getId(), e.getValue(), false);
			}
			// THEMES
			log.info("~~~~~~~~~~~~updated themes:::: " + newComponents.getAllThemes().size());
 
			array = new ArrayList<String>();
			for (Theme t : newComponents.getAllThemes().values()) {
				if (startupComponents.getAllThemes().get(t.getId()) != null) {
					log.info("theme lastmodified--->" + t.getHeader().getLastModified());
					updater.updateTheme(t, startupComponents.getUser().getId(), false);
					array.add(t.getId());
				}
			}
			for (String str : array) {
				newComponents.getAllThemes().remove(str);
			}
			for (Entry<String, Theme> e : newComponents.getAllThemes().entrySet()) {
				updater.createNewTheme(startupComponents.getUser().getId(), e.getValue(), false);
			}
			log.info("~~~~~~~~~~~~updated users:::: " + newComponents.getCurators().size());
 
			// TODO
			array = new ArrayList<String>();
			for (Entry<String, String> e : newComponents.getCurators().entrySet()) {
				log.info("user lastmodified--->" + e.getValue());
				if (startupComponents.getCurators().get(e.getKey()) != null) {
					String userName = e.getValue();
					greetingService.getUserById(e.getKey(), new AsyncCallback<User>() {
						
						public void onSuccess(User user) {
							 updater.updateUser(user, startupComponents.getUser().getId(), false);							
						}
						
						public void onFailure(Throwable arg0) {
							log.info("Fail to reload user....");								
						}
					});
					 
					array.add(e.getKey());
				}
			}
			for (String s : array) {
				newComponents.getCurators().remove(s);

			}
			for (Entry<String, String> e : newComponents.getCurators().entrySet()) {
				greetingService.getUserById(e.getKey(), new AsyncCallback<User>() {
					
					public void onSuccess(User user) {
						 updater.createUser(user, startupComponents.getUser().getId(), false);							
					}
					
					public void onFailure(Throwable arg0) {
						log.info("Fail to reload user....");								
					}
				});
 			}
			log.info("~~~~~~~~~~~~deleted components:::: " + newComponents.getDeletedComponents().size());

			// DELETES
			for (DeletedComponent deletedComponent : newComponents.getDeletedComponents()) {
				if (deletedComponent.getComponentType().toLowerCase().contains("item") && startupComponents.getAllItems().get(deletedComponent.getComponentId()) != null) {
					updater.deleteItem(startupComponents.getAllItems().get(deletedComponent.getComponentId()), startupComponents.getUser().getId(), false);
				} else if (deletedComponent.getComponentType().toLowerCase().contains("frame") && startupComponents.getAllFrames().get(deletedComponent.getComponentId()) != null) {
					updater.deleteFrame(startupComponents.getAllFrames().get(deletedComponent.getComponentId()), startupComponents.getUser().getId(), false);

				} else if (deletedComponent.getComponentType().toLowerCase().contains("topic") && startupComponents.getAllTopics().get(deletedComponent.getComponentId()) != null) {
					updater.deleteTopic(startupComponents.getAllTopics().get(deletedComponent.getComponentId()), startupComponents.getUser().getId(), false);

				} else if (deletedComponent.getComponentType().toLowerCase().contains("theme") && startupComponents.getAllThemes().get(deletedComponent.getComponentId()) != null) {
					updater.deleteTheme(startupComponents.getAllThemes().get(deletedComponent.getComponentId()), startupComponents.getUser().getId(), false);

				} else if (deletedComponent.getComponentType().toLowerCase().contains("user") && startupComponents.getCurators().get(deletedComponent.getComponentId()) != null) {
					updater.deleteUser(deletedComponent.getComponentId(), startupComponents.getUser().getId(), false);

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.warning(e.getMessage() + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa  error in update!!!!!!!!!!!!!!!!!!!!!!!!!");
		}
	}

	public StartUpComponents getStartupComponents() {
		return startupComponents;
	}

	public void setStartupComponents(StartUpComponents startupComponents) {
		this.startupComponents = startupComponents;
	}

	public List<ReloadListener> getReloadListenerAdapters() {
		return reloadListenerAdapters;
	}

	public void setReloadListenerAdapters(List<ReloadListener> reloadListenerAdapters) {
		this.reloadListenerAdapters = reloadListenerAdapters;
	}

	public void addReloadListener(ReloadListener reloadListener) {
		getReloadListenerAdapters().add(reloadListener);
	}

}
