/**
 * This software was developed for use in the SWIM Terminal Data Distribution System (STDDS) 
 * for DOT/RITA/Volpe and is For Official Use Only. 
**/

package gov.faa.swim.stdds.webmonitor.controller.parser;

import java.io.StringReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import gov.faa.swim.stdds.webmonitor.data.StateValue;
import gov.faa.swim.stdds.webmonitor.data.StatusValue;
import gov.faa.swim.stdds.webmonitor.data.notification.Notification;
import gov.faa.swim.stdds.webmonitor.data.notification.NotificationRepository;
import gov.faa.swim.stdds.webmonitor.data.notification.NotificationType;
import gov.faa.swim.stdds.webmonitor.data.tracon.Connection;
import gov.faa.swim.stdds.webmonitor.data.tracon.Link;
import gov.faa.swim.stdds.webmonitor.data.tracon.Service;
import gov.faa.swim.stdds.webmonitor.data.tracon.Tracon;
import gov.faa.swim.stdds.webmonitor.data.tracon.TraconRepository;
import gov.faa.swim.stdds.webmonitor.util.Constants;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.ConnectionState;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.ConnectionStatus;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.Connections;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.ExternalLink;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.LinkMode;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.LinkStatus;
import us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.ServiceStatus;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.ASDEXExternalLinks;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.AirportDataServiceStatus;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.EFSTSExternalLinks;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.RVRExternalLinks;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.STARSExternalLinks;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.SurfaceMovementEventServiceStatus;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.TDLSExternalLinks;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.TerminalAutomationInformationServiceStatus;
import us.gov.dot.faa.atm.terminal.entities.v4_0.status.TowerDepartureEventServiceStatus;

/**
 * StatusMessageParserV4 implements the StatusMessageParser for version 4 of the
 * schemas
 */
public class StatusMessageParserV4 extends StatusMessageParser {

	private static final Logger log = LoggerFactory.getLogger(StatusMessageParserV4.class);

	/** Version number that this parser works for */
	public static final String VERSION_NUMBER = "4.0";

	/**
	 * Constructor
	 * 
	 * @param traconRepo
	 * @param notificationRepo
	 */
	public StatusMessageParserV4(TraconRepository traconRepo, NotificationRepository notificationRepo) {
		super(traconRepo, notificationRepo);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.faa.swim.stdds.webmonitor.controller.parser.StatusMessageParser#
	 * parseTAISStatusMessage(gov.faa.swim.stdds.webmonitor.data.tracon.Tracon,
	 * java.lang.String)
	 */
	@Override
	protected void parseTAISStatusMessage(Tracon tracon, String messageText) {
		long timestamp = System.currentTimeMillis();

		TerminalAutomationInformationServiceStatus taisMessage = null;
		try {
			JAXBContext context = JAXBContext.newInstance(TerminalAutomationInformationServiceStatus.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			StringReader reader = new StringReader(messageText);
			taisMessage = (TerminalAutomationInformationServiceStatus) unmarshaller.unmarshal(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Service service = tracon.getService(Constants.TAIS);
		if ((service == null) && (DYNAMIC_MODE)) {
			service = new Service();
			service.setName(Constants.TAIS);
			tracon.addService(service);
		}
		if (service != null) {
			service.setTimeStamp(timestamp);
			STARSExternalLinks starsLinks = taisMessage.getStarsLinks();
			if (starsLinks != null) {
				for (ExternalLink exLink : starsLinks.getStarsLink()) {
					handleLink(timestamp, service, tracon.getName(), exLink);
				}
			}
			// Don't set the service status if not configured for override
			boolean changed = false;
			if (!OVERRIDE_STATUS) {
				changed = service.setStatus(getMessageStatus(taisMessage.getServiceStatus()));
			} else {
				changed = service.refreshStatus();
			}
			if (changed) {
				notificationRepo.save(new Notification(timestamp, service.getStatus(),
						service.getName(), tracon.getName(), NotificationType.SERVICE));
			}
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.faa.swim.stdds.webmonitor.controller.parser.StatusMessageParser#
	 * parseSMESStatusMessage(gov.faa.swim.stdds.webmonitor.data.tracon.Tracon,
	 * java.lang.String)
	 */
	@Override
	protected void parseSMESStatusMessage(Tracon tracon, String messageText) {
		long timestamp = System.currentTimeMillis();

		SurfaceMovementEventServiceStatus smesMessage = null;
		try {
			JAXBContext context = JAXBContext.newInstance(SurfaceMovementEventServiceStatus.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			StringReader reader = new StringReader(messageText);
			smesMessage = (SurfaceMovementEventServiceStatus) unmarshaller.unmarshal(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Service service = tracon.getService(Constants.SMES);
		if ((service == null) && (DYNAMIC_MODE)) {
			service = new Service();
			service.setName(Constants.SMES);
			tracon.addService(service);
		}
		if (service != null) {
			service.setTimeStamp(timestamp);
			ASDEXExternalLinks asdexLinks = smesMessage.getAsdexLinks();
			if (asdexLinks != null) {
				for (ExternalLink exLink : asdexLinks.getAsdexLink()) {
					handleLink(timestamp, service, tracon.getName(), exLink);
				}
			}
			// Don't set the service status if not configured for override
			boolean changed = false;
			if (!OVERRIDE_STATUS) {
				changed = service.setStatus(getMessageStatus(smesMessage.getServiceStatus()));
			} else {
				changed = service.refreshStatus();
			}
			if (changed) {
				notificationRepo.save(new Notification(timestamp, service.getStatus(),
						service.getName(), tracon.getName(), NotificationType.SERVICE));
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.faa.swim.stdds.webmonitor.controller.parser.StatusMessageParser#
	 * parseTDESStatusMessage(gov.faa.swim.stdds.webmonitor.data.tracon.Tracon,
	 * java.lang.String)
	 */
	@Override
	protected void parseTDESStatusMessage(Tracon tracon, String messageText) {
		long timestamp = System.currentTimeMillis();

		TowerDepartureEventServiceStatus taisMessage = null;
		try {
			JAXBContext context = JAXBContext.newInstance(TowerDepartureEventServiceStatus.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			StringReader reader = new StringReader(messageText);
			taisMessage = (TowerDepartureEventServiceStatus) unmarshaller.unmarshal(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Service service = tracon.getService(Constants.TDES);
		if ((service == null) && (DYNAMIC_MODE)) {
			service = new Service();
			service.setName(Constants.TDES);
			tracon.addService(service);
		}
		if (service != null) {
			service.setTimeStamp(timestamp);
			EFSTSExternalLinks efstsLinks = taisMessage.getEfstsLinks();
			if (efstsLinks != null) {
				for (ExternalLink exLink : efstsLinks.getEfstsLink()) {
					handleLink(timestamp, service, tracon.getName(), exLink);
				}
			}

			TDLSExternalLinks tdlsLinks = taisMessage.getTdlsLinks();
			if (tdlsLinks != null) {
				for (ExternalLink exLink : tdlsLinks.getTdlsLink()) {
					handleLink(timestamp, service, tracon.getName(), exLink);
				}
			}

			// Don't set the service status if not configured for override
			boolean changed = false;
			if (!OVERRIDE_STATUS) {
				changed = service.setStatus(getMessageStatus(taisMessage.getServiceStatus()));
			} else {
				changed = service.refreshStatus();
			}
			if (changed) {
				notificationRepo.save(new Notification(timestamp, service.getStatus(),
						service.getName(), tracon.getName(), NotificationType.SERVICE));
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.faa.swim.stdds.webmonitor.controller.parser.StatusMessageParser#
	 * parseAPDSStatusMessage(gov.faa.swim.stdds.webmonitor.data.tracon.Tracon,
	 * java.lang.String)
	 */
	@Override
	protected void parseAPDSStatusMessage(Tracon tracon, String messageText) {
		long timestamp = System.currentTimeMillis();

		AirportDataServiceStatus apdsMessage = null;
		try {
			JAXBContext context = JAXBContext.newInstance(AirportDataServiceStatus.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			StringReader reader = new StringReader(messageText);
			apdsMessage = (AirportDataServiceStatus) unmarshaller.unmarshal(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Service service = tracon.getService(Constants.APDS);
		if ((service == null) && (DYNAMIC_MODE)) {
			service = new Service();
			service.setName(Constants.APDS);
			tracon.addService(service);
		}
		if (service != null) {
			service.setTimeStamp(timestamp);
			RVRExternalLinks rvrLinks = apdsMessage.getRvrLinks();
			if (rvrLinks != null) {
				for (ExternalLink exLink : rvrLinks.getRvrLink()) {
					handleLink(timestamp, service, tracon.getName(), exLink);
				}
			}
			// Don't set the service status if not configured for override
			boolean changed = false;
			if (!OVERRIDE_STATUS) {
				changed = service.setStatus(getMessageStatus(apdsMessage.getServiceStatus()));
			} else {
				changed = service.refreshStatus();
			}
			if (changed) {
				notificationRepo.save(new Notification(timestamp, service.getStatus(),
						service.getName(), tracon.getName(), NotificationType.SERVICE));
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.faa.swim.stdds.webmonitor.controller.parser.StatusMessageParser#
	 * parseSTDDSStatusMessage(gov.faa.swim.stdds.webmonitor.data.tracon.Tracon,
	 * java.lang.String)
	 */
	@Override
	protected void parseSTDDSStatusMessage(Tracon traconObject, String messageText) {
		// TODO Auto-generated method stub

	}

	/**
	 * Translate xml type ServiceStatus to internal StatusType
	 * 
	 * @param originalStatus
	 * @return StatusType
	 */
	private static StatusValue getMessageStatus(ServiceStatus originalStatus) {
		if (originalStatus == ServiceStatus.AVAILABLE) {
			return StatusValue.NORMAL;
		} else if (originalStatus == ServiceStatus.DEGRADED) {
			return StatusValue.DEGRADED;
		} else if (originalStatus == ServiceStatus.UNAVAILABLE) {
			return StatusValue.FAILED;
		} else {
			return StatusValue.UNKNOWN;
		}
	}

	/**
	 * Translate xml type LinkStatus to internal StatusType
	 * 
	 * @param originalStatus
	 * @return StatusType
	 */
	private static StatusValue getLinkStatus(LinkStatus originalStatus) {
		if (originalStatus == LinkStatus.NORMAL) {
			return StatusValue.NORMAL;
		} else if (originalStatus == LinkStatus.DEGRADED) {
			return StatusValue.DEGRADED;
		} else if (originalStatus == LinkStatus.FAILED) {
			return StatusValue.FAILED;
		} else if (originalStatus == LinkStatus.UNKNOWN) {
			return StatusValue.UNKNOWN;
		} else {
			return StatusValue.UNKNOWN;
		}
	}

	/**
	 * Translate xml type ConnectionStatus to internal StatusType
	 * 
	 * @param originalStatus
	 * @return StatusType
	 */
	private static StatusValue getConnectionStatus(ConnectionStatus originalStatus) {
		if (originalStatus == ConnectionStatus.NORMAL) {
			return StatusValue.NORMAL;
		} else if (originalStatus == ConnectionStatus.FAILED) {
			return StatusValue.FAILED;
		} else {
			return StatusValue.UNKNOWN;
		}
	}

	/**
	 * Translate xml type ConnectionState to internal StateType
	 * 
	 * @param originalState
	 * @return StateType
	 */
	private StateValue getConnectionState(ConnectionState originalState) {
		if (originalState == ConnectionState.ACTIVE) {
			return StateValue.ACTIVE;
		} else if (originalState == ConnectionState.STANDBY) {
			return StateValue.STANDBY;
		}
		return StateValue.UNKNOWN;
	}

	/**
	 * Handle setting an individual link's status
	 * 
	 * @param timestamp
	 *            - the timestamp
	 * @param service
	 *            - the service
	 * @param traconID
	 *            - the tracon Id
	 * @param exLink
	 *            - the external link
	 */
	private void handleLink(long timestamp, Service service, String traconID, ExternalLink exLink) {
		String lName = exLink.getName();
		Link link = service.getLink(lName);
		if ((link == null) && (DYNAMIC_MODE)) {
			link = new Link();
			link.setName(lName);
			link.setTraconID(traconID);
			service.addLink(link);
		}
		if (link != null) {
			boolean maintenance = isLinkMaintenance(lName, timestamp);

			LinkMode mode = exLink.getLinkMode();
			StatusValue status = getLinkStatus(exLink.getLinkStatus());
			if (maintenance && (status != StatusValue.NORMAL)) {
				status = StatusValue.MAINTENANCE;
			}

			// handle mode
			if (mode == LinkMode.OFFLINE) {
				status = StatusValue.OFFLINE;
			} else if (mode == LinkMode.MAINTENANCE) {
				status = StatusValue.MAINTENANCE;
			}

			boolean changed = link.setStatus(status);
			if (changed) {
				notificationRepo
						.save(new Notification(timestamp, status, link.getName(), traconID, NotificationType.LINK));
			}
			link.setTimeStamp(timestamp);

			Connections messageConnections = exLink.getConnections();
			if (messageConnections != null) {
				for (us.gov.dot.faa.atm.terminal.entities.v4_0.commontypes.Connection msgConnection : messageConnections
						.getConnection()) {
					String cName = msgConnection.getName();
					Connection connection = link.getConnection(cName);
					if ((connection == null) && (DYNAMIC_MODE)) {
						connection = new Connection();
						connection.setName(cName);
						connection.setTraconID(traconID);
						link.addConnection(connection);
					}
					if (connection != null) {
						LinkMode connectionMode = msgConnection.getConnectionMode();
						StatusValue connectionStatus = getConnectionStatus(msgConnection.getConnectionStatus());
						StateValue connectionState = getConnectionState(msgConnection.getConnectionState());
						if (maintenance && (connectionStatus != StatusValue.NORMAL)) {
							connectionStatus = StatusValue.MAINTENANCE;
						}

						// handle mode
						if (connectionMode == LinkMode.OFFLINE) {
							connectionStatus = StatusValue.OFFLINE;
						} else if (connectionMode == LinkMode.MAINTENANCE) {
							connectionStatus = StatusValue.MAINTENANCE;
						}

						connection.setStatus(connectionStatus);
						connection.setState(connectionState);
						connection.setTimeStamp(timestamp);
					} else {
						log.error("No adapted connection found with the name " + cName);
					}
				}
			}

		} else {
			log.error("No adapted link found with the name " + lName);
		}
	}

}