content: /*******************************************************************************
  * Copyright (c) 2013-2015 IBM Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *******************************************************************************/
  package com.acmeair.wxs.service;
  
  import java.math.BigDecimal;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import javax.annotation.PostConstruct;
  import javax.inject.Inject;
  
  import com.acmeair.entities.AirportCodeMapping;
  import com.acmeair.entities.Flight;
  import com.acmeair.entities.FlightSegment;
  import com.acmeair.service.BookingService;
  import com.acmeair.service.DataService;
  import com.acmeair.service.FlightService;
  import com.acmeair.service.KeyGenerator;
  import com.acmeair.wxs.WXSConstants;
  import com.acmeair.wxs.entities.AirportCodeMappingImpl;
  import com.acmeair.wxs.entities.FlightImpl;
  import com.acmeair.wxs.entities.FlightSegmentImpl;
  import com.acmeair.wxs.utils.WXSSessionManager;
  import com.ibm.websphere.objectgrid.ObjectGrid;
  import com.ibm.websphere.objectgrid.ObjectGridException;
  import com.ibm.websphere.objectgrid.ObjectMap;
  import com.ibm.websphere.objectgrid.Session;
  import com.ibm.websphere.objectgrid.UndefinedMapException;
  import com.ibm.websphere.objectgrid.plugins.TransactionCallbackException;
  import com.ibm.websphere.objectgrid.plugins.index.MapIndex;
  import com.ibm.websphere.objectgrid.plugins.index.MapIndexPlugin;
  
  @DataService(name=WXSConstants.KEY,description=WXSConstants.KEY_DESCRIPTION)
  public class FlightServiceImpl extends FlightService implements  WXSConstants {
  
  	private static String FLIGHT_MAP_NAME="Flight";
  	private static String FLIGHT_SEGMENT_MAP_NAME="FlightSegment";
  	private static String AIRPORT_CODE_MAPPING_MAP_NAME="AirportCodeMapping";
  	
  	private static String BASE_FLIGHT_MAP_NAME="Flight";
  	private static String BASE_FLIGHT_SEGMENT_MAP_NAME="FlightSegment";
  	private static String BASE_AIRPORT_CODE_MAPPING_MAP_NAME="AirportCodeMapping";
  	
  	private final static Logger logger = Logger.getLogger(BookingService.class.getName()); 
  	
  	private ObjectGrid og;
  	
  	@Inject
  	KeyGenerator keyGenerator;
  	
  	
  	@PostConstruct
  	private void initialization()  {	
  		try {
  			og = WXSSessionManager.getSessionManager().getObjectGrid();
  			FLIGHT_MAP_NAME = BASE_FLIGHT_MAP_NAME + WXSSessionManager.getSessionManager().getMapSuffix();
  			FLIGHT_SEGMENT_MAP_NAME = BASE_FLIGHT_SEGMENT_MAP_NAME + WXSSessionManager.getSessionManager().getMapSuffix();
  			AIRPORT_CODE_MAPPING_MAP_NAME = BASE_AIRPORT_CODE_MAPPING_MAP_NAME + WXSSessionManager.getSessionManager().getMapSuffix();
  		} catch (ObjectGridException e) {
  			logger.severe("Unable to retreive the ObjectGrid reference " + e.getMessage());
  		}
  	}
  	
  	@Override
  	public Long countFlights() {
  		try {
  			Session session = og.getSession();
  			ObjectMap objectMap = session.getMap(FLIGHT_MAP_NAME);			
  			MapIndex mapIndex = (MapIndex)objectMap.getIndex(MapIndexPlugin.SYSTEM_KEY_INDEX_NAME);			
  			Iterator<?> keyIterator = mapIndex.findAll();
  			Long result = 0L;
  			while(keyIterator.hasNext()) {
  				keyIterator.next(); 
  				result++;
  			}
  			/*
  			int partitions = og.getMap(FLIGHT_MAP_NAME).getPartitionManager().getNumOfPartitions();
  			Long result = 0L;
  			ObjectQuery query = og.getSession().createObjectQuery("SELECT COUNT ( o ) FROM " + FLIGHT_MAP_NAME + " o ");
  			for(int i = 0; i<partitions;i++){
  				query.setPartition(i);
  				result += (Long) query.getSingleResult();
  			}
  			*/
  			return result;
  		} catch (UndefinedMapException e) {
  			e.printStackTrace();
  		} catch (TransactionCallbackException e) {
  			e.printStackTrace();
  		} catch (ObjectGridException e) {
  			e.printStackTrace();
  		}
  		return -1L;
  	}
  	
  	@Override
  	public Long countAirports() {
  		try {
  			Session session = og.getSession();
  			ObjectMap objectMap = session.getMap(AIRPORT_CODE_MAPPING_MAP_NAME);			
  			MapIndex mapIndex = (MapIndex)objectMap.getIndex(MapIndexPlugin.SYSTEM_KEY_INDEX_NAME);			
  			Iterator<?> keyIterator = mapIndex.findAll();
  			Long result = 0L;
  			while(keyIterator.hasNext()) {
  				keyIterator.next(); 
  				result++;
  			}
  			return result;
  		} catch (UndefinedMapException e) {
  			e.printStackTrace();
  		} catch (TransactionCallbackException e) {
  			e.printStackTrace();
  		} catch (ObjectGridException e) {
  			e.printStackTrace();
  		}
  		return -1L;
  	}
  	
  	@Override
  	public Long countFlightSegments() {
  		try {
  			Session session = og.getSession();
  			ObjectMap objectMap = session.getMap(FLIGHT_SEGMENT_MAP_NAME);			
  			MapIndex mapIndex = (MapIndex)objectMap.getIndex(MapIndexPlugin.SYSTEM_KEY_INDEX_NAME);			
  			Iterator<?> keyIterator = mapIndex.findAll();
  			Long result = 0L;
  			while(keyIterator.hasNext()) {
  				keyIterator.next(); 
  				result++;
  			}
  			/*
  			int partitions = og.getMap(FLIGHT_SEGMENT_MAP_NAME).getPartitionManager().getNumOfPartitions();
  			Long result = 0L;
  			ObjectQuery query = og.getSession().createObjectQuery("SELECT COUNT ( o ) FROM " + FLIGHT_SEGMENT_MAP_NAME + " o ");
  			for(int i = 0; i<partitions;i++){
  				query.setPartition(i);
  				result += (Long) query.getSingleResult();
  			}
  			*/			
  			return result;
  
  		} catch (UndefinedMapException e) {
  			e.printStackTrace();
  		} catch (TransactionCallbackException e) {
  			e.printStackTrace();
  		} catch (ObjectGridException e) {
  			e.printStackTrace();
  		}
  		return -1L;
  	}
  	
  	/*
  	public Flight getFlightByFlightKey(FlightPK key) {
  		try {
  			Flight flight;
  			flight = flightPKtoFlightCache.get(key);
  			if (flight == null) {
  				//Session session = sessionManager.getObjectGridSession();
  				Session session = og.getSession();
  				ObjectMap flightMap = session.getMap(FLIGHT_MAP_NAME);
  				@SuppressWarnings("unchecked")
  				HashSet<Flight> flightsBySegment = (HashSet<Flight>)flightMap.get(key.getFlightSegmentId());
  				for (Flight f : flightsBySegment) {
  					if (f.getPkey().getId().equals(key.getId())) {
  						flightPKtoFlightCache.putIfAbsent(key, f);
  						flight = f;
  						break;
  					}
  				}
  			}
  			return flight;
  		} catch (Exception e) {
  			throw new RuntimeException(e);
  		}
  	}
  	*/
  	@Override
  	protected Flight getFlight(String flightId, String flightSegmentId) {
  		try {
  			if(logger.isLoggable(Level.FINER))
  				logger.finer("in WXS getFlight.  search for flightId = '" + flightId + "' and flightSegmentId = '"+flightSegmentId+"'");
  			//Session session = sessionManager.getObjectGridSession();
  			Session session = og.getSession();
  			ObjectMap flightMap = session.getMap(FLIGHT_MAP_NAME);
  			@SuppressWarnings("unchecked")
  			HashSet<FlightImpl> flightsBySegment = (HashSet<FlightImpl>)flightMap.get(flightSegmentId);
  			for (FlightImpl flight : flightsBySegment) {
  				if (flight.getFlightId().equals(flightId)) {
  					return flight;
  				}
  			}
  			logger.warning("No matching flights found for flightId =" + flightId + " and flightSegment " + flightSegmentId);
  			return null;
  		} catch (Exception e) {
  			throw new RuntimeException(e);
  		}
  	}
  
  	@Override
  	protected  FlightSegment getFlightSegment(String fromAirport, String toAirport) {
  		try {
  		Session session = null;
  //		boolean startedTran = false;
  		//session = sessionManager.getObjectGridSession();
  		session = og.getSession();
  		FlightSegment segment = null;
  /*		if (!session.isTransactionActive()) {
  			startedTran = true;
  			session.begin();
  		}
  		*/
  		ObjectMap flightSegmentMap = session.getMap(FLIGHT_SEGMENT_MAP_NAME);
  		@SuppressWarnings("unchecked")
  		HashSet<FlightSegment> segmentsByOrigPort = (HashSet<FlightSegment>)flightSegmentMap.get(fromAirport);
  		if (segmentsByOrigPort!=null) {
  			for (FlightSegment fs : segmentsByOrigPort) {
  				if (fs.getDestPort().equals(toAirport)) {
  					segment = fs;
  					return segment;
  				}
  			}
  		}
  		if (segment == null) {
  			segment = new FlightSegmentImpl(); // put a sentinel value of a non-populated flightsegment
  		}
  //		if (startedTran)
  //			session.commit();
  		
  		return segment;
  		} catch (Exception e) {
  			throw new RuntimeException(e);
  		}
  	}
  	
  	@Override
  	protected  List<Flight> getFlightBySegment(FlightSegment segment, Date deptDate){
  		try {
  		List<Flight> flights = new ArrayList<Flight>();
  		Session session = null;
  		boolean startedTran = false;
  		if (session == null) {
  			//session = sessionManager.getObjectGridSession();
  			session = og.getSession();
  			if (!session.isTransactionActive()) {
  				startedTran = true;
  				session.begin();
  			}
  		}				
  		
  		ObjectMap flightMap = session.getMap(FLIGHT_MAP_NAME);
  		@SuppressWarnings("unchecked")
  		HashSet<Flight> flightsBySegment = (HashSet<Flight>)flightMap.get(segment.getFlightName());
  		if(deptDate != null){
  			for (Flight f : flightsBySegment) {
  				if (areDatesSameWithNoTime(f.getScheduledDepartureTime(), deptDate)) {
  					f.setFlightSegment(segment);
  					flights.add(f);
  				}
  			}
  		} else {
  			for (Flight f : flightsBySegment) {
  				f.setFlightSegment(segment);
  				flights.add(f);
  			}
  		}
  		if (startedTran)
  			session.commit();
  		
  		return flights;
  		} catch (Exception e) {
  			throw new RuntimeException(e);
  		}
  	}
  
  	
  	private static boolean areDatesSameWithNoTime(Date d1, Date d2) {
  		return getDateWithNoTime(d1).equals(getDateWithNoTime(d2));
  	}
  	
  	private static Date getDateWithNoTime(Date date) {
  		Calendar c = Calendar.getInstance();
  		c.setTime(date);
  		c.set(Calendar.HOUR_OF_DAY, 0);
  		c.set(Calendar.MINUTE, 0);
  		c.set(Calendar.SECOND, 0);
  		c.set(Calendar.MILLISECOND, 0);
  		return c.getTime();
  	}
  	
  	
  	@Override
  	public void storeAirportMapping(AirportCodeMapping mapping) {
  		try{
  			//Session session = sessionManager.getObjectGridSession();
  			Session session = og.getSession();
  			ObjectMap airportCodeMappingMap = session.getMap(AIRPORT_CODE_MAPPING_MAP_NAME);
  			airportCodeMappingMap.upsert(mapping.getAirportCode(), mapping);
  		}catch (Exception e)
  		{
  			throw new RuntimeException(e);
  		}
  	}
  
  	@Override 
  	public AirportCodeMapping createAirportCodeMapping(String airportCode, String airportName){
  		AirportCodeMapping acm = new AirportCodeMappingImpl(airportCode, airportName);
  		return acm;
  	}
  	
  	@Override
  	public Flight createNewFlight(String flightSegmentId,
  			Date scheduledDepartureTime, Date scheduledArrivalTime,
  			BigDecimal firstClassBaseCost, BigDecimal economyClassBaseCost,
  			int numFirstClassSeats, int numEconomyClassSeats,
  			String airplaneTypeId) {
  		try{
  			String id = keyGenerator.generate().toString();
  			Flight flight = new FlightImpl(id, flightSegmentId,
  				scheduledDepartureTime, scheduledArrivalTime,
  				firstClassBaseCost, economyClassBaseCost,
  				numFirstClassSeats, numEconomyClassSeats,
  				airplaneTypeId);
  			//Session session = sessionManager.getObjectGridSession();
  			Session session = og.getSession();
  			ObjectMap flightMap = session.getMap(FLIGHT_MAP_NAME);
  			//flightMap.insert(flight.getPkey(), flight);
  			//return flight;
  			@SuppressWarnings("unchecked")
  			HashSet<Flight> flightsBySegment = (HashSet<Flight>)flightMap.get(flightSegmentId);
  			if (flightsBySegment == null) {
  				flightsBySegment = new HashSet<Flight>();
  			}
  			if (!flightsBySegment.contains(flight)) {
  				flightsBySegment.add(flight);
  				flightMap.upsert(flightSegmentId, flightsBySegment);
  			}
  			return flight;
  		}catch (Exception e)
  		{
  			throw new RuntimeException(e);
  		}
  	}
  
  	@Override
  	public void storeFlightSegment(FlightSegment flightSeg) {
  		try {
  			//Session session = sessionManager.getObjectGridSession();
  			Session session = og.getSession();
  			ObjectMap flightSegmentMap = session.getMap(FLIGHT_SEGMENT_MAP_NAME);
  			// TODO: Consider moving this to a ArrayList - List ??
  			@SuppressWarnings("unchecked")
  			HashSet<FlightSegment> segmentsByOrigPort = (HashSet<FlightSegment>)flightSegmentMap.get(flightSeg.getOriginPort());
  			if (segmentsByOrigPort == null) {
  				segmentsByOrigPort = new HashSet<FlightSegment>();
  			}
  			if (!segmentsByOrigPort.contains(flightSeg)) {
  				segmentsByOrigPort.add(flightSeg);
  				flightSegmentMap.upsert(flightSeg.getOriginPort(), segmentsByOrigPort);
  			}
  		
  		} catch (Exception e) {
  			throw new RuntimeException(e);
  		}
  	}
  	
  	@Override 
  	public void storeFlightSegment(String flightName, String origPort, String destPort, int miles) {
  		FlightSegment flightSeg = new FlightSegmentImpl(flightName, origPort, destPort, miles);
  		storeFlightSegment(flightSeg);
  	}
  
  
  
  }
  