/*
 * Project Scelight
 * 
 * Copyright (c) 2013 Andras Belicza <[email protected]>
 * 
 * This software is the property of Andras Belicza.
 * Copying, modifying, distributing, refactoring without the author's permission
 * is prohibited and protected by Law.
 */
package hu.scelight.sc2.map.cache;

import hu.scelight.sc2.map.MapParser;
import hu.scelight.sc2.rep.repproc.RepProcessor;
import hu.scelight.service.env.Env;
import hu.scelight.util.Utils;
import hu.sllauncher.bean.VersionBean;
import hu.sllauncher.gui.icon.LRIcon;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.xml.bind.JAXB;

/**
 * Map image cache.
 * 
 * @author Andras Belicza
 */
public class MapImageCache {
	
	/** Version of the map image cache. */
	public static final VersionBean VERSION          = new VersionBean( 1, 1 );
	
	
	/** Folder where the map images are stored. */
	private static final Path       CACHE_FOLDER     = Env.PATH_WORKSPACE.resolve( "map-img-cache" );
	
	/** Path of the cache config bean file. */
	private static final Path       PATH_CONFIG_BEAN = CACHE_FOLDER.resolve( "config.xml" );
	
	/** Cache config bean. */
	private static ConfigBean       config;
	static {
		// Load config on "startup"
		ConfigBean config = getConfig();
		
		if ( config == null || !VERSION.equals( config.getVersion() ) ) {
			// Clear map image cache
			Utils.deletePath( CACHE_FOLDER );
			
			// Recreate cache folder
			if ( !Files.exists( CACHE_FOLDER ) )
				try {
					Files.createDirectories( CACHE_FOLDER );
				} catch ( final IOException ie ) {
					Env.LOGGER.error( "Failed to create map image cache folder: " + CACHE_FOLDER, ie );
				}
			
			// Re-save current config
			config = new ConfigBean();
			config.setVersion( VERSION );
			setConfig( config );
		}
	}
	
	/**
	 * Returns the cache config bean.
	 * 
	 * @return the cache config bean
	 */
	private static ConfigBean getConfig() {
		if ( config == null && Files.exists( PATH_CONFIG_BEAN ) )
			try {
				config = JAXB.unmarshal( PATH_CONFIG_BEAN.toFile(), ConfigBean.class );
			} catch ( final Exception e ) {
				Env.LOGGER.error( "Failed to read map image cache config from: " + PATH_CONFIG_BEAN, e );
			}
		
		return config;
	}
	
	/**
	 * Sets the cache config bean.
	 * 
	 * @param config cache config bean to be set
	 */
	private static void setConfig( final ConfigBean config ) {
		try {
			JAXB.marshal( config, PATH_CONFIG_BEAN.toFile() );
			MapImageCache.config = config;
		} catch ( final Exception e ) {
			Env.LOGGER.error( "Failed to write map image cache config to: " + PATH_CONFIG_BEAN, e );
		}
	}
	
	
	/** In-memory map image cache, mapped from map hash name. */
	private static final Map< String, LRIcon > MAP_HASH_IMAGE_MAP = new HashMap<>();
	
	/**
	 * Returns the map image of the specified map file.
	 * 
	 * @param repProc {@link RepProcessor} whose map image to return
	 * @return the map image of the specified map file
	 */
	public static LRIcon getMapImage( final RepProcessor repProc ) {
		// Locally tested edited maps do not have cache handles, only the map name is filled in the game descriptions of the init data
		if ( repProc.getMapCacheHandle() == null )
			return null;
		
		final String hash = repProc.getMapCacheHandle().contentDigest;
		LRIcon ricon = MAP_HASH_IMAGE_MAP.get( hash );
		
		if ( ricon == null ) {
			final Path imgFile = CACHE_FOLDER.resolve( hash + ".jpg" );
			// Have to synchronize this, else if the same image is written by 2 threads, file gets corrupted.
			synchronized ( hash.intern() ) {
				if ( !Files.exists( imgFile ) ) {
					final BufferedImage mapImage = MapParser.getMapImage( repProc );
					if ( mapImage != null )
						try {
							ImageIO.write( mapImage, "jpg", imgFile.toFile() );
						} catch ( final IOException ie ) {
							Env.LOGGER.error( "Failed to write map image: " + imgFile, ie );
						}
				}
			}
			if ( Files.exists( imgFile ) )
				try {
					MAP_HASH_IMAGE_MAP.put( hash, ricon = new LRIcon( imgFile.toUri().toURL() ) );
					ricon.setStringValue( hash );
				} catch ( final MalformedURLException mue ) {
					// Never to happen
					Env.LOGGER.error( "", mue );
				}
		}
		
		return ricon;
	}
	
}